tasks是org.gradle.api.internal.tasks.DefaultTaskContainer的一种实例,新版本一般建议通过register 来配置和创建task的:
// DefaultTaskContainer 的register的方法 TaskProviderregister(String name, Action super Task> configurationAction)
register 方法通过TaskCreatingProvider来创建task,其返回类型也是TaskCreatingProvider。TaskContainer提供了findByPath来获取创建的task的对象,可以看到默认的task是DefaultTask的一种实现:
def sayHello = tasks.register('sayHello') {
doLast {
println 'sayHello' + " hai"
}
}
println sayHello
println "Class:"+sayHello.getClass().getName()
println "Superclass:"+sayHello.getClass().getSuperclass().getName()
println "-"*30
def t = tasks.findByPath("sayHello")
println t.getClass()
println t.getClass().getSuperclass().getName()
输出结果:
> Configure project : provider(task 'sayHello', class org.gradle.api.DefaultTask) Class:org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider_Decorated Superclass:org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider ------------------------------ class org.gradle.api.DefaultTask_Decorated org.gradle.api.DefaultTask
TaskContainer 的register 有一些重载方法,可以指定task的类型, 比如:
TaskProvider register(String name, Class type) TaskProvider register(String name, Class type, Object... constructorArgs) TaskProvider register(String name, Class type, Action super T> configurationAction)
指定的类型必须是Task一种具体实现,而且Gradle提供了一些具备特定功能的Task具体类型,比如:
| Task Class | Description |
|---|---|
| Copy | 将文件复制到目标目录 |
| Delete | 删除文件和目录 |
| Exec | 执行命令行程序 |
| GradleBuild | 执行Gradle构建 |
| JavaExec | 在一个子进程中执行一个Java程序 |
| SourceTask | 对源文件执行一些操作 |
| Sync | 同步文件或者目录 |
| Upload | 将配置的组件上传到执行的仓库 |
| WorkResults | Helps access trivial WorkResult objects. |
| WriteProperties | Writes a Properties in a way that the results can be expected to be reproducible. |
例如,可以定义一个复制文件的task,需要做的主要内容是配置task,具体的动作Copy 已经实现了:
tasks.register("copyTxt", Copy){
from "origin"
into "desc"
}
task的执行结果(outcomes)
task的执行结果可以被标记以下几种:
- (no label) or EXECUTED: task执行了其动作
- UP-TO-DATE:task的outputs 和inputs没有变化,动作不会被执行
- FROM-CACHE:启用缓存,task也支持缓存,task的outputs 可以从以前的执行中找到
- SKIPPED:task没有执行其动作
- NO-SOURCE:没有sources,task不需要执行其动作(Task has inputs and outputs, but no sources. For example, source files are .java files for JavaCompile.)
还是以Copy task举例,通过-i 参数显示更多日志,执行task来观察其标记标签:
- 第一次执行 gradle -i copyTxt
> Task :copyTxt Caching disabled for task ':copyTxt' because: Build cache is disabled Task ':copyTxt' is not up-to-date because: No history is available. :copyTxt (Thread[Execution worker for ':' Thread 2,5,main]) completed. Took 0.007 secs.
没有看到标签,task执行过了 - 再次执行gradle -i copyTxt
> Task :copyTxt UP-TO-DATE Caching disabled for task ':copyTxt' because: Build cache is disabled Skipping task ':copyTxt' as it is up-to-date. :copyTxt (Thread[Execution worker for ':',5,main]) completed. Took 0.002 secs.
可以看到被标记UP-TO-DATE,task被跳过了。修改源目录下文件的文本内容,再次执行:> Task :copyTxt Caching disabled for task ':copyTxt' because: Build cache is disabled Task ':copyTxt' is not up-to-date because: Input property 'rootSpec$1' file E:learn_gradletestoriginhello.txt has changed. :copyTxt (Thread[Execution worker for ':',5,main]) completed. Took 0.005 secs.
没有标签,因为源文件内容发生了变化,所以task将会被执行;同样如果目标文件内容发生了变化,task同样会被执行。:> Task :copyTxt Caching disabled for task ':copyTxt' because: Build cache is disabled Task ':copyTxt' is not up-to-date because: Output property 'destinationDir' file E:learn_gradletestdeschello.txt has changed. :copyTxt (Thread[Execution worker for ':',5,main]) completed. Took 0.004 secs.
- 如果要启用缓存的话,可以在执行任务的时候加上参数--build-cache 或者在gradle.properties文件中指定org.gradle.caching=true, 但是并不是所有的task都支持缓存,gradle提供一些内建的支持缓存的任务,比如JavaCompile、Javadoc、Test,copy不在此列。
# 在控制台执行:gradle -i --build-cache copyTxt > Task :copyTxt UP-TO-DATE Caching disabled for task ':copyTxt' because: Caching has not been enabled for the task Skipping task ':copyTxt' as it is up-to-date. :copyTxt (Thread[Execution worker for ':',5,main]) completed. Took 0.002 secs.
可以看到Caching has not been enabled for the task。支持缓存的task需要可以使用注解@CacheableTask,此注解不可以被继承,默认task是不使用缓存的。 - 当不满足执行条件,task被跳过时,会被标记为SKIPPED
def hello = tasks.register('hello') { doLast { println 'hello world' } } hello.configure { onlyIf { !project.hasProperty('skipHello') } }# 命令行执行:gradle hello -PskipHello -i > Task :hello SKIPPED Skipping task ':hello' as task onlyIf is false. :hello (Thread[Execution worker for ':',5,main]) completed. Took 0.001 secs.
task会不会被标记为UP-TO-DATE,与inputs和outputs息息相关。一种简单地配置inputs和outputs的方式可以通过Task的属性inputs和outputs:
@Internal
TaskInputs getInputs();
@Internal
TaskOutputs getOutputs();
通过TaskInputs 和TaskOutputs 提供的接口,可以在配置任务的定义其输入和输出:
tasks.register('task_one') {
inputs.file("origin/hello.txt")
outputs.file("desc/test.txt")
doLast {
println "执行任务$name"
}
}
# 命令行:gradle task_one -i > Task :task_one Watching 1 directory hierarchies to track changes Caching disabled for task ':task_one' because: Build cache is disabled Task ':task_one' is not up-to-date because: No history is available. 执行任务task_one :task_one (Thread[Execution worker for ':' Thread 2,5,main]) completed. Took 0.007 secs.
在不改变文件内容的情况下,再次执行:
# 命令行:gradle task_one -i > Task :task_one UP-TO-DATE Caching disabled for task ':task_one' because: Build cache is disabled Skipping task ':task_one' as it is up-to-date. :task_one (Thread[Execution worker for ':' Thread 2,5,main]) completed. Took 0.002 secs.
如果修改了outputs包含的文件,那么会再次执行task:
# 命令行:gradle task_one -i > Task :task_one Watching 1 directory hierarchies to track changes Caching disabled for task ':task_one' because: Build cache is disabled Task ':task_one' is not up-to-date because: Output property '$1' file E:learn_gradletestdesctest.txt has changed. 执行任务task_one :task_one (Thread[Execution worker for ':' Thread 2,5,main]) completed. Took 0.002 secs.TaskInputs 接口
| Modifier and Type | Method | Description |
|---|---|---|
| TaskInputFilePropertyBuilder | dir(Object dirPath) | Registers an input directory hierarchy. |
| TaskInputFilePropertyBuilder | file(Object path) | Registers some input file for this task. |
| TaskInputFilePropertyBuilder | files(Object... paths) | Registers some input files for this task. |
| FileCollection | getFiles() | Returns the input files of this task. |
| boolean | getHasInputs() | Returns true if this task has declared the inputs that it consumes. |
| boolean | getHasSourceFiles() | Returns true if this task has declared that it accepts source files. |
| Map | getProperties() | Returns a map of input properties for this task. |
| FileCollection | getSourceFiles() | Returns the set of source files for this task. |
| TaskInputs | properties(Map | Registers a set of input properties for this task. |
| TaskInputPropertyBuilder | property(String name, Object value) | Registers an input property for this task. |
| Modifier and Type | Method | Description |
|---|---|---|
| void | cacheIf(String cachingEnabledReason, Spec super Task> spec) | Cache the results of the task only if the given spec is satisfied. |
| void | cacheIf(Spec super Task> spec) | Cache the results of the task only if the given spec is satisfied. |
| TaskOutputFilePropertyBuilder | dir(Object path) | Registers an output directory for this task. |
| TaskOutputFilePropertyBuilder | dirs(Object... paths) | Registers some output directories for this task. |
| void | donotCacheIf(String cachingDisabledReason, Spec super Task> spec) | Disable caching the results of the task if the given spec is satisfied. |
| TaskOutputFilePropertyBuilder | file(Object path) | Registers some output file for this task. |
| TaskOutputFilePropertyBuilder | files(Object... paths) | Registers some output files for this task. |
| FileCollection | getFiles() | Returns the output files of this task. |
| boolean | getHasOutput() | Returns true if this task has declared any outputs. |
| void | upToDateWhen(Closure upToDateClosure) | Adds a predicate to determine whether previous outputs of this task can be reused. |
| void | upToDateWhen(Spec super Task> upToDateSpec) | Adds a predicate to determine whether previous outputs of this task can be reused. |
task的依赖必须先执行,这个之前已经了解了
mustRunAftertasks.register('task_one') {
println "配置任务$name"
doLast {
println "执行任务$name"
}
}
tasks.register('task_two') {
mustRunAfter tasks.task_one
println "配置任务$name"
doLast {
println "执行任务$name"
}
}
# gradle task_two task_one 配置任务task_one 配置任务task_two > Task :task_one 执行任务task_one > Task :task_two 执行任务task_two
# gradle task_two 配置任务task_one 配置任务task_two > Task :task_two 执行任务task_two
mustRunAfter 跟dependsOn 很大的不同就是,task不是必须要执行mustRunAfter指定的任务,而是当指定顺序的task都存在的时候,需要按照先后顺序来执行;dependsOn 则是无论有没有在命令行指明要执行依赖的task,任务的依赖都会先执行;
另外一点需要注意的,task的配置阶段执行顺序并不是当前的task一定在mustRunAfter之后。例如通过以下方式定义顺序,task_two将会先配置,所以尽量不要依赖配置阶段的执行顺序:
def task_one = tasks.register('task_one') {
println "配置任务$name"
doLast {
println "执行任务$name"
}
}
def task_two = tasks.register('task_two') {
println "配置任务$name"
doLast {
println "执行任务$name"
}
}
task_two.configure {
mustRunAfter task_one
}
# gradle task_two task_one 配置任务task_two 配置任务task_one > Task :task_one 执行任务task_one > Task :task_two 执行任务task_twoshouldRunAfter
跟mustRunAfter相似,但是并不严格,两种情况下shouldRunAfter 不做保证:
- 通过shouldRunAfter 导致了循环的情况;mustRunAfter对于循环的情况会报错。
- 当并行执行时,除了 “should run after” 类型依赖之外,其他类型的依赖都满足的情况下,当前任务就会执行。可以认为并行的时候并不会考虑shouldRunAfter。
指定当前任务的终结任务,可以用来指定在其后执行什么任务
tasks.register('task_one') {
finalizedBy tasks.task_two
println "配置任务$name"
doLast {
println "执行任务$name"
}
}
tasks.register('task_two') {
println "配置任务$name"
doLast {
println "执行任务$name"
}
}
# gradle task_one 配置任务task_two 配置任务task_one > Task :task_one 执行任务task_one > Task :task_two 执行任务task_two
finalizedBy 更重要的作用体现在出现异常的情况, 即便出现异常,其Finalizer 也会执行:
tasks.register('task_one') {
finalizedBy tasks.task_two
println "配置任务$name"
doLast {
println "执行任务$name"
throw new RuntimeException()
}
}
tasks.register('task_two') {
println "配置任务$name"
doLast {
println "执行任务$name"
}
}
# gradle task_one 配置任务task_two 配置任务task_one > Task :task_one FAILED 执行任务task_one > Task :task_two 执行任务task_two FAILURE: Build failed with an exception.超时配置
超过配置的时间,任务失败并终止
tasks.register('task_one') {
timeout = Duration.ofSeconds(3)
println "配置任务$name"
doLast {
println "开始执行任务$name"
Thread.sleep(5000)
println "结束执行任务$name"
}
}
# gradle task_one 配置任务task_one > Task :task_one 开始执行任务task_one Requesting stop of task ':task_one' as it has exceeded its configured timeout of 3s. > Task :task_one FAILED FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':task_one'. > Timeout has been exceededTask rules
可以通过定义Rule 在运行的时候来定义、配置并执行任务:
tasks.addRule("自定义一个Rule") { String taskName ->
if (taskName.startsWith("say_")) {
task(taskName) {
println "rule task $name"
doLast {
println "执行$name"
}
}
}
}
tasks.register("task_one") {
dependsOn("say_hi","say_hello")
println "配置任务$name"
doLast {
println "执行任务$name"
}
}
# gradle say_bye rule task say_bye > Task :say_bye 执行say_bye
# gradle task_one 配置任务task_one rule task say_hi rule task say_hello > Task :say_hello 执行say_hello > Task :say_hi 执行say_hi > Task :task_one 执行任务task_one
通过Rule来定义的task最开始不会出现tasks任务列表中,通过gradle tasks --all 查不到, 只能看到rule的描述,所以如果定义Rule最好将描述写的更好一点。
# gradle tasks --all 结果的一部分 Rules ----- 自定义一个Rule



