Gradle7.x学习篇02 - task配置

Gradle7.x学习篇02 - task配置,第1张

Gradle7.x学习篇02 - task配置 task的类型

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

// DefaultTaskContainer 的register的方法
TaskProvider	register​(String name, Action 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 configurationAction)

指定的类型必须是Task一种具体实现,而且Gradle提供了一些具备特定功能的Task具体类型,比如:

Task ClassDescriptionCopy将文件复制到目标目录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_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.
    
配置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"
    }
}
# 命令行: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 TypeMethodDescriptionTaskInputFilePropertyBuilderdir(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.MapgetProperties()Returns a map of input properties for this task.FileCollectiongetSourceFiles()Returns the set of source files for this task.TaskInputsproperties(Map 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 TypeMethodDescriptionvoidcacheIf(String cachingEnabledReason, Spec spec)Cache the results of the task only if the given spec is satisfied.voidcacheIf(Spec 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 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 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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4658470.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-06
下一篇 2022-11-06

发表评论

登录后才能评论

评论列表(0条)

保存