首先 Gradle 遵守约定大于配置
,会为我们提供一条约定俗成的东西,这套约定俗成的东西是大多数人都熟悉的,如果没有特殊需求,我们应该尽量的不去定制。
Gradle 中的构建脚本默认是在项目目录下的 build.gradle
。
gradle 不仅支持 groovy 脚本,现在还支持使用 Kotlin DSL 脚本。下面我会给出两个版本的代码。
按照惯例,我们会使用一个 Hello World 来进行入门。
在 Gradle 中我们会定义不同的 task 去执行。下面的代码定义了一个 helloworld 的 task 用来输出 helloworld。
task helloworld {
doLast {
print 'Hello World!!'
}
}
tasks.register("helloworld") {
doLast {
println("Hello World!")
}
}
我们可以使用 gradle -q helloworld
来执行 helloworld 任务。
gradle -q helloworld
Hello World!
使用 -q 能够压制 Gradle 的 log 输出,让我们的输出更加的清晰。
这个构建脚本定义了一个 helloworld 的 task,并且添加一个 action,当你运行 gradle helloworld
,gradle 执行 helloworld 的 task,一次执行你写的这些 action。
Gradle 脚本提供了 Groovy 和 Kotlin 的全部工作。
upper:
task upper {
doLast {
String someString = 'my_nAmE'
println "Original $someString"
println "Upper case ${someString.toUpperCase()}"
}
}
tasks.register("upper") {
doLast {
val someString = "mY_nAmE"
println("Original: $someString")
println("Upper case: ${someString.toUpperCase()}")
}
}
gradle -q upper
Original: mY_nAmE
Upper case: MY_NAME
count:
task count {
doLast {
4.times {
print "$it "
}
}
}
tasks.register("count") {
doLast {
repeat(4) {
print("$it ")
}
}
}
gradle -q count
0 1 2 3
你可以声明 task 的依赖。
task hello {
doLast {
println "Hello world!"
}
}
task intro {
dependsOn hello
doLast {
println "I'm Gradle"
}
}
tasks.register("hello") {
doLast {
println("Hello World!")
}
}
tasks.register("intro") {
dependsOn("hello")
doLast {
println("I'm Gradle")
}
}
gradle -q intro
Hello World!
I'm Gradle
我们这里定义了一个 task 依赖于另一个 task,我们可以看到会先执行依赖的 task,然后才会执行这个 task 的 action。
我们可以将依赖的 task 定义在当前的 task 之后,这并不不会有什么影响。
task taskX {
dependsOn 'taskY'
doLast {
println 'taskX'
}
}
task taskY {
doLast {
println 'taskY'
}
}
tasks.register("taskX") {
dependsOn("taskY")
doLast {
println("taskX")
}
}
tasks.register("taskY") {
doLast {
println("taskY")
}
}
gradle -q taskX
taskY
taskX
这一点对于多项目的构建很重要。
请注意,在 gradle 版本中不能使用 dependsOn taskY
,我们需要对这里的 taskY 加单引号。
4.times { counter ->
task "task$counter" {
doLast {
println "I'm task number $counter"
}
}
}
repeat(4) { counter ->
tasks.register("task$counter") {
doLast {
println("I'm task number $counter")
}
}
}
gradle -q task1
I'm task number 1
任务一旦被创建就可以通过 API 来访问。例如,在运行时可以动态添加任务之间的依赖。
4.times { counter ->
task "task$counter" {
doLast {
println "I' m task number $counter"
}
}
}
task0.dependsOn task2, task3
repeat(4) { counter ->
tasks.register("task$counter") {
doLast {
println("I' m task number $counter")
}
}
}
tasks.named("task0") {
dependsOn("task2", "task3")
}
➜ example08_kts gradle -q task0
I' m task number 2
I' m task number 3
I' m task number 0
这里我们定义了 task0 依赖于 task2 和 task3,所以在执行 task0 的时候必须先执行 task2 和 task3。
task2 → task3 → task0
task hello {
doLast {
println "Hello Earth"
}
}
hello.doFirst {
println "Hello Venus"
}
hello.configure {
doLast {
println "Hello Mars"
}
}
hello.configure {
doLast {
println "Hello Jupiter"
}
}
val hello by tasks.registering {
doLast {
println("Hello Earth")
}
}
hello {
doFirst {
println("Hello Venus")
}
}
hello {
doLast {
println("Hello Mars")
}
}
hello {
doLast {
println("Hello Jupiter")
}
}
doFirst
和 doLast
能够被添加多次。每次它们分别能够添加在 action 列表的第一个和最后一个,当执行任务,这些 action 在 action 列表中依次被执行。
对于已经存在的 task 在 groovy DSL 中会能一个能够使用属性。
task hello {
doLast {
println "Hello World!"
}
}
hello.doLast {
println "Greetings from the $hello.name task."
}
这能够提高我们代码的可阅读性,尤其是使用一些插件提供的 task 的时候,例如像 compile task。
你可以给一个任务添加自己的属性。添加一个名为 myProperty
属性,给 ext.myProperty
设置初始化值。然后,这个属性能够别读取和设置像一个预定义的 task 属性。
task myTask {
ext.myProperty = "myValue"
}
task printTaskProperties {
doLast {
println myTask.myProperty
}
}
tasks.register("myTask") {
extra["myProperty"] = "myValue"
}
tasks.register("printTaskProperties") {
doLast {
println(tasks["myTask"].extra["myProperty"])
}
}
➜ example11_kts gradle -q printTaskProperties
myValue
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!"
}
}
defaultTasks("clean", "run")
task("clean") {
doLast {
println("Default Cleaning!")
}
}
tasks.register("run") {
doLast {
println("Default Running!")
}
}
tasks.register("other") {
doLast {
println("I'm not a default task!")
}
}
➜ example12_kts gradle -q
Default Cleaning!
Default Running!
这等同于执行 gradle clean run
。在一个多项目的构建中每个子项目能够有它自己的特定的默认的任务。如果一个子项目没有特定的默认的任务,则使用父项目的默认任务(如果父项目定义了)。
gradle 具有配置阶段和执行阶段。在配置阶段之后,Gradle 知道应该执行的所有任务。Gradle 提供了一个利用这些信息的机会。一个例子是检查发布任务是否在需要执行的任务中,以此为基础,可以为某些变量分配不同的值。
task distribution {
doLast {
println "We build the zip with version=$version"
}
}
task release {
dependsOn 'distribution'
doLast {
prinltn "We release now"
}
}
gradle.taskGraph.whenReady { taskGraph ->
if (taskGraph.hasTask(":release")) {
version = '1.0'
} else {
version = '1.0-SNAPSHOT'
}
}
tasks.register("distribution") {
doLast {
println("We build the zip with version=$version")
}
}
tasks.register("release") {
dependsOn("distribution")
doLast {
println("We release now")
}
}
gradle.taskGraph.whenReady {
version = if (hasTask(":release")) "1.0" else "1.0-SHAPSHOT"
}
➜ example13_kts gradle -q release
We build the zip with version=1.0
We release now
➜ example13_kts gradle -q distribution
We build the zip with version=1.0-SHAPSHOT
在执行 release task 之前, whenReady
会影响 release task。即是 release task 不是主要的 task (通过 greadle 命令参数传递的)。
上面代码能够执行的原因是 version 的值仅仅能够被读取在执行阶段。当你在实际构建中使用类似的代码时,必须确保在配置的过程中不会立即读取这个值。否则,你的构建可能会在配置和执行之间的属性使用不同的值。
如果你的构建脚本需要使用一个外部的库,你可以添加它们脚本的路径在你的构建看脚本中。使用 buildScript()
方法,传入一个声明脚本路径的代码。
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
}
}
buildscript 方法配置了一个 ScriptHandler 实例。你可以通过 classpath 来配置一个构建脚本路径。这与你声明 Java 编译路径的方式相同。你可以使用除项目依赖以外的任意的依赖类型(implementation,api 等)。
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)
}
}
import org.apache.commons.codec.binary.Base64
buildscript {
repositories {
mavenCentral()
}
dependencies {
"classpath"(group = "commons-codec", name = "commons-codec", version = "1.2")
}
}
tasks.register("encode") {
val encodedString = Base64().encode("hello world!\n".toByteArray())
println(String(encodedString))
}
多余多项目的构建,buildscript() 方法中的 dependencies 声明可以作用于每一个子项目的构建脚本。
构建脚本的 dependencies 也许来自 Gradle 插件,Please consult Using Gradle Plugins for more information on Gradle plugins.
每一个项目都会自动拥有一个 BuildEnvironmentReportTask 类型的 buildEnvironment task,可以调用该任务获取构建脚本依赖的解析情况。