Gradle7.x学习篇02 - task配置

task的类型

tasks是org.gradle.api.internal.tasks.DefaultTaskContainer的一种实例,新版本一般建议通过register 来配置和创建task的:

// DefaultTaskContainer 的register的方法
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 ClassDescription
Copy将文件复制到目标目录
Delete删除文件和目录
Exec执行命令行程序
GradleBuild执行Gradle构建
JavaExec在一个子进程中执行一个Java程序
SourceTask对源文件执行一些操作
Sync同步文件或者目录
Upload将配置的组件上传到执行的仓库
WorkResultsHelps access trivial WorkResult objects.
WritePropertiesWrites 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不在此列。
    # 在控制台执行: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.
    

配置inputs和outputs

task会不会被标记为UP-TO-DATE,与inputs和outputs息息相关。一种简单地配置inputs和outputs的方式可以通过Task的属性inputs和outputs:

    /**
     * <p>Returns the inputs of this task.</p>
     *
     * @return The inputs. Never returns null.
     */
    @Internal
    TaskInputs getInputs();

    /**
     * <p>Returns the outputs of this task.</p>
     *
     * @return The outputs. Never returns null.
     */
    @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_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 TypeMethodDescription
TaskInputFilePropertyBuilderdir(Object dirPath)Registers an input directory hierarchy.
TaskInputFilePropertyBuilderfile(Object path)Registers some input file for this task.
TaskInputFilePropertyBuilderfiles(Object... paths)Registers some input files for this task.
FileCollectiongetFiles()Returns the input files of this task.
booleangetHasInputs()Returns true if this task has declared the inputs that it consumes.
booleangetHasSourceFiles()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.
FileCollectiongetSourceFiles()Returns the set of source files for this task.
TaskInputsproperties(Map<String,?> properties)Registers a set of input properties for this task.
TaskInputPropertyBuilderproperty(String name, Object value)Registers an input property for this task.

TaskOutputs 接口

Modifier and TypeMethodDescription
voidcacheIf(String cachingEnabledReason, Spec<? super Task> spec)Cache the results of the task only if the given spec is satisfied.
voidcacheIf(Spec<? super Task> spec)Cache the results of the task only if the given spec is satisfied.
TaskOutputFilePropertyBuilderdir(Object path)Registers an output directory for this task.
TaskOutputFilePropertyBuilderdirs(Object... paths)Registers some output directories for this task.
voiddoNotCacheIf(String cachingDisabledReason, Spec<? super Task> spec)Disable caching the results of the task if the given spec is satisfied.
TaskOutputFilePropertyBuilderfile(Object path)Registers some output file for this task.
TaskOutputFilePropertyBuilderfiles(Object... paths)Registers some output files for this task.
FileCollectiongetFiles()Returns the output files of this task.
booleangetHasOutput()Returns true if this task has declared any outputs.
voidupToDateWhen(Closure upToDateClosure)Adds a predicate to determine whether previous outputs of this task can be reused.
voidupToDateWhen(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"
    }
}
# 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_two

shouldRunAfter

跟mustRunAfter相似,但是并不严格,两种情况下shouldRunAfter 不做保证:

  1. 通过shouldRunAfter 导致了循环的情况;mustRunAfter对于循环的情况会报错。
  2. 当并行执行时,除了 “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"
    }
}
# 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 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"
    }
}
# 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
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值