Gradle 构建脚本基础(introductory tutorial)

Projects and tasks 项目和任务

每个 Gradle 构建都由一个或多个项目组成。 一个项目代表什么取决于你在 Gradle 上做什么。 例如,一个项目可能表示一个库 JAR 或一个 web 应用程序。 它可以表示从其他项目生成的 jar 组装起来的发行版 ZIP。 一个项目并不一定代表要构建的东西。 它可能代表要做的事情,比如将应用程序部署到登台或生产环境。 不要担心,如果这看起来有点含糊现在。 Gradle 的按惯例构建支持为项目增加了一个更具体的定义。

Hello world 你好,世界

要进行尝试,请创建以下名为 build.gradle 的构建脚本。

task hello {
    doLast {
        println 'Hello world!'
    }
}

在命令行 shell 中,移动到包含目录并使用 gradle-q hello 执行构建脚本:

本用户指南中的大多数示例都使用 -q 命令行选项运行。 这会抑制 Gradle 的日志消息,因此只显示任务的输出。 这使得用户指南中的示例输出更加清晰。 如果你不想使用这个选项,你就不需要使用它。 有关影响 Gradle 输出的命令行选项的详细信息,请参阅日志记录。

> gradle -q hello
Hello world!

这是怎么回事? 这个构建脚本定义一个称为 hello 的任务,并向其添加一个操作。 当运行 Gradle hello 时,Gradle 执行 hello 任务,而 hello 任务又执行所提供的操作。 操作只是一个包含要执行的代码的块。

Build scripts are code 构建脚本就是代码

Gradle 的构建脚本为您提供了 Groovy 和 Kotlin 的全部功能:

task upper {
    doLast {
        String someString = 'mY_nAmE'
        println "Original: $someString"
        println "Upper case: ${someString.toUpperCase()}"
    }
}

task count {
    doLast {
        4.times { print "$it " }
    }
}

Task dependencies 任务依赖性

正如您可能已经猜到的,您可以声明依赖于其他任务的任务。

task hello {
    doLast {
        println 'Hello world!'
    }
}
task intro {
    dependsOn hello
    doLast {
        println "I'm Gradle"
    }
}

懒惰依赖——另一个任务还不存在

task taskX {
    dependsOn 'taskY'
    doLast {
        println 'taskX'
    }
}
task taskY {
    doLast {
        println 'taskY'
    }
}

Dynamic tasks 动态任务

Groovy 或 Kotlin 的强大功能不仅仅用于定义任务的功能。 例如,您还可以使用它动态创建任务。

4.times { counter ->
    task "task$counter" {
        doLast {
            println "I'm task number $counter"
        }
    }
}
> gradle -q task1
I'm task number 1

Manipulating existing tasks 操纵现有的任务

一旦创建了任务,就可以通过 API 访问它们。 例如,您可以使用它在运行时动态地向任务添加依赖项。 蚂蚁不允许这样的事情发生。

4.times { counter ->
    task "task$counter" {
        doLast {
            println "I'm task number $counter"
        }
    }
}
task0.dependsOn task2, task3
> gradle -q task0
I'm task number 2
I'm task number 3
I'm task number 0

Example 9. Accessing a task via API - adding behaviour 通过 API 添加行为访问任务

task hello {
    doLast {
        println 'Hello Earth'
    }
}
hello.doFirst {
    println 'Hello Venus'
}
hello.configure {
    doLast {
        println 'Hello Mars'
    }
}
hello.configure {
    doLast {
        println 'Hello Jupiter'
    }
}
> gradle -q hello
Hello Venus
Hello Earth
Hello Mars
Hello Jupiter

Dofirst 和 doast 调用可以多次执行。 它们将一个动作添加到任务的动作列表的开始或结束处。 执行任务时,按顺序执行操作列表中的操作。

Groovy DSL shortcut notations Groovy DSL 快捷符号

有一种方便的符号可用于访问现有任务。 每个任务都作为构建脚本的属性可用:

task hello {
    doLast {
        println 'Hello world!'
    }
}
hello.doLast {
    println "Greetings from the $hello.name task."
}

gradle -q hello
Hello world!
Greetings from the hello task.

Extra task properties 额外的任务属性

您可以将自己的属性添加到任务中。 若要添加名为 myProperty 的属性,请将 ext.myProperty 设置为初始值。 从那时起,可以像预定义的任务属性那样读取和设置该属性。

tasks.register("myTask") {
    extra["myProperty"] = "myValue"
}

tasks.register("printTaskProperties") {
    doLast {
        println(tasks["myTask"].extra["myProperty"])
    }
}
> gradle -q printTaskProperties
myValue

额外属性并不仅限于任务。您可以在Extra properties 中了解更多相关信息。

Using Ant Tasks 使用 Ant 任务

蚂蚁是 Gradle 的一等公民。 通过简单地依赖 Groovy,Gradle 为 Ant 任务提供了优秀的集成。 Groovy 附带了神奇的 AntBuilder。 在 Gradle 使用 Ant 任务比在 build.xml 文件中使用 Ant 任务更方便、更强大。 而且在 Kotlin 也可以使用。 从下面的示例中,您可以学习如何执行 Ant 任务以及如何访问 Ant 属性:

task loadfile {
    doLast {
        def files = file('./antLoadfileResources').listFiles().sort()
        files.each { File file ->
            if (file.isFile()) {
                ant.loadfile(srcFile: file, property: file.name)
                println " *** $file.name ***"
                println "${ant.properties[file.name]}"
            }
        }
    }
}
> gradle -q loadfile
 *** agile.manifesto.txt ***
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration  over contract negotiation
Responding to change over following a plan
 *** gradle.manifesto.txt ***
Make the impossible possible, make the possible easy and make the easy elegant.
(inspired by Moshe Feldenkrais)

Using methods 使用方法

使用方法组织构建逻辑

task checksum {
    doLast {
        fileList('./antLoadfileResources').each { File file ->
            ant.checksum(file: file, property: "cs_$file.name")
            println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
        }
    }
}

task loadfile {
    doLast {
        fileList('./antLoadfileResources').each { File file ->
            ant.loadfile(srcFile: file, property: file.name)
            println "I'm fond of $file.name"
        }
    }
}

File[] fileList(String dir) {
    file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
}
Output of 产量gradle -q loadfile
> gradle -q loadfile
I'm fond of agile.manifesto.txt
I'm fond of gradle.manifesto.txt

Default tasks 默认任务

Gradle 允许您定义一个或多个在没有指定其他任务的情况下执行的默认任务。

defaultTasks 'clean', 'run'

task clean {
    doLast {
        println 'Default Cleaning!'
    }
}

task run {
    doLast {
        println 'Default Running!'
    }
}

task other {
    doLast {
        println "I'm not a default task!"
    }
}
> gradle -q
Default Cleaning!
Default Running!

这相当于运行 gradle clean run。 在多项目构建中,每个子项目都可以有自己特定的默认任务。 如果子项目未指定默认任务,则使用父项目的默认任务(如果已定义)。

Configure by DAG 配置由 DAG 完成

正如我们后面详细描述的(请参阅构建生命周期) ,Gradle 有一个配置阶段和一个执行阶段。 在配置阶段之后,Gradle 知道应该执行的所有任务。 Gradle 为你提供了一个利用这些信息的钩子。 这样做的用例是检查发布任务是否在要执行的任务之中。 根据这一点,您可以为某些变量分配不同的值。

在下面的示例中,分发和发布任务的执行导致 version 变量的值不同。

task distribution {
    doLast {
        println "We build the zip with version=$version"
    }
}

task release {
    dependsOn 'distribution'
    doLast {
        println 'We release now'
    }
}

gradle.taskGraph.whenReady { taskGraph ->
    if (taskGraph.hasTask(":release")) {
        version = '1.0'
    } else {
        version = '1.0-SNAPSHOT'
    }
}
> gradle -q distribution
We build the zip with version=1.0-SNAPSHOT
> gradle -q release
We build the zip with version=1.0
We release now

重要的是,whenReady 在执行发布任务之前影响发布任务。 即使发布任务不是 primary 任务(即,传递给 gradle 命令的任务) ,这种方法也可以工作。

此示例之所以有效,是因为版本值只在执行时读取。 在实际的构建中使用类似的构造时,您必须确保在配置期间不要急切地读取值。 否则,在配置和执行之间,构建可能对属性使用不同的值。

External dependencies for the build script 构建脚本的外部依赖项

如果构建脚本需要使用外部库,可以将它们添加到构建脚本本身中的脚本类路径中。 您可以使用 buildscript ()方法进行此操作,传入一个声明构建脚本类路径的块。

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
    }
}

传递给 buildscript ()方法的块配置一个 scriptandler 实例。 通过向类路径配置添加依赖项,可以声明构建脚本类路径。 这与声明 Java 编译类路径的方式相同,例如。 您可以使用除项目依赖项以外的任何依赖项类型。

在声明了构建脚本类路径之后,您可以像使用类路径中的任何其他类一样使用构建脚本中的类。 下面的示例添加到前面的示例中,并使用来自构建脚本类路径的类。

具有外部依赖关系的构建脚本

import org.apache.commons.codec.binary.Base64

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
    }
}

task encode {
    doLast {
        def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
        println new String(encodedString)
    }
}
> gradle -q encode
aGVsbG8gd29ybGQK

对于多项目构建,项目的 buildscript ()方法声明的依赖关系可用于其所有子项目的构建脚本。

构建脚本依赖可能是 Gradle 插件。请参考使用 Gradle 插件获取更多关于 Gradle 插件的信息。

每个项目都自动具有 BuildEnvironmentReportTask 类型的 buildEnvironment 任务,可以调用该任务来报告构建脚本依赖关系的解析。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342