task
定义
// 1
task('hello') {
doLast {
println "hello"
}
}
task('copy', type: Copy) {
from(file('srcDir'))
into(buildDir)
}
// 2
tasks.create('hello') {
doLast {
println "hello"
}
}
tasks.create('copy', Copy) {
from(file('srcDir'))
into(buildDir)
}
// 3 Using Groovy dynamic keywords
task(hello) {
doLast {
println "hello"
}
}
task(copy, type: Copy) {
from(file('srcDir'))
into(buildDir)
}
这里'copy'
就是task的名字,而Copy
本身是gradle
的默认task
,这里我们使用这个默认task
来定义自己的task
。通过create
方法创建task
时会将task
加入到一个task
容器。
定位task
task hello
task copy(type: Copy)
// Access tasks using Groovy dynamic properties on Project
println hello.name
println project.hello.name
println copy.destinationDir
println project.copy.destinationDir
// 还可以通过容器的方式进行访问
task hello
task copy(type: Copy)
println tasks.hello.name
println tasks.named('hello').get().name
println tasks.copy.destinationDir
println tasks.named('copy').get().destinationDir
从这里我们可以更容易理解了task
容器tasks
。
或者使用getByPath
方法
task hello
println tasks.getByPath('hello').path
println tasks.getByPath(':hello').path
task
依赖
task taskX {
doLast {
println 'taskX'
}
}
task taskY {
doLast {
println 'taskY'
}
}
taskX.dependsOn taskY
我们还可以这样定义依赖
task taskY {
doLast {
println 'taskY'
}
}
task taskX(dependsOn: taskY) {
doLast {
println 'taskX'
}
}
注意:这里如果taskY
定义在taskX
后面则在运行的时候会报错。
> gradle -q taskX
taskY
taskX
但是这种依赖却可以
task taskX {
doLast {
println 'taskX'
}
}
task lib1 {
doLast {
println 'lib1'
}
}
// Using a Groovy Closure
taskX.dependsOn {
tasks.findAll { task -> task.name.startsWith('lib') }
}
task lib2 {
doLast {
println 'lib2'
}
}
task notALib {
doLast {
println 'notALib'
}
}
task
的顺序
task taskX {
doLast {
println 'taskX'
}
}
task taskY {
doLast {
println 'taskY'
}
}
taskY.mustRunAfter taskX
E:\project\gradle-study>gradle -q taskX
taskX
E:\project\gradle-study>gradle -q taskY
taskY
E:\project\gradle-study>gradle -q taskY taskX
taskX
taskY
这里使用mustRunAfter
来规定两个任务的运行顺序,当然我们还可以使用shouldRunAfter
来规定,但是这个规定并不是强制的,只是建议。
task taskX {
doLast {
println 'taskX'
}
}
task taskY {
doLast {
println 'taskY'
}
}
task taskZ {
doLast {
println 'taskZ'
}
}
taskX.dependsOn taskY
taskY.dependsOn taskZ
taskZ.shouldRunAfter taskX
E:\project\gradle-study>gradle -q taskX
taskZ
taskY
taskX
这里涉及到一个循环依赖的问题,此时shouldRunAfter
就会失效。
给task
备注说明信息(description
)
task copy(type: Copy) {
description 'Copies the resource directory to the target directory.'
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}
跳过相关task
不执行
task hello {
doLast {
println 'hello world'
}
}
hello.onlyIf { !project.hasProperty('skipHello') }
执行
E:\project\gradle-study>gradle -q hello
hello world
E:\project\gradle-study>gradle -q hello -PskipHello
还可以通过一场控制依赖的task
task compile {
doLast {
println 'We are doing the compile.'
}
}
compile.doFirst {
if (project.hasProperty('throwExp')) { throw new StopExecutionException() }
}
task myTask {
dependsOn('compile')
doLast {
println 'I am not affected'
}
}
执行的时候如果依赖的task
中报异常,则会跳过此依赖
E:\project\gradle-study>gradle -q myTask -PthrowExp
I am not affected
E:\project\gradle-study>gradle -q myTask
We are doing the compile.
I am not affected
task
有一个默认的标签属性enabled
,默认为true
,如果设置为false
则会跳过
myTask.enabled = false
给task
设置超时时间
task hangingTask() {
doLast {
Thread.sleep(10000)
}
timeout = Duration.ofMillis(500)
}
E:\project\gradle-study>gradle -q hangingTask
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':hangingTask'.
> Timeout has been exceeded
增量编译
如果我们将Gradle
的Task
看作一个黑盒子,那么我们便可以抽象出输入和输出的概念,一个Task
对输入进行操作,然后产生输出。比如,在使用java
插件编译源代码时,输入即为Java
源文件,输出则为class
文件。如果多次执行一个Task
时的输入和输出是一样的,那么我们便可以认为这样的Task
是没有必要重复执行的。此时,反复执行相同的Task
是冗余的,并且是耗时的。
为了解决这样的问题,Gradle
引入了增量式构建的概念。在增量式构建中,我们为每个Task
定义输入(inputs
)和输入(outputs
),如果在执行一个Task
时,如果它的输入和输出与前一次执行时没有发生变化,那么Gradle
便会认为该Task
是最新的(UP-TO-DATE
),因此Gradle
将不予执行。一个Task
的inputs
和outputs
可以是一个或多个文件,可以是文件夹,还可以是Project
的某个Property
,甚至可以是某个闭包所定义的条件。
每个Task
都拥有inputs
和outputs
属性,他们的类型分别为TaskInputs
和TaskOutputs
。在下面的例子中,我们展示了这么一种场景:名为combineFileContentNonIncremental
的Task
从src
目录中读取所有的文件,然后将每个文件的内容合并到destination.txt
文件中。让我们先来看看没有定义Task
输入和输出的情况:
task combineFileContentNonIncremental {
def sources = fileTree('src')
def destination = file('destination.txt')
doLast {
destination.withPrintWriter {
writer -> sources.each {
source -> writer.println(source.text)
}
}
}
}
多次执行,那么会多次合并,但是其实我们运行一次就达到目的了。此时我们可以将sources
声明为该Task
的inputs
,而将destination
声明为outputs
,重新创建一个Task
如下:
task combineFileContent {
def sources = fileTree('src')
def destination = file('destination.txt')
inputs.dir sources
outputs.file destination
doLast {
destination.withPrintWriter {
writer -> sources.each {
source -> writer.println(source.text)
}
}
}
}
E:\project\gradle-study>gradle -q combineFileContent
E:\project\gradle-study>gradle combineFileContent
Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.1-rc-1/userguide/command_line_interface.html#sec:command_line_warning
s
BUILD SUCCESSFUL in 1s
1 actionable task: 1 up-to-date