第一章:Gradle 任务简介

1.1 概述

  • Gradle 项目中会包含若干个任务(task)进行描述,同时每个任务定义的语句形式都是利用 Groovy 语法完成的(现在也支持 kotlin 了)。
  • 在一个 Gradle 项目中利用任务的概念,可以将一个完整的处理任务拆分成若干个细小的子任务。

1.2 任务列表

  • 当创建完成一个 Gradle 项目之后,默认情况下都会存在若干个任务列表,可以直接通过 Gradle 命令查看:
  1. gradle -q tasks

1.gif

  • 其实,在 IDEA 中展示的就是这些任务列表:

2.png

1.3 项目属性

  • Gradle 项目是基于 Groovy 语言编写的,所以在使用 Groovy 操作 Gradle 项目的时候需要获取一些项目的信息,可以通过如下的命令获取所有的项目属性:
  1. gradle -q properties

3.gif

1.4 任务定义

  • 清楚了 项目任务 的基本关系之后,下面就需要动手来直接定义一个任务,所有的任务定义都是使用 Groovy 语法来完成的,所有的任务都可以直接在 build.gradle 文件里面进行定义处理。
  1. plugins {
  2. id 'java' // 配置的是一个 Java 插件(Java 项目)
  3. }
  4. group 'com.github.fairy.era' // 组织名称
  5. version '1.0' // 项目版本
  6. sourceCompatibility = 1.8 // 源代码版本
  7. repositories { // 仓库配置
  8. mavenCentral()
  9. }
  10. dependencies { // 依赖管理
  11. /* junit 5 */
  12. testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.8.2'
  13. }
  14. tasks.withType(JavaCompile) { // 针对程序编译的任务进行配置
  15. options.encoding = "UTF-8"
  16. }
  17. test { // 进行测试任务的配置
  18. useJUnitPlatform() // 使用 Junit 平台
  19. }
  20. task hello { // 定义一个新的任务,任务的名称是 hello
  21. println('你好,Gradle')
  22. }
  • 当任务定义完成之后,会自动的在 Gradle 命令执行列表中出现有任务的名称信息。

4.png

  • 当然,也可以通过执行命令来执行该任务:
  1. gradle hello

第二章:Gradle 任务定义语法

2.1 概述

  • 通过之前的具体操作,已经成功的实现了一个 Gradle 自定义的任务项,但是需要注意的是,在 Gradle 里面任务的定义语法形式是非常丰富的,所以本次来研究一下各种任务的定义语法形式。

2.2 原型创建

  • 在 Gradle 里面提供了一个 task() 处理函数,这个函数可以直接创建一个任务,同时需要设置好任务的名称。
  • 对于任务的创建主要通过 org.gradle.api.Project 接口里面提供的任务创建方法:
  1. Task task(String var1) throws InvalidUserDataException;
  • 在使用 task() 方法进行任务创建的时候,需要设置一个字符串,修改 build.gradle 文件:
  1. // 每一个创建出来的任务都是 Task(org.gradle.api.Task) 的对象实例
  2. def task = task(helloTask) // 创建一个任务:helloTask
  • 每当使用 task() 方法创建的任务都自动返回一个 Task 接口的实例,Task 接口的定义如下:
  1. package org.gradle.api;
  2. public interface Task extends Comparable<Task>, ExtensionAware {
  3. String TASK_NAME = "name";
  4. String TASK_DESCRIPTION = "description";
  5. String TASK_GROUP = "group";
  6. String TASK_TYPE = "type";
  7. String TASK_DEPENDS_ON = "dependsOn";
  8. String TASK_OVERWRITE = "overwrite";
  9. String TASK_ACTION = "action";
  10. String TASK_CONSTRUCTOR_ARGS = "constructorArgs";
  11. // 其他略
  12. }
  • 当获取到了一个 Task 接口的实例之后,就需要进行此任务的具体执行行为的配置(任务体),修改 build.gradle 文件:
  1. // 每一个创建出来的任务都是 Task(org.gradle.api.Task) 的对象实例
  2. def task = task(helloTask) // 创建一个任务:helloTask
  3. task.doLast { // 在任务之后执行此操作
  4. println '你好啊,Groovy'
  5. }

2.3 任务属性

  • 在创建 task 的时候,有可以使用 Project 接口中提供的其他的创建方法,同时设置好一些相应的任务的属性内容。
  • 任务的创建方法:
  1. Task task(Map<String, ?> var1, String var2) throws InvalidUserDataException;
  • 设置任务的属性,修改 build.gradle 文件:
  1. // 每一个创建出来的任务都是 Task(org.gradle.api.Task) 的对象实例
  2. def task = task(description: '自定义 Gradle 任务的描述', helloTask) // 创建一个任务:helloTask
  3. task.doLast { // 在任务之后执行此操作
  4. println '你好啊,Groovy'
  5. }
  • 程序执行命令:
  1. gradle helloTask
  • 程序执行结果:
  1. 14:11:10: 正在执行 'helloTask'
  2. > Task :helloTask
  3. 你好啊,Groovy
  4. BUILD SUCCESSFUL in 50ms
  5. 1 actionable task: 1 executed
  6. 14:11:10: 执行完成 'helloTask'
  • 程序执行命令:
  1. gradle help --task helloTask
  • 程序执行结果:
  1. 14:11:20: 正在执行 'help --task helloTask'
  2. > Task :help
  3. Detailed task information for helloTask
  4. Path
  5. :helloTask
  6. Type
  7. Task (org.gradle.api.Task)
  8. Description
  9. 自定义 Gradle 任务的描述
  10. Group
  11. -
  12. BUILD SUCCESSFUL in 44ms
  13. 1 actionable task: 1 executed
  14. 14:11:20: 执行完成 'help --task helloTask'
  • 这些描述信息可以直接在进行任务帮助的时候进行详细的显示操作。
  • 设置更多的属性内容,修改 build.gradle 文件:
  1. // 每一个创建出来的任务都是 Task(org.gradle.api.Task) 的对象实例
  2. def task = task([description: '自定义 Gradle 任务的描述',group: 'com.github.fairy.era'], helloTask) // 创建一个任务:helloTask
  3. task.doLast { // 在任务之后执行此操作
  4. println '你好啊,Groovy'
  5. }
  • 程序执行命令:
  1. gradle help --task helloTask
  • 程序执行结果:
  1. 14:14:28: 正在执行 'help --task helloTask'
  2. > Task :help
  3. Detailed task information for helloTask
  4. Path
  5. :helloTask
  6. Type
  7. Task (org.gradle.api.Task)
  8. Description
  9. 自定义 Gradle 任务的描述
  10. Group
  11. com.github.fairy.era
  12. BUILD SUCCESSFUL in 56ms
  13. 1 actionable task: 1 executed
  14. 14:14:28: 执行完成 'help --task helloTask'

2.4 闭包机制

  • 在 Groovy 这种提供闭包的处理操作机制,也可以用这样的语法形式来实现 Task 的创建,修改 build.gradle 文件:
  1. task helloTask { // 进行任务的创建
  2. description '自定义 Gradle 任务的描述'
  3. group 'com.github.fairy.era'
  4. doLast {
  5. println '你好啊,Groovy'
  6. }
  7. }
  • 需要注意的是,如果修改了 group 属性,那么任务项将不在 other 任务列表中显示了:

5.png

  • 程序执行命令:
  1. gradle help --task helloTask
  • 程序执行结果:
  1. 14:27:39: 正在执行 'help --task helloTask'
  2. > Task :help
  3. Detailed task information for helloTask
  4. Path
  5. :helloTask
  6. Type
  7. Task (org.gradle.api.Task)
  8. Description
  9. 自定义 Gradle 任务的描述
  10. Group
  11. com.github.fairy.era
  12. BUILD SUCCESSFUL in 39ms
  13. 1 actionable task: 1 executed
  14. 14:27:39: 执行完成 'help --task helloTask'
  • 以上这种任务创建的形式是比较正统的一种形式,因为这样操作的语法相对而言简单一些,但是需要注意的是,同样的定义形式在 Gradle 中也可以采用如下的两种方式代替,修改 build.gradle 文件:
  1. task (helloTask) { // 进行任务的创建
  2. description '自定义 Gradle 任务的描述'
  3. group 'com.github.fairy.era'
  4. doLast {
  5. println '你好啊,Groovy'
  6. }
  7. }
  1. task (helloTask, { // 进行任务的创建
  2. description '自定义 Gradle 任务的描述'
  3. group 'com.github.fairy.era'
  4. doLast {
  5. println '你好啊,Groovy'
  6. }
  7. })

2.5 TaskContainer

  • 在 Gradle 中提供有一个 TaskContainer(任务容器) 的概念实现任务的创建操作,首先来观察当前这种任务创建的方法定义:
  1. // 此方法在 org.gradle.api.tasks.TaskContainer 中定义
  2. Task create(Map<String, ?> var1, Closure var2) throws InvalidUserDataException;
  • 创建自定义任务,修改 build.gradle 文件:
  1. tasks.create('helloTask') {
  2. description '自定义 Gradle 任务的描述'
  3. group 'com.github.fairy.era'
  4. doLast {
  5. println '你好啊,Groovy'
  6. }
  7. }
  • 程序执行命令:
  1. gradle help --task helloTask
  • 程序执行结果:
  1. 14:43:31: 正在执行 'help --task helloTask'
  2. > Task :help
  3. Detailed task information for helloTask
  4. Path
  5. :helloTask
  6. Type
  7. Task (org.gradle.api.Task)
  8. Description
  9. 自定义 Gradle 任务的描述
  10. Group
  11. com.github.fairy.era
  12. BUILD SUCCESSFUL in 58ms
  13. 1 actionable task: 1 executed
  14. 14:43:32: 执行完成 'help --task helloTask'

第三章:Gradle 任务属性

3.1 概述

  • 在每一个任务之中都会存在若干个属性信息,如:描述(description)、任务的分组(group),这些都属于任务属性,但是除了这些任务属性之外,还有一些关于任务继承相关的属性存在。

3.2 属性设置

  • 所有的任务属性除了在任务定义的时候进行设置之外,还可以直接通过任务对象进行设置,修改 build.gradle 文件:
  1. def ta = task helloTask { // 进行任务的创建
  2. doFirst {
  3. println '【doFirst】你好,Groovy'
  4. }
  5. doLast {
  6. println '【doLast】 你好,Groovy'
  7. }
  8. }
  9. // 属性设置
  10. ta.group = 'com.github.fairy.era'

6.png

  • 在进行任务分组的时候,为了方便起见也可以将其归纳到内置的 Gradle 任务分组之中,此时可以直接使用 BasePlugin 类中提供的一系列常量来进行配置。
  1. public class BasePlugin implements Plugin<Project> {
  2. public static final String CLEAN_TASK_NAME = "clean";
  3. public static final String ASSEMBLE_TASK_NAME = "assemble";
  4. public static final String BUILD_GROUP = "build";
  5. public static final String UPLOAD_ARCHIVES_TASK_NAME = "uploadArchives";
  6. public static final String UPLOAD_GROUP = "upload";
  7. // 其余略
  8. }
  • 将任务分组归纳到内置的 Gradle 任务分组之中,修改 build.gradle 文件(以下写法,任选一种即可):
  1. def ta = task helloTask { // 进行任务的创建
  2. doFirst {
  3. println '【doFirst】你好,Groovy'
  4. }
  5. doLast {
  6. println '【doLast】 你好,Groovy'
  7. }
  8. }
  9. // 属性设置
  10. ta.group = BasePlugin.BUILD_GROUP // 设置任务所在的分组
  11. ta.description = '自定义 Gradle 的任务描述'
  1. def ta = task helloTask { // 进行任务的创建
  2. doFirst {
  3. println '【doFirst】你好,Groovy'
  4. }
  5. doLast {
  6. println '【doLast】 你好,Groovy'
  7. }
  8. }
  9. // 属性设置
  10. helloTask { // 定义任务属性内容
  11. group = BasePlugin.BUILD_GROUP // 设置任务所在的分组
  12. description = '自定义 Gradle 的任务描述' // 定义任务描述
  13. }

7.png

3.3 控制属性

  • 既然任务已经可以直接创建了,并且所创建的任务也都可以正常执行,那么就可以通过任务属性的配置来设置某一个任务是否可以启动,修改 build.gradle 文件:
  1. def ta = task helloTask { // 进行任务的创建
  2. doFirst {
  3. println '【doFirst】你好,Groovy'
  4. }
  5. doLast {
  6. println '【doLast】 你好,Groovy'
  7. }
  8. }
  9. // 属性设置
  10. helloTask { // 定义任务属性内容
  11. group = BasePlugin.BUILD_GROUP // 设置任务所在的分组
  12. description = '自定义 Gradle 的任务描述' // 定义任务描述
  13. enabled = false // 任务禁用
  14. }
  • 程序执行命令:
  1. gradle helloTask
  • 程序执行结果:
  1. 20:59:38: 正在执行 'helloTask'
  2. > Task :helloTask SKIPPED
  3. BUILD SUCCESSFUL in 40ms
  4. 20:59:38: 执行完成 'helloTask'

3.4 自定义任务类型(任务继承)

  • 在 Gradle 里面,如果自定义的任务类型不能满足实际的开发需求时,最简单的方法就是编写自定义的任务类型,其只需要继承 DefaultTask 父类即可,修改 build.gradle 文件:
  1. class CustomTask extends DefaultTask {
  2. @TaskAction // 必须追加任务执行的注解
  3. def doSelf() { // 定义任务的主体
  4. println '【CustomTask - doSelf】 - 执行任务的主体'
  5. }
  6. @TaskAction
  7. def doLast() {
  8. println '【CustomTask - doLast】 - 任务主体执行完毕后的操作'
  9. }
  10. }
  11. task helloTask(type: CustomTask) { // 进行任务的创建
  12. group = BasePlugin.BUILD_GROUP // 设置任务所在的分组
  13. description = '自定义 Gradle 的任务描述' // 定义任务描述
  14. doFirst {
  15. println '【doFirst】你好,Groovy'
  16. }
  17. doLast {
  18. println '【doLast】 你好,Groovy'
  19. }
  20. }
  • 程序执行命令:
  1. gradle helloTask
  • 程序执行结果:
  1. 8:09:04: 正在执行 'helloTask'
  2. > Task :helloTask
  3. doFirst】你好,Groovy
  4. CustomTask - doLast - 任务主体执行完毕后的操作
  5. CustomTask - doSelf - 执行任务的主体
  6. doLast 你好,Groovy
  7. BUILD SUCCESSFUL in 53ms
  8. 1 actionable task: 1 executed
  9. 8:09:05: 执行完成 'helloTask'

3.5 onlyif

  • 在任务执行的时候还有一个 onlyif 属性,这个属性的含义可以接收一个闭包的处理,根据处理的结果来判断此任务是否要执行(回调机制,如果返回 true ,就表示需要执行;如果返回 false ,就表示不需要执行),修改 build.gradle 文件:
  1. final String BUILD_TASK_NAME = 'hello'
  2. task helloTask {
  3. group BasePlugin.BUILD_GROUP
  4. description '自定义 Gradle 的任务描述'
  5. onlyIf { // 此属性类似于 enabled 的形式
  6. def executeResult = false // 执行的最终结果
  7. // 对于此任务是否执行,取决于外部设置的一个属性内容,即在执行命令的时候所配置的属性内容
  8. if (project.hasProperty('flag')) { // 判断是否存在 flag 配置属性
  9. def flagProperties = project.property('flag') // 获取属性内容
  10. if (BUILD_TASK_NAME == flagProperties) { // 和内置属性相同
  11. executeResult = true // 要执行此任务
  12. }
  13. }
  14. return executeResult // 根据此结果来判断是否执行任务
  15. }
  16. doLast {
  17. println '你好啊,Groovy'
  18. }
  19. }
  • 程序执行命令:
  1. gradle helloTask
  • 程序执行结果:
  1. 8:44:17: 正在执行 'helloTask'
  2. > Task :helloTask SKIPPED
  3. BUILD SUCCESSFUL in 48ms
  4. 8:44:17: 执行完成 'helloTask'
  • 程序执行命令:
  1. gradle -Pflag=hello helloTask
  • 程序执行结果:
  1. 8:46:43: 正在执行 'helloTask -Pflag=hello'
  2. > Task :helloTask
  3. 你好啊,Groovy
  4. BUILD SUCCESSFUL in 50ms
  5. 1 actionable task: 1 executed
  6. 8:46:43: 执行完成 'helloTask -Pflag=hello'

第四章:Gradle 多任务定义

4.1 概述

  • 通过之前的一系列分析,我们已经清楚的知道单个任务的定义语法,但是很多情况下有可能需要在一个 Gradle 项目里面定义若干个任务,如:gradle clean build,那么现在就尝试如何在 Gradle 里面实现多个任务的定义和执行。

4.2 多任务

  • 在 build.gradle 文件中定义三个任务信息,修改 build.gradle 文件:
  1. task helloTask1 { // 第一个执行任务
  2. println '【第一个执行任务】'
  3. }
  4. task helloTask2 { // 第二个执行任务
  5. println '【第二个执行任务】'
  6. }
  7. task helloTask3 { // 第三个执行任务
  8. println '【第三个执行任务】'
  9. }
  • 程序执行命令:
  1. gradle helloTask2 helloTask1 helloTask3
  • 程序执行结果:
  1. 8:58:09: 正在执行 'helloTask2 helloTask1 helloTask3'
  2. > Configure project :
  3. 【第一个执行任务】
  4. 【第二个执行任务】
  5. 【第三个执行任务】
  6. > Task :helloTask2 UP-TO-DATE
  7. > Task :helloTask1 UP-TO-DATE
  8. > Task :helloTask3 UP-TO-DATE
  9. BUILD SUCCESSFUL in 65ms
  10. 8:58:09: 执行完成 'helloTask2 helloTask1 helloTask3'
  • 通过此时的多任务的执行结果,可以发现,在 build.gradle 里面定义的多个任务信息,实际上都会根据其定义的顺序来执行,而不是由 gradle 命令配置的顺序来执行。

4.3 任务依赖

  • 既然已经存在了多个任务,那么这些任务彼此之间就可能存在有依赖关系(任务 A 依赖于 任务 B ,如:进行打包之前必须先进行编译之后,才能进行打包),这样的依赖关系就可以通过 dependsOn 来进行配置,修改 build.gradle 文件:
  1. // 注意:所有依赖的任务彼此之间一定要有一个顺序,被依赖的任务一定要在之前进行定义
  2. task helloTask1 { // 第一个执行任务
  3. doLast {
  4. println '【第一个执行任务】'
  5. }
  6. }
  7. task helloTask2 { // 第二个执行任务
  8. doLast {
  9. println '【第二个执行任务】'
  10. }
  11. }
  12. task helloTask3(dependsOn: [helloTask1, helloTask2]) { // 第三个执行任务,当前的任务依赖于前面的两个任务
  13. doLast {
  14. println '【第三个执行任务】'
  15. }
  16. }
  17. task allTask(dependsOn: helloTask3) { // 总的执行任务
  18. doFirst {
  19. println 'allTask doFirst'
  20. }
  21. }
  • 程序执行命令:
  1. gradle allTask
  • 程序执行结果:
  1. 9:11:17: 正在执行 'allTask'
  2. > Task :helloTask1
  3. 【第一个执行任务】
  4. > Task :helloTask2
  5. 【第二个执行任务】
  6. > Task :helloTask3
  7. 【第三个执行任务】
  8. > Task :allTask
  9. allTask doFirst
  10. BUILD SUCCESSFUL in 51ms
  11. 4 actionable tasks: 4 executed
  12. 9:11:17: 执行完成 'allTask'
  • 可以发现,所有被依赖的任务会首先执行,而所有要执行的任务都会最后执行。

4.4 任务禁用

  • 在之前学习任务属性的时候,学习过一个 enabled 属性,这个属性的内容会决定当前的任务是否执行,那么如果说被依赖的任务没有执行,那么会不会影响到其他的任务?修改 build.gradle 文件:
  1. task helloTask1 { // 第一个执行任务
  2. enabled false // 此任务直接跳过
  3. doLast {
  4. println '【第一个执行任务】'
  5. }
  6. }
  7. task helloTask2 { // 第二个执行任务
  8. doLast {
  9. println '【第二个执行任务】'
  10. }
  11. }
  12. task helloTask3(dependsOn: [helloTask1, helloTask2]) { // 第三个执行任务,当前的任务依赖于前面的两个任务
  13. enabled false // 此任务直接跳过
  14. doLast {
  15. println '【第三个执行任务】'
  16. }
  17. }
  18. task allTask(dependsOn: helloTask3) { // 总的执行任务
  19. doFirst {
  20. println 'allTask doFirst'
  21. }
  22. }
  • 程序执行命令:
  1. gradle allTask
  • 程序执行结果:
  1. 9:16:19: 正在执行 'allTask'
  2. > Task :helloTask1 SKIPPED
  3. > Task :helloTask2
  4. 【第二个执行任务】
  5. > Task :helloTask3 SKIPPED
  6. > Task :allTask
  7. allTask doFirst
  8. BUILD SUCCESSFUL in 334ms
  9. 2 actionable tasks: 2 executed
  10. 9:16:20: 执行完成 'allTask'
  • 此时存在的依赖关系,即便父任务没有执行,那么子任务也依然正确执行。

第五章:Gradle 任务定义深入

5.1 概述

  • 现在已经清楚了 Gradle 任务定义的大部分的操作语法,实际上除了这些基础的语法之外,在任务处理的时候也存在着一些项目的属性、方法等信息。

5.2 任务属性

  • 所有的任务实际上都会放在 TaskContainer 中进行任务管理(也是在项目中保存),那么如果要想在任务中获取到一些任务的属性,也是可以直接完成的,修改 build.gradle 文件:
  1. task helloTask {
  2. doFirst {
  3. println '常规输出:你好,Gradle!!!'
  4. println '任务名称 - 1:' + helloTask.name // 任务名称获取
  5. println '任务名称 - 2:' + project.helloTask.name // 项目中的任务名称获取
  6. println '任务名称 - 3:' + tasks.helloTask.name // 任务容器获取任务名称
  7. println '任务名称 - 4:' + tasks['helloTask'].name // 任务容器获取任务名称
  8. }
  9. }
  • 程序执行命令:
  1. gradle helloTask
  • 程序执行结果:
  1. 9:43:55: 正在执行 'helloTask'
  2. > Task :helloTask
  3. 常规输出:你好,Gradle!!!
  4. 任务名称 - 1helloTask
  5. 任务名称 - 2helloTask
  6. 任务名称 - 3helloTask
  7. 任务名称 - 4helloTask
  8. BUILD SUCCESSFUL in 48ms
  9. 1 actionable task: 1 executed
  10. 9:43:55: 执行完成 'helloTask'

5.3 任务路径

  • 在每一个任务里面都会存在有任务路径,通常而言,建议任务路径名称和任务名称相同,修改 build.gradle 文件:
  1. task helloTask {
  2. doFirst {
  3. println '常规输出:你好,Gradle!!!'
  4. println '任务路径 - 1:' + helloTask.path // 任务路径获取
  5. println '任务路径 - 2:' + project.helloTask.path // 项目中的任务路径获取
  6. println '任务路径 - 3:' + tasks.helloTask.path // 任务容器获取任务路径
  7. println '任务路径 - 4:' + tasks['helloTask'].path // 任务容器获取任务路径
  8. println '任务路径 - 5:' + tasks.getByPath('helloTask').path // 任务路径获取
  9. println '任务路径 - 6:' + tasks.getByPath(':helloTask').path // 任务路径获取
  10. }
  11. }
  • 程序执行命令:
  1. gradle :helloTask
  • 程序执行结果:
  1. 9:50:36: 正在执行 ':helloTask'
  2. > Task :helloTask
  3. 常规输出:你好,Gradle!!!
  4. 任务路径 - 1::helloTask
  5. 任务路径 - 2::helloTask
  6. 任务路径 - 3::helloTask
  7. 任务路径 - 4::helloTask
  8. 任务路径 - 5::helloTask
  9. 任务路径 - 6::helloTask
  10. BUILD SUCCESSFUL in 56ms
  11. 1 actionable task: 1 executed
  12. 9:50:36: 执行完成 ':helloTask'

总结:整个 Gradle 中的项目信息是通过 project 得到的,对应的属性是通过 property() 方法获取,而所有的任务信息都可以通过 tasks(TasksContainer) 来获取,里面保存的就是一个 Map 集合。

5.4 任务顺序

  • 对于任务来说,在整个 Gradle 里面有各种各样的处理语法,在之前也已经学习过一种最为简单的多任务执行操作,而当时定义的多任务是直接使用 task 的形式创建的,这样的创建形式在执行的时候,就会按照定义的顺序执行任务,但是如果此时采用的是下面的语法形式,则可以根据执行的顺序来进行配置,修改 build.gradle 文件:
  1. def taskA = task helloTask {
  2. doFirst {
  3. println '你好啊,Groovy 中的 helloTask'
  4. }
  5. }
  6. def taskB = task helloTask1 {
  7. doFirst {
  8. println '你好啊,Groovy 中的 helloTask1'
  9. }
  10. }
  • 程序执行命令:
  1. gradle helloTask1 helloTask
  • 程序执行结果:
  1. 10:01:23: 正在执行 'helloTask1 helloTask'
  2. > Task :helloTask1
  3. 你好啊,Groovy 中的 helloTask1
  4. > Task :helloTask
  5. 你好啊,Groovy 中的 helloTask
  6. BUILD SUCCESSFUL in 192ms
  7. 2 actionable tasks: 2 executed
  8. 10:01:23: 执行完成 'helloTask1 helloTask'
  • 但是,此时希望不管什么时候都希望 helloTask1 在 helloTask 之后执行,所以此时就可以通过配置顺序的形式来定义任务的执行顺序,修改 build.gradle 文件:
  1. def taskA = task helloTask {
  2. doFirst {
  3. println '你好啊,Groovy 中的 helloTask'
  4. }
  5. }
  6. def taskB = task helloTask1 {
  7. doFirst {
  8. println '你好啊,Groovy 中的 helloTask1'
  9. }
  10. }
  11. // B 任务用于在 A任务之后执行
  12. taskB.mustRunAfter(taskA)
  • 程序执行命令:
  1. gradle helloTask1 helloTask
  • 程序执行结果:
  1. 10:04:29: 正在执行 'helloTask1 helloTask'
  2. > Task :helloTask
  3. 你好啊,Groovy 中的 helloTask
  4. > Task :helloTask1
  5. 你好啊,Groovy 中的 helloTask1
  6. BUILD SUCCESSFUL in 96ms
  7. 2 actionable tasks: 2 executed
  8. 10:04:29: 执行完成 'helloTask1 helloTask'
  • 通过此时的执行结果可以清楚的发现:即便执行的时候任务的顺序没有设置好,但是由于在 build.gradle 文件中设置好了任务执行的顺序,那么所有的任务也会按照事前设置好的顺序依次执行。

5.5 任务错误

  • 当在配置任务的时候,使用了 mustRunAfter 属性之后,如果还同时存在有依赖的关系,那么很有可能会存在递归任务的错误之中, 修改 build.gradle 文件:
  1. def taskA = task helloTask {
  2. doFirst {
  3. println '你好啊,Groovy 中的 helloTask'
  4. }
  5. }
  6. def taskB = task helloTask1 {
  7. doFirst {
  8. println '你好啊,Groovy 中的 helloTask1'
  9. }
  10. }
  11. // 依赖环境的配置
  12. taskA.dependsOn(taskB)
  13. // B 任务用于在 A任务之后执行
  14. taskB.mustRunAfter(taskA)
  • 程序执行命令:
  1. gradle helloTask
  • 程序执行结果:
  1. Circular dependency between the following tasks:
  2. :helloTask
  3. \--- :helloTask1
  4. \--- :helloTask (*)
  5. * Try:
  6. Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
  • 按照任务依赖,可能会先执行被依赖的任务,然后在执行本身的任务,但是按照 mustRunAfter 的做法,需要配置任务的执行顺序,所以此时会出现一个程序的递归错误。

5.6 依赖顺序解决

  • 在庞大的 Gradle 项目开发过程之中,你很难保证不出现上面的任务错误(程序的递归错误),所以最佳的做法就是忽略递归执行任务的死循环,修改 build.gradle 文件:
  1. def taskA = task helloTask {
  2. doFirst {
  3. println '你好啊,Groovy 中的 helloTask'
  4. }
  5. }
  6. def taskB = task helloTask1 {
  7. doFirst {
  8. println '你好啊,Groovy 中的 helloTask1'
  9. }
  10. }
  11. // 依赖环境的配置
  12. taskA.dependsOn(taskB)
  13. // B 任务用于在 A任务之后执行,修改这边啊,将 mustRunAfter 改为 shouldRunAfter
  14. taskB.shouldRunAfter(taskA)
  • 程序执行命令:
  1. gradle helloTask
  • 程序执行结果:
  1. 10:18:40: 正在执行 'helloTask'
  2. > Task :helloTask1
  3. 你好啊,Groovy 中的 helloTask1
  4. > Task :helloTask
  5. 你好啊,Groovy 中的 helloTask
  6. BUILD SUCCESSFUL in 48ms
  7. 2 actionable tasks: 2 executed
  8. 10:18:40: 执行完成 'helloTask'
  • 此时并没有强制性的发生任务先后执行的顺序逻辑,而是采用了一种较为柔和的方式来完成任务执行顺序的配置。

5.7 任务替换

  • 按照 Java 程序,如果父类中的某个操作方法不好用,那么子类就可以考虑进行方法的扩展(通过重写来解决此问题);同样的问题也出现在 Gradle 之中,虽然 Gradle 提供了大量的内置的操作方法,但是如果我们发现某些操作方法不合适,那么也可以进行重写,修改 build.gradle 文件(以下方式,任选其一即可):
  1. task build(overwrite: true) {
  2. doFirst {
  3. println '你好啊,Gradle'
  4. }
  5. }
  1. def task = task([overwrite: true], build) {
  2. doFirst {
  3. println '你好啊,Gradle'
  4. }
  5. }
  • 程序执行命令:
  1. gradle build
  • 程序执行结果:
  1. 10:48:54: 正在执行 'build'
  2. > Task :compileJava UP-TO-DATE
  3. > Task :processResources NO-SOURCE
  4. > Task :classes UP-TO-DATE
  5. > Task :jar
  6. > Task :assemble
  7. > Task :compileTestJava UP-TO-DATE
  8. > Task :processTestResources NO-SOURCE
  9. > Task :testClasses UP-TO-DATE
  10. > Task :test
  11. > Task :check
  12. > Task :build
  13. 你好啊,Gradle
  14. BUILD SUCCESSFUL in 813ms
  15. 5 actionable tasks: 3 executed, 2 up-to-date
  16. 10:48:55: 执行完成 'build'

5.8 任务失败

  • 既然现在操作的是多任务,那么多任务彼此之间就可能有任务会出现失败情况,那么如果有的任务失败了会影响到其他的任务吗?为了模拟任务的失败,手动抛出一个任务停止的异常,修改 build.gradle 文件:
  1. def taskA = task helloTask {
  2. doFirst {
  3. if (true) {
  4. throw new StopExecutionException('我出异常了,你能拿我咋办?')
  5. }
  6. println '你好啊,Groovy 中的 helloTask'
  7. }
  8. }
  9. def taskB = task helloTask1 {
  10. dependsOn taskA
  11. doFirst {
  12. println '你好啊,Groovy 中的 helloTask1'
  13. }
  14. }
  • 程序执行命令:
  1. gradle helloTask1
  • 程序执行结果:
  1. 10:58:44: 正在执行 'helloTask1'
  2. > Task :helloTask
  3. > Task :helloTask1
  4. 你好啊,Groovy 中的 helloTask1
  5. BUILD SUCCESSFUL in 44ms
  6. 2 actionable tasks: 2 executed
  7. 10:58:44: 执行完成 'helloTask1'
  • 虽然此时第一个任务中产生了各种异常,但是并不影响到后续的其他任务的执行。

第六章:Gradle 文件处理任务

6.1 概述

  • 在以后编写最多的处理任务就是进行项目中各类资源文件的控制了,按照一个正规的项目开发来说,肯定需要提供有 resources、config 之类的文件目录,这些文件目录在进行打包的时候,肯定要将其全部打包的目标目录之中,此时就需要进行文件操作了。

6.2 文件创建

  • 既然要打包,通常而言都会提供一些配置的目录,同时在配置目录下会存在有许多 *.properties 的文件,在 src 目录下创建一个 config/database.properties 文件,内容如下:
  1. # 随便写
  2. druid.database.name=erp

8.png

6.3 文件定位

  • 如果要进行各种资源文件的处理,那么首先肯定需要解决的就是文件定位的问题了,修改 build.gradle 文件:
  1. task fileTask { // 文件处理任务
  2. doFirst {
  3. // org.gradle.api.Project 提供的 File file(Object var1);
  4. File configFileA = file('src/config/database.properties')
  5. println '【文件定位 - A】' + configFileA
  6. // 如果觉得以上的 file() 方式不习惯,那么也可以按照传统的方式编写完整路径
  7. File configFileB = file(new File('src' + File.separator + 'config' + File.separator + 'database.properties'))
  8. println '【文件定位 - B】' + configFileB
  9. }
  10. }
  • 程序执行命令:
  1. gradle fileTask
  • 程序执行结果:
  1. 13:32:23: 正在执行 'fileTask'
  2. > Task :fileTask
  3. 【文件定位 - AD:\project\gradle-01\src\config\database.properties
  4. 【文件定位 - BD:\project\gradle-01\src\config\database.properties
  5. BUILD SUCCESSFUL in 204ms
  6. 1 actionable task: 1 executed
  7. 13:32:23: 执行完成 'fileTask'
  • 在 Gradle 里面可以使用 Project 接口所提供的 file() 函数,直接针对于当前项目中的资源进行定位处理。

6.4 资源集合

  • 如果一个项目中包含多种资源,那么也可以考虑使用资源集合的形式进行配置,在 src 目录下创建一个 config/dubbo.properties 文件,内容如下:
  1. dubbo.application.name=gradle

9.png

  • 修改 build.gradle 文件:
  1. task fileTask { // 文件处理任务
  2. doFirst {
  3. // org.gradle.api.Project 提供的 ConfigurableFileCollection files(Object... var1);
  4. def fileCollection = files('src/config/database.properties','src/config/dubbo.properties')
  5. fileCollection.each { File file ->
  6. println file
  7. }
  8. }
  9. }
  • 程序执行命令:
  1. gradle fileTask
  • 程序执行结果:
  1. 13:45:57: 正在执行 'fileTask'
  2. > Task :fileTask
  3. D:\project\gradle-01\src\config\database.properties
  4. D:\project\gradle-01\src\config\dubbo.properties
  5. BUILD SUCCESSFUL in 47ms
  6. 1 actionable task: 1 executed
  7. 13:45:58: 执行完成 'fileTask'
  • 通过 files 函数返回的是一个 FileCollection 文件集合,这个集合属于一个可迭代的对象,所以可以使用 each() 函数。

6.5 集合操作

  • 通过 FileCollection 可以获取文件的集合,但是有些情况下可能需要对集合中的内容进行扩充,在 src 目录下创建一个 config/redis.properties 文件,内容如下:
  1. redis.server.address=192.168.0.1

10.png

  • 修改 build.gradle 文件:
  1. task fileTask { // 文件处理任务
  2. doFirst {
  3. // org.gradle.api.Project 提供的 ConfigurableFileCollection files(Object... var1);
  4. def fileCollection = files('src/config/database.properties', 'src/config/dubbo.properties')
  5. def union = fileCollection + files('src/config/redis.properties') // 集合相加
  6. union.each { File file -> // 输出相加后的集合
  7. println file
  8. }
  9. println '----------------------------'
  10. def diff = fileCollection - files('src/config/database.properties')
  11. diff.each { File file -> // 输出相减后的集合
  12. println file
  13. }
  14. println '----------------------------'
  15. println '【增加后的集合】' + union.files // 输出全部的内容
  16. println '【减少后的集合】' + diff.singleFile // 单个集合中的内容,必须确定集合中只有一个
  17. }
  18. }
  • 程序执行命令:
  1. gradle fileTask
  • 程序执行结果:
  1. 14:08:55: 正在执行 'fileTask'
  2. > Task :fileTask
  3. D:\project\gradle-01\src\config\database.properties
  4. D:\project\gradle-01\src\config\dubbo.properties
  5. D:\project\gradle-01\src\config\redis.properties
  6. ----------------------------
  7. D:\project\gradle-01\src\config\dubbo.properties
  8. ----------------------------
  9. 【增加后的集合】[D:\project\gradle-01\src\config\database.properties, D:\project\gradle-01\src\config\dubbo.properties, D:\project\gradle-01\src\config\redis.properties]
  10. 【减少后的集合】D:\project\gradle-01\src\config\dubbo.properties
  11. BUILD SUCCESSFUL in 251ms
  12. 1 actionable task: 1 executed
  13. 14:08:56: 执行完成 'fileTask'

6.6 文件列表

  • 以上的做法是将所有文件的完整路径进行单独的配置,这样的配置实在是太繁琐了,那么就可以考虑指定目录下的文件进行全部加载操作,修改 build.gradle 文件:
  1. task fileTask { // 文件处理任务
  2. doFirst {
  3. File configFile = file('src/config') // 定位要使用的文件路径
  4. def fileCollection = files(configFile.listFiles()) // 文件列表
  5. fileCollection.each {File file ->
  6. println file
  7. }
  8. }
  9. }
  • 程序执行命令:
  1. gradle fileTask
  • 程序执行结果:
  1. 14:16:05: 正在执行 'fileTask'
  2. > Task :fileTask
  3. D:\project\gradle-01\src\config\database.properties
  4. D:\project\gradle-01\src\config\dubbo.properties
  5. D:\project\gradle-01\src\config\redis.properties
  6. BUILD SUCCESSFUL in 54ms
  7. 1 actionable task: 1 executed
  8. 14:16:05: 执行完成 'fileTask'
  • 此时直接设置了一个加载的目录,随后就可以将此目录中的全部内容进行自动的加载,整个的程序实在是太容易了。

6.7 文件树

  • 文件树指的是一个按照层次结构分布的文件集合,如:一个文件树可以描述一个目录的组成结构,而在 Gradle 里面可以直接通过 fileTree() 来生成这样的文件树,修改 build.gradle 文件:
  1. task fileTask { // 文件处理任务
  2. doFirst {
  3. def tree = fileTree('src') // 对 src 目录进行列表
  4. tree.exclude('**/*.java') // 不显示 *.java 源文件
  5. tree.include ('**/*.properties','**/*.xml')
  6. tree.visit { element -> // 查看整个文件树的全部内容
  7. println element
  8. }
  9. }
  10. }
  • 程序执行命令:
  1. gradle fileTask
  • 程序执行结果:
  1. 14:26:52: 正在执行 'fileTask'
  2. > Task :fileTask
  3. file 'D:\project\gradle-01\src\config'
  4. file 'D:\project\gradle-01\src\config\database.properties'
  5. file 'D:\project\gradle-01\src\config\dubbo.properties'
  6. file 'D:\project\gradle-01\src\config\redis.properties'
  7. file 'D:\project\gradle-01\src\main'
  8. file 'D:\project\gradle-01\src\main\java'
  9. file 'D:\project\gradle-01\src\main\java\com'
  10. file 'D:\project\gradle-01\src\main\java\com\github'
  11. file 'D:\project\gradle-01\src\main\java\com\github\fairy'
  12. file 'D:\project\gradle-01\src\main\java\com\github\fairy\era'
  13. file 'D:\project\gradle-01\src\main\java\com\github\fairy\era\service'
  14. file 'D:\project\gradle-01\src\main\java\com\github\fairy\era\service\impl'
  15. file 'D:\project\gradle-01\src\main\resources'
  16. file 'D:\project\gradle-01\src\test'
  17. file 'D:\project\gradle-01\src\test\java'
  18. file 'D:\project\gradle-01\src\test\java\com'
  19. file 'D:\project\gradle-01\src\test\java\com\github'
  20. file 'D:\project\gradle-01\src\test\java\com\github\era'
  21. file 'D:\project\gradle-01\src\test\java\com\github\era\fairy'
  22. file 'D:\project\gradle-01\src\test\java\com\github\era\fairy\service'
  23. file 'D:\project\gradle-01\src\test\resources'
  24. BUILD SUCCESSFUL in 54ms
  25. 1 actionable task: 1 executed
  26. 14:26:52: 执行完成 'fileTask'

6.8 源文件目录

  • 在所有的构建工具创建的项目里面一般都会包含有源文件的目录,这些源文件的目录也可以直接在任务中进行访问,但是首先需要明确的定义出可能包含的源文件目录,修改 build.gradle 文件:
  1. sourceSets { // 建立源代码的目录集合
  2. main {
  3. java {
  4. srcDirs = ['src/main/java']
  5. }
  6. resources {
  7. srcDirs = ['src/main/resources', 'src/config']
  8. }
  9. }
  10. }
  11. task fileTask { // 文件处理任务
  12. doFirst {
  13. sourceSets.main.allJava.each { File file ->
  14. println file
  15. }
  16. println '-------------------'
  17. sourceSets.main.resources.each { File file ->
  18. println file
  19. }
  20. }
  21. }
  • 程序执行命令:
  1. gradle fileTask
  • 程序执行结果:
  1. 14:44:34: 正在执行 'fileTask'
  2. > Task :fileTask
  3. D:\project\gradle-01\src\main\java\com\github\fairy\era\service\IMessageService.java
  4. D:\project\gradle-01\src\main\java\com\github\fairy\era\service\impl\MessageServiceImpl.java
  5. -------------------
  6. D:\project\gradle-01\src\config\database.properties
  7. D:\project\gradle-01\src\config\dubbo.properties
  8. D:\project\gradle-01\src\config\redis.properties
  9. BUILD SUCCESSFUL in 41ms
  10. 1 actionable task: 1 executed
  11. 14:44:34: 执行完成 'fileTask'
  • 在实际的项目开发过程之中,以上配置里面所使用的 sourceSets 环境配置项目,开发中一般都会进行定义。

6.9 文件拷贝

  • 通过一系列的操作已经清楚了文件的基本流程,在以后进行项目打包输出控制的时候这样的操作是最为方便的,但是如果要想真正将其应用到项目里面还需要掌握文件拷贝的操作,将 config 目录移动到 src/main 下。

11.png

  • 修改 build.gradle 文件:
  1. sourceSets { // 建立源代码的目录集合
  2. main {
  3. java {
  4. srcDirs = ['src/main/java']
  5. }
  6. resources {
  7. srcDirs = ['src/main/resources', 'src/main/config']
  8. }
  9. }
  10. }
  11. task fileTask(type: Copy) { // 定义一个文件拷贝的子任务
  12. from 'src/main/config' // [配置文件]定义要拷贝的程序来源
  13. from 'src/main/resources' // [配置文件]定义要拷贝的程序来源
  14. from 'src/main/java' // [程序文件]编译后的文件内容
  15. into 'build/out' // 拷贝的目标路径
  16. include '**/*.xml' // 包含的文件类型(匹配后缀)
  17. include '**/*.properties' // 包含的文件类型(匹配后缀)
  18. exclude '**/*.java' // 不包含源文件
  19. }
  • 程序执行命令:
  1. gradle fileTask
  • 程序执行结果:

12.png

  • 在日后进行项目打包具体操作的时候,此类的路径的匹配操作一定会在项目中出现,尤其以 dubbo 为主。