Gradle Task 进阶:Task 依赖关系、输入输出、增量构建原理
Gradle Task 进阶:依赖关系、输入输出与增量构建
Gradle 的 Task 是构建过程的基本执行单元,理解 Task 的依赖关系、输入输出声明 和增量 增量构建原理对于对于优化构建性能至关重要。
一、Task 依赖关系
Task 之间的依赖关系定义了它们的执行顺序,Gradle 提供了多种方式来声明依赖:
1. 基本依赖声明
task taskA {doLast {println "Executing taskA"}
}task taskB {dependsOn taskAdoLast {println "Executing taskB"}
}
2. 动态依赖
// 依赖所有以"test"开头的任务
task integrationTest {dependsOn tasks.matching { task ->task.name.startsWith('test')}
}
3. 任务依赖规则
tasks.withType(JavaCompile).configureEach { task ->// 所有JavaCompile任务都依赖于checkStyle任务task.dependsOn checkStyle
}
4. 最终任务与mustRunAfter
task clean {doLast { /* 清理操作 */ }
}task build {doLast { /* 构建操作 */ }
}// 指定执行顺序,但不强制依赖
build.mustRunAfter clean
二、Task 输入输出
声明任务的输入输出是实现增量构建的基础,Gradle 通过跟踪这些值来判断任务是否需要重新执行。
1. 简单输入输出声明
task processResources {// 输入目录inputs.dir 'src/main/resources'// 输出目录outputs.dir 'build/resources'doLast {// 复制资源文件的逻辑copy {from 'src/main/resources'into 'build/resources'}}
}
2. 输入属性
task generateVersionFile {// 声明输入属性inputs.property('version', project.version)inputs.property('buildNumber', System.getenv('BUILD_NUMBER') ?: 'SNAPSHOT')// 输出文件outputs.file "$buildDir/version.txt"doLast {file("$buildDir/version.txt").text = """Version: ${project.version}Build Number: ${System.getenv('BUILD_NUMBER') ?: 'SNAPSHOT'}""".stripIndent()}
}
3. 嵌套输入
对于复杂对象作为输入,需要使用@Nested
注解:
class Credentials {String usernameString password
}task deploy {inputs.property('serverUrl', 'https://example.com')inputs.nested(new Credentials(username: 'admin', password: 'secret'))doLast {// 部署逻辑}
}
三、增量构建原理
增量构建是 Gradle 性能优化的核心特性,其工作原理如下:
-
任务执行前:Gradle 检查任务的输入和输出
- 计算所有输入的哈希值
- 检查输出是否存在且未发生变化
-
判断逻辑:
- 如果输入输出都没有变化,任务会被标记为
UP-TO-DATE
,跳过执行 - 如果输入发生变化或输出缺失/变化,任务会被执行
- 如果输入输出都没有变化,任务会被标记为
-
实现方式:
- Gradle 在
.gradle/[version]/taskArtifacts
目录中存储任务的输入哈希和输出元数据 - 每次构建时对比当前输入哈希与存储的哈希值
- Gradle 在
增量构建示例
task compileCustom {// 源代码作为输入inputs.files fileTree('src').include('**/*.custom')// 编译结果作为输出outputs.dir "$buildDir/classes/custom"doLast {println "Compiling custom files..."// 编译逻辑}
}
当src
目录下的.custom
文件没有变化时,再次运行会显示:
:compileCustom UP-TO-DATE
四、高级技巧
- 任务输出缓存:可以通过
outputs.cacheIf { true }
启用远程缓存
task heavyProcessing {inputs.files 'data'outputs.dir 'results'outputs.cacheIf { // 只有在CI环境才缓存输出System.getenv('CI') != null }doLast {// 耗时处理}
}
- 任务禁用与启用:
task legacyTask {enabled = false // 默认禁用doLast { /* 遗留逻辑 */ }
}
- 任务规则:动态创建任务依赖
tasks.addRule("Pattern: compile<Language>") { String taskName ->if (taskName.startsWith("compile")) {task(taskName) {doLast {println "Compiling with ${taskName - 'compile'} compiler"}}}
}
理解这些概念可以帮助你编写更高效、更可靠的 Gradle 构建脚本,显著提升构建性能,特别是在大型项目中。