task的类型
tasks是org.gradle.api.internal.tasks.DefaultTaskContainer的一种实例,新版本一般建议通过register 来配置和创建task的:
TaskProvider<Task> register?(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的类型, 比如:
<T extends Task> TaskProvider<T> register(String name, Class<T> type)
<T extends Task> TaskProvider<T> register(String name, Class<T> type, Object... constructorArgs)
<T extends Task> TaskProvider<T> register(String name, Class<T> 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_gradle\test\origin\hello.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_gradle\test\desc\hello.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不在此列。
> 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') }
}
> Task :hello SKIPPED
Skipping task ':hello' as task onlyIf is false.
:hello (Thread[Execution worker for ':',5,main]) completed. Took 0.001 secs.
配置inputs和outputs
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"
}
}
> 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.
在不改变文件内容的情况下,再次执行:
> 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:
> 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_gradle\test\desc\test.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<String,Object> | getProperties() | Returns a map of input properties for this task. | FileCollection | getSourceFiles() | Returns the set of source files for this task. | TaskInputs | properties(Map<String,?> properties) | Registers a set of input properties for this task. | TaskInputPropertyBuilder | property(String name, Object value) | Registers an input property for this task. |
TaskOutputs 接口
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的执行顺序
dependsOn
task的依赖必须先执行,这个之前已经了解了
mustRunAfter
tasks.register('task_one') {
println "配置任务$name"
doLast {
println "执行任务$name"
}
}
tasks.register('task_two') {
mustRunAfter tasks.task_one
println "配置任务$name"
doLast {
println "执行任务$name"
}
}
配置任务task_one
配置任务task_two
> Task :task_one
执行任务task_one
> Task :task_two
执行任务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
}
配置任务task_two
配置任务task_one
> Task :task_one
执行任务task_one
> Task :task_two
执行任务task_two
shouldRunAfter
跟mustRunAfter相似,但是并不严格,两种情况下shouldRunAfter 不做保证:
- 通过shouldRunAfter 导致了循环的情况;mustRunAfter对于循环的情况会报错。
- 当并行执行时,除了 “should run after” 类型依赖之外,其他类型的依赖都满足的情况下,当前任务就会执行。可以认为并行的时候并不会考虑shouldRunAfter。
finalizedBy
指定当前任务的终结任务,可以用来指定在其后执行什么任务
tasks.register('task_one') {
finalizedBy tasks.task_two
println "配置任务$name"
doLast {
println "执行任务$name"
}
}
tasks.register('task_two') {
println "配置任务$name"
doLast {
println "执行任务$name"
}
}
配置任务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"
}
}
配置任务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"
}
}
配置任务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 exceeded
Task 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"
}
}
rule task say_bye
> Task :say_bye
执行say_bye
配置任务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最好将描述写的更好一点。
Rules
-----
自定义一个Rule
|