增量式构建
- gradle判断task是否改变过的依据是:这个task的inputs和outputs属性组成的集合。当一个task链被执行时,如果其中某一个task的inputs和outputs没有发生改变,则认为该task是最新的,该task将被跳过,在执行链输出中可以看到该task被标为
UP-TO-DATE
,下图为Task中的imputs和outputs属性
- inputs 属性应该被赋值为 一个目录、一个或多个文件、或是一个任意属性
- outputs 应该被赋值为一个或多个目录或者一个或多个文件
那么增量式构建使用场景是怎样的呢?下面是一个例子:
- 例子
假设一个task,makeReleaseVersion,在项目发布部署之前要修改项目版本的release值为true:
task makeReleaseVersion(group: 'versioning' , description: 'Makes Project a release version.') << {
version.release = true
ant.propertyfile(file: versionFile){
entry(key: 'release', type: 'string',operation: '=',value:'true')//利用ant task的propertyfile提供的便利方式来修改属性文件
}
}
但是意外发生了,在自动化发布的过程中无法连上服务器,在修复故障后,重新运行自动化发布的任务,因为部署任务依赖于修改版本号任务,所以makeReleaseVersion任务会再次被运行,即使版本号已经在上次运行过程中被修改。
为解决这一问题,需要采用gradle提供的增量式构建特性,为该task的inputs和outputs赋值:
task makeReleaseVersion(group: 'versioning' , description: 'Makes Project a release version.') {
inputs.property ('release',version.release)
outputs.file versionFile //由于该任务会修改版本文件,所以它被声明为outputs
doLast{
version.release = true
ant.propertyfile(file: versionFile){
entry(key: 'release', type: 'string',operation: '=',value:'true')//利用ant task的propertyfile提供的便利方式来修改属性文件
}
}
}
这样就实现了增量式构建
- task的inputs和outputs的执行
确保inputs和outputs的值在配置阶段是可访问的,因为它们在配置阶段被执行。如果要通过编程获得输出,则可以通过upToDateWhen(Closure var1)方法来实现,与常规的inputs和outputs的set方法比,这个方法是在执行阶段执行的,如果闭包返回true,则这个task被认为是最新的。
自定义task
在gradle中,默认Task的实现类为DefaultTask,而DefaultTask中空无一物,全部继承自AbstractTask。当构建task的动作逻辑变得相当复杂时,必然会产生结构化task代码的需求,这时就可以实现自定义的Task实现类。
下面将makeReleaseVersion封装为ReleaseVersionTask的自定义Task
class ReleaseVersionTask extends DefaultTask {
@Input Boolean release //通过org.gradle.api.tasks包下的注解来声明输入输出
@OutputFile File destFile
ReleaseVersionTask(){
group = 'versioning'
description = 'Makes Project a release version.'
}
@TaskAction //使用注解声明执行代码,
void start(){
project.version.release = true
ant.propertyfile(file: destFile){
entry(key: 'release', type: 'string',operation: '=',value:'true')//利用ant task的propertyfile提供的便利方式来修改属性文件
}
}
}
注意:@Input注解会在配置期间验证属性值,如果为null,gradle会抛出TaskValidationException异常。为了允许输入null,需要给它添加@Optional注解。
- 使用方法
task makeReleaseVersion(type: ReleaseVersionTask) {
release = version.release
destFile = versionFile
}
Gradle的内置task类型
在Gradle DSL指南中可以找到完整的task参考
下面用一个使用类型为内置的task类型Zip的task
task createDistribution(type: Zip, dependsOn: makeReleaseVersion){
from war.outputs.files //依赖推断
from(sourceSets*.allSource){
into 'src'
}
from(rootDir){
include versionFile.name
}
}
- task依赖推断
在上面的createDistribution task中声明了依赖的task,但是一些task并不直接依赖其他task,比如createDistribution对于war。通过使用一个task的输出作为另一个task的输入,Gradle就可以推断出依赖关系,所依赖的task会自动运行。
task规则
Gradle引入task规则的概念,就是根据task名称模式执行特定的逻辑。
- task规则命名模式
task名称的静态部分和一个占位符组成一个task规则名字,例如increment<Classifier>Version
。java插件的clean任务便利用了task规则,clean<TaskName>
用来删除指定task的输出。
- 声明task规则
- 首先,获得对TaskContainer的引用,该引用为tasks
- 然后,调用addRule(String,Closure)方法,第一个参数提供了描述信息(比如,task命名模式),第二个参数声明了要执行的闭包来应用规则。
- 例如这样:
tasks.addRule("Pattern: increment<Classifier>Version - Increments the project version classifier."){ String taskName -> if(taskName.startsWith('increment') && taskName.endWith('Version')){ task(taskName)<<{ String classifier = (taskName - 'increment'- 'Version').toLowerCase() //接下来便是分支判断代码 } } }
- task规则不能像处理任何其它简单的task或增强的task一样被独立分组,task规则会显示在Rules组下。
构建源代码目录buildSrc
事实上在build.gradle文件中不适合写面向对象的代码,所以应该把之前的POJO类和自定义task类像项目源代码一样组织起来,类似于java源代码放在src/main/java目录下、Groovy代码放在src/main/groovy目录下,buildSrc用于组织构建代码源文件。构建代码包可以放在buildSrc/src/main/groovy或buildSrc/src/main/java下,这样还方便写构建代码的单元测试代码。