美文网首页
Gradle通关系列(四)-深入Task

Gradle通关系列(四)-深入Task

作者: kevin丶xie | 来源:发表于2021-02-20 22:37 被阅读0次

    深入Task

    task作为Gradle构建的最小原子工作,可以通过task之间的相互依赖灵活的定义一个项目的构建。

    task包含一下属性

    • task自身的相关属性,包含所有的getter、setter方法
    • extentsions属性
    • conventions属性
    • extra属性

    Task配置

    定义Task

    可以为Project定义一系列的任务项,每个任务都会有自己一系列的Action,可以通过doFirst、doLast添加action

    //创建默认task
    tasks.register("hello") {
        //配置阶段的代码
        group = "custiom"
        doLast {
            //执行阶段的代码
            println("hello")
        }
    }
    //创建基于Task模板的task
    tasks.register<Copy>("copy") {
        //配置拷贝源
        from(file("srcDir"))
        //配置拷贝目的
        into(buildDir)
    }
    

    获取Task

    可以获取已经定义的task,获取task的相关配置或者对其进行再次配置

    //通过名称获取task
    println(tasks.named("hello").get().name)
    //通过名称获取指定类型的task
    println(tasks.named<Copy>("copy").get().destinationDir)
    //根据类型获取task
    tasks.withType<Copy>().configureEach {
        group = "customCopy"
    }
    

    我们可以在TaskContainer源码中查看更多关于获取task的api

    Task依赖及排序

    一个task可能有依赖另外一个task,也可能需要被放在某个task之后执行,Gradle确保在执行任务时遵守所有的任务依赖关系和排序规则,使用dependsOn来操作task的依赖,使用mustRunAfter、shouldRunAfter来操作task的执行顺序。

    通过以下对象来指定task依赖或排序

    • task字符串路径
    • Task对象
    • TaskDenpendcy对象
    • TaskRefrence对象
    • RegularFileProperty、File、DirectoryProperty对象
    • 包含上述类型返回值的Provider对象
    • 包含上述类型的集合
    • 包含上述类型的闭包
    1. 依赖其他项目的task
    project("project-a") {
        //依赖项目b的taskY
        tasks.register("taskX") {
            //task的路径通过:分割
            dependsOn(":project-b:taskY")
            doLast {
                println("taskX")
            }
        }
    }
    
    project("project-b") {
        tasks.register("taskY") {
            doLast {
                println("taskY")
            }
        }
    }
    

    执行taskX之前会执行taskY

    1. 依赖一个闭包
    val taskX by tasks.registering {
        doLast {
            println("taskX")
        }
    }
    
    //依赖一个闭包
    taskX {
        dependsOn(provider {
            tasks.filter { task -> task.name.startsWith("lib") }
        })
    }
    
    tasks.register("lib1") {
        doLast {
            println("lib1")
        }
    }
    
    tasks.register("lib2") {
        doLast {
            println("lib2")
        }
    }
    
    tasks.register("notALib") {
        doLast {
            println("notALib")
        }
    }
    

    执行taskX之前会执行lib1、lib2

    1. 对task的执行流程进行排序
    val taskX by tasks.registering {
        doLast {
            println("taskX")
        }
    }
    val taskY by tasks.registering {
        doLast {
            println("taskY")
        }
    }
    taskY {
        mustRunAfter(taskX)
    }
    

    执行以下命令的输出情况

    > gradle -q taskY taskX
    taskX
    taskY
    
    > gradle -q taskY
    taskY
    

    可以看出来依赖与顺序的区别

    当一个task依赖其他task时,会优先执行依赖的task

    task的执行顺序,并不意味着作为参照的task将被执行,只是在需要一起执行时,按照约定的先后顺序执行

    1. 当task已经有依赖流程了,会忽略排序流程
    val taskX by tasks.registering {
        doLast {
            println("taskX")
        }
    }
    val taskY by tasks.registering {
        doLast {
            println("taskY")
        }
    }
    val taskZ by tasks.registering {
        doLast {
            println("taskZ")
        }
    }
    taskX { dependsOn(taskY) }
    taskY { dependsOn(taskZ) }
    taskZ { shouldRunAfter(taskX) }
    
    > gradle -q taskX
    taskZ
    taskY
    taskX
    

    跳过task

    1. 通过判断条件

      val hello by tasks.registering {
          doLast {
              println("hello world")
          }
      }
      
      hello {
          onlyIf { !project.hasProperty("skipHello") }
      }
      
    2. 通过异常,抛出StopExecutionException

    3. 设置不可用

      val disableMe by tasks.registering {
          doLast {
              println("This should not be printed if the task is disabled.")
          }
      }
      
      disableMe {
          enabled = false
      }
      
      
    4. 设置task超时时间

      tasks.register("hangingTask") {
          doLast {
              Thread.sleep(100000)
          }
          timeout.set(Duration.ofMillis(500))
      }
      

    给TaskContainer添加Rule

    TaskContainer继承自NamedDomainObjectCollection,它可以添加一个规则,当给定一个未知命名的domain object时,会应用改规则,你可以进行忽略,或者自行创建该命名的domain object

    tasks.addRule("Pattern: ping<ID>") {
        val taskName = this
        if (startsWith("ping")) {
            task(taskName) {
                doLast {
                    println("Pinging: " + (taskName.replace("ping", "")))
                }
            }
        }
    }
    

    自定义Task模版

    定义简单的task Class

    open class GreetingTask : DefaultTask() {
        var greeting = "hello from GreetingTask"
            //TaskAction中写task的具体执行逻辑,此方法是在执行阶段执行
        @TaskAction
        fun greet() {
            println(greeting)
        }
    }
    
    tasks.register<GreetingTask>("hello")
    

    通过setter方法配置task

    tasks.register<GreetingTask>("greeting") {
        //配置greeting参数
        greeting = "greetings from GreetingTask"
    }
    

    通过构造方法配置task

    open class GreetingTask() : DefaultTask() {
        var greeting = "hello from GreetingTask"
    
        @javax.inject.Inject
        constructor(greeting: String) : this() {
            this.greeting = greeting
        }
    
        @TaskAction
        fun greet() {
            println(greeting)
        }
    }
    //直接传递构造函数的参数
    tasks.register<GreetingTask>("greeting", "hello gradle")
    

    通过命令行选项配置task

    open class GreetingTask() : DefaultTask() {
        @Option(option = "m", description = "配置greeting文本")
        var greeting = "hello from GreetingTask"
    
        @TaskAction
        fun greet() {
            println(greeting)
        }
    }
    
    tasks.register<GreetingTask>("greeting")
    //执行命令gradlew greeting -m hellogradle
    

    增量构建

    为了提升Gradle的构建效率,避免进行重复的工作,Gradle引入了增量构建的概念。

    在大多数情况下,task一般都会包含输入和输出,以Gradle通关系列(三)中的ZipResTask为例,资源文件就是输入,打包的zip文件就是输出。 如果多次执行一个Task时的输入和输出是一样的,那么我们便可以认为这样的Task是没有必要重复执行的 。 每个Task都拥有inputs和outputs属性,他们的类型分别为TaskInputs和TaskOutputs。 在增量式构建中,我们可以为每个Task定义输入(inputs)和输入(outputs),如果在执行一个Task时,如果它的输入和输出与前一次执行时没有发生变化,那么Gradle便会认为该Task是最新的(UP-TO-DATE),因此Gradle将不予执行。一个Task的inputs和outputs可以是一个或多个文件,可以是文件夹,还可以是Project的某个Property,甚至可以是某个闭包所定义的条件 。

    改造ZipResTask为增量构建

    //custom_build.gradle.kts
    import org.gradle.kotlin.dsl.support.zipTo
    
    open class ZipResExtensions {
        var resPath: String = ""
        var outputPath: String = ""
    }
    
    extensions.create<ZipResExtensions>("zipRes")
    
    abstract class ZipResTask : DefaultTask() {
        @get:InputDirectory
        abstract val resDir: Property<File>
    
        @get:OutputFile
        abstract val outputFile: Property<File>
    
        @TaskAction
        fun zipRes() {
            zipTo(outputFile.get(), resDir.get())
        }
    }
    
    tasks.register("zipRes", ZipResTask::class)
    
    afterEvaluate {
        tasks.named("zipRes", ZipResTask::class) {
            val zipResExtensions = project.extensions.getByName<ZipResExtensions>("zipRes")
            resDir.set(file(zipResExtensions.resPath))
            outputFile.set(file(zipResExtensions.outputPath))
        }
    }
    

    执行zipRes的输出情况

    第一次执行

    16:39:11: Executing task 'zipRes'...
    
    > Task :zipRes
    
    BUILD SUCCESSFUL in 88ms
    1 actionable task: 1 executed
    16:39:11: Task execution finished 'zipRes'.
    

    第二次执行

    16:39:57: Executing task 'zipRes'...
    
    > Task :zipRes UP-TO-DATE
    
    BUILD SUCCESSFUL in 83ms
    1 actionable task: 1 up-to-date
    16:39:57: Task execution finished 'zipRes'.
    

    没有改动就是直接跳过该task的执行,后面标记UP-TO-DATE

    总结

    task是Gradle构建的最小原子工作,我们需要会创建task、并配置它、调整各种task之间的依赖来完成我们的构建

    相关文章

      网友评论

          本文标题:Gradle通关系列(四)-深入Task

          本文链接:https://www.haomeiwen.com/subject/qnfffltx.html