本节以软件项目的版本管理作为例子来学习gradle的task的使用
gradle构建生命周期
想要理解task的使用方法,理解gradle'build lifecycle phases(gradle构建生命周期各阶段)是至关重要的,无论什么时候执行gradle构建,都会运行三个不同的生命周期:如下
- 初始化阶段(Initialization phase)
这个阶段,gradle在内存中为项目创建了一个Project实例。根据在执行的项目,gradle找出哪些项目依赖需要参与到构建中,在这个阶段当前已有的构建脚本代码并不会被执行。
- 配置阶段(Configuration phase)
初始化阶段完成后,进入配置阶段。在这个阶段gradle构造任务的实例,所以这个阶段非常适合于为项目或task设置所需的配置。
需要明确,写在gradle构建脚本中的代码分两种:配置代码和动作代码- 配置代码(Configuration code):
在配置阶段执行的代码,除动作代码之外的代码都是配置代码。注意,项目每一次构建的任何配置代码都可以被执行,即使你只指定gradle tasks
- 动作代码(Action code)
Task接口提供了两个相关的方法来声明task动作:doFirst(Closure)和doLast(Closure),当task被执行时,动作逻辑被定义为闭包参数被依次执行。
- 配置代码(Configuration code):
- 执行阶段(Execution phase)
在执行阶段,所有的task的动作逻辑都应该以正确顺序被执行,执行顺序由它们的依赖决定的。如果任务被认为没有修改过,则将被跳过。
声明task动作(action)
version = '0.1-SNAPSHOT'
task printVersion{
doLast{
println "Version: $version"
}
}
添加动作
printVersion.doFirst { println "First action" }
printVersion << { println "Last version" }
在内部,每个task都保持了一个动作列表,在运行时,他们按顺序执行。
访问DefaultTask属性
关于DefaultTask的全部属性可以通过查看源代码或者在官网的gradleAPI文档查看。
Gradle的Project和Task都提供了一个logger属性,logger是一个基于SLF4J日志库的logger实现。除了实现常规范围内的日至级别(DEBUG、ERROR、INFO、TRACE、WARN)之外,Gradle还增加了一些额外的日至级别。直接调用logger,就相当于调用它的get方法。(牢记在groovy中访问属性是groovy提供的语法糖)
下面使用logger打印QUIET日志级别版本号
task printVersion << {
logger.quiet "Version: $version"
}
Task的属性group制定了该task的分组,description指定task描述。在使用命令gradle tasks
时可以看到task的分组与描述。在创建时可以用如下方式给task的属性赋值:
task printVersion(group: 'versioning',description:'Prints project version'){
logger.quiet "Version:$version"
}
或者去掉语法糖
task ([group: 'versioning',description:'Prints project version'],'printVersion',{
logger.quiet "Version:$version"
})
或者直接在委托闭包中使用set来赋值
task printVersion{
group = `versioning`
description = 'Prints project version.'
doLast {
logger.quiet "Version:$version"
}
}
定义task的紧前与紧后
这里借用工作流程中紧前工序与紧后工序来理解下面两个概念:
- task依赖(task dependencies):
相当于目前task的紧前task。
dependsOn方法允许声明依赖一个或多个task,或者使用dependsOn的set方法来设置task的依赖,下面的例子体现了这两种声明task依赖方式:
task first << {println "first"}
task second << {print "second"}
task printVersion(dependOn: [second,first]) << {
logger.quiet "Version: $version"
}
这种方式属于调用dependsOn的set方法来声明依赖,这是一种类似于groovy"命名参数"语法糖的一种糖,实际上调用了Task的如下方法:
或者像下面这样声明依赖:
task third << { println "third" }
third.dependOn('printVersion')
这种方式是调用Task的dependOn方法:
需要明确一点,dependsOn方法只是定义了所以来的task需要先执行,而没有定义真正的task执行顺序,在gradle中,执行顺序是由task的 input/output 自动确定的。
- 终结器task(Finalizer tasks):
定义终结器task相当于定义目前task的紧后task
与dependsOn类似,gradle提供了finalizedBy来声明终结器task,用法也和dependsOn相同,因为它们的源码形式都相同:
添加任意代码
在gradle构建脚本中可以添加面向对象代码来实现对POJO的抽象,提高代码复用性。当然也可以把这些封装的类写在单独的文件中,在这里先写在构建脚本中
version = new ProjectVersion(0,1) //在gradle中version属性类型是Object,输出它默认使用toString方法
class ProjectVersion{
Integer major
Integer minor
Boolean release
ProjectVersion(Integer major, Integer minor){
this.major = major
this.minor = minor
this.release = Boolean.FALSE
}
ProjectVersion(Integer major, Integer minor,Boolean release){
this.major = major
this.minor = minor
this.release = release
}
@oberride
String toString(){
"$major.$minor${release? '' : '-SNAPSHOT'}"
}
}
添加task配置
在前面了解了gradle生命周期,明确了配置代码与动作代码的区别,所以在为项目添加版本号时,可以把版本号写在外部文件中,然后在gradle配置代码中读取它,然后再利用读取到的版本号执行task动作代码,这样可以避免因为版本更替而需要修改构建脚本的问题。
- 创建一个名为version.properties的属性文件,并写入如下内容作为最初的版本属性:
major = 0
minor = 1
release = false
- 定义一个新的project属性,用来代表属性文件的File对象,添加一个loadVersion task ,用作task配置:
ext.versionFile = file('version.properties')
task loadVersion {
project.version = readVersion()
}
ProjectVersion readVersion() {
logger.quiet 'Reading the version file'
if(!versionFile.exists()){
throw new GradleException("Required version file does not exist : $versionFile.canonicalPath")
Properties versionProps = new Properties()
versionFile.withInputStream{
stream ->
versionProps.load(stream)
}
new ProjectVersion(versionProps.major.toInteger(),
versionProps.minor.toInteger(),
versionProps.release.toBoolean())
}
}
这时,若使用命令gradle printVersion
,就会发现loadVersion和printVersion被调用。这是因为loadVersion中的配置代码会在配置阶段就被调用,及时使用命令gradle tasks
,如果不加限制,所有的配置代码都会执行。
如:在脚本中写入
task printVersion(group: 'versioning',description:'print current version'){
logger.quiet "Version:$version"
}
printVersion{
println "END printVersion"
}
然后执行gradle tasks
,输出如下:
下一节继续学习task的高级使用方法,包括增量式构建、自定义Task、gradle内置task类型、task依赖推断、task规则等内容