Gradle学习(十一)——构建脚本基础知识

时间:2022-12-26 20:11:28

转载请注明出处:http://blog.csdn.net/lastsweetop/article/details/78962619

项目和任务

Gradle的所有都是基于两个基础概念:项目和任务

每个Gradle构建是由一个或者多个项目构成的,项目表示什么取决于你用Gradle做什么。比如,项目可以表示jar库或者web程序。它也可以表示由其他项目生产的jar包后打包成的发布zip包。一个项目不一定非要表示构建什么。它也可以表示做什么,比如把你的app发布到脚手架或者生产环境。讲到这里,你可能有点迷糊,但是不要担心,Gradle对按惯例构建的支持给出了项目是什么更明确的定义。

每个项目是由一个和多个任务构成的。任务表示构建运行的工作原件。它可以是编译classes,创建jar,生成javadoc,也可以是发布archives到仓库中。

我们本章内容都是同一个项目下的一些简单的任务操作,多项目交互的要到后面的章节体现。

Hello World

运行gradle命令时,Gralde回去查找当前目录下的build.gradle文件,这个文件就可以叫做构建脚本,虽然看起来更像是构建配置脚本。构建脚本中定义了一个项目和项目的一些任务。

build.gradle的内容如下:

task hello {
doLast {
println 'Hello World'
}
}

然后在当前目录执行gradle -q hello命令,结果如下:

± % gradle -q hello 
Hello World

让我们来看看发生了什么,首先构建脚本定义了一个叫hello的任务,并且添加了一个action。当你运行gradle -q hello的时候,Gradle就会执行hello任务,然后执行你提供的action。这个action只是一个包含了简单的groovy代码的closure。

构建脚本即code

构建脚本其实就是groovy代码,通过强力的脚本语言groovy让构建脚本更加强力,例如:

task upper {
doLast {
def str = 'sWeeTop'
println 'Original:' + str
println 'Upper Case:'+str.toUpperCase()
}
}

然后执行upper任务:

± % gradle -q upper                                                       
Original:sWeeTop
Upper Case:SWEETOP

再来一个:

task count {
doLast {
4.times { println it}
}
}

然后执行count任务:

± % gradle -q count                                                       
0
1
2
3

任务依赖

我们在命令行那张也涉及了一点任务依赖的例子,我们现在再来讲解一下,例子如下:

task hello {
doLast {
println 'Hello World'
}
}
task intro(dependsOn:hello) {
doLast {
println "I'm Gradle"
}
}

然后执行intro任务:

± % gradle intro -q                                                       
Hello World
I'm Gradle

所依赖的任务还可以是预先未定义的,例子如下:

task taskX(dependsOn: 'taskY') {
doLast {
println 'taskX'
}
}

task taskY() {
doLast {
println 'taskY'
}
}

然后执行taskX任务:

± % gradle taskX -q 
taskY
taskX

依赖于taskY的任务taskX却比taskY先定义,这个特性对多任务构建来说很重要。

还有一点要注意就是,采取惰性依赖你就无法使用所依赖任务的快捷符号了

动态任务

强力的Groovy各种66666,比如你可以定义动态的任务:

4.times { number ->
task "task$number" {
doLast {
println "I'm task number $number"
}
}
}

然后执行task2任务:

± % gradle task2 -q                                                       
I'm task number 2

操作已存在的任务

一旦任务被创建之后,你就可以通过api直接访问了,比如你可以动态的给一个任务增加点依赖,ant可以做不到这点,示例:

4.times { number ->
task "task$number" {
doLast {
println "I'm task number $number"
}
}
}

task0.dependsOn task2,task3

然后我们只想task0任务:

± % gradle task0 -q                                                       
I'm task number 2
I'm task number 3
I'm task number 0

或者为已经存在的任务增加点行为:

task hello {
doLast {
println 'Hello World'
}

}
hello.doFirst {
println 'Hello Dong'
}

hello.doLast {
println 'Hello Nan'
}

hello {
doLast {
println 'Hello Bei'
}

}

然后执行hello任务:

± % gradle hello -q 
Hello Dong
Hello World
Hello Nan
Hello Bei

doFirstdoLast能被多次执行,他们可以在任务的开始和结尾增加action,任务执行的时候,action列表就会依次执行。

快捷符号

有个非常方便的符号可以访问已存在的任务,在构建脚本中的每个任务都有这些属性:

task hello {
doLast {
println 'Hello World'
}
}

hello.doLast {
println "Greeting from the $hello.name task"
}

然后执行hello任务:

± % gradle hello -q                                                      
Hello World
Greeting from the hello task

这样可以写出更加易读性的代码,特别是用那些插件提供的任务,比如compile任务

扩展任务属性

你可以为任务增加自己的属性,要增加myProperty属性你给ext.myProperty设置一个初始值,然后这个属性就和其他预先定义的属性一样被读取和设置。示例:

task myTask {
ext.myProperty='init value'
}

task printTaskProperty {
doLast {
println myTask.myProperty
}
}

然后执行printTaskProperty任务

± % gradle printTaskProperty -q                                           
init value

使用ant任务

ant任务是gradle的一等公民,gradle把通过把ant任务完全交托给groovy实现了卓越的集成。在groovy这里是由奇妙的AntBuilder提供相关功能。在Gradle里使用ant任务就像直接在build.xml里的任务一样简单而又强力。下面的例子演示了如何执行ant任务和访问ant属性:

task loadFile {
doLast {
def files = file('../unpublished').listFiles().sort()
files.each { File file ->
if (file.isFile()) {
ant.loadfile(srcFile: file,property:file.name)
println " ***$file.name*** "
println ant.properties[file.name]
}
}
}
}

然后执行loadFile任务:

± % gradle loadFile -q                                                    
***build.gradle***
apply plugin: 'java'

archivesBaseName='hello'
***settings.gradle***
version = 1.0

使用方法

gralde在组织构建逻辑时可以进行分级扩展,组织构架逻辑的第一级就是提取方法:

task loadFile {
doLast {
fileListEachAction('../unpublished') { File file ->
ant.loadfile(srcFile: file, property: file.name)
println " ***$file.name*** "
println ant.properties[file.name]
}
}
}

task checkSum {
doLast {
fileListEachAction('../unpublished') { File file ->
ant.checksum(file: file, property: "cs_$file.name")
println "$file.name checksum is ${ant.properties["cs_$file.name"]}"
}
}
}

def fileListEachAction(String path, Closure closure) {
def files = file(path).listFiles().sort()
files.each { File file ->
if (file.isFile()) {
closure(file)
}
}
}

然后执行checkSum任务:

± % gradle checkSum -q                                                    
build.gradle checksum is 193e5061bf3681c0434d6f4d937f1fc3
settings.gradle checksum is 0cba3f2c2582e03de4dc86f1d495a39b

后面还会讲到,这样的方法在多项目构建中也可以跨项目调用。

默认任务

Gradle可以定义一个或者多个默认任务,当没有特别指定具体任务时,默认任务就会执行

defaultTasks 'clean','run'

task clean {
doLast {
println "task $it is running"
}
}

task run {
doLast {
println "task $it is runing"
}
}

执行默认任务:

± % gradle -q                                                             
task task ':clean' is running
task task ':run' is runing

相当于运行了gralde -q clean run,在多项目构建中,每个子项目都可以设置自己的默认任务,当没有子项目的默认任务被指定那么就会执行root项目的默认任务

通过DAG配置Gradle

Gradle有配置阶段和运行阶段,在配置阶段之后,Gradle就会知道所以需要执行的任务,Gradle提供了hook可以让你使用这些信息,有一个用例就是你需要确认release任务是否在执行的任务之中,在这个基础上你可以设置不同的变量。

下面的例子会演示contribution任务和release任务下会有不同的version值:

task distribution {
doLast {
println "Current Version is $version"
}
}

task release(dependsOn: distribution) {
doLast {
println 'We release now'
}
}

gradle.taskGraph.whenReady { taskGraph ->
if (taskGraph.hasTask(release)) {
version = '1.0.0'
} else {
version = '1.0.0-SNAPSHOT'
}
}

执行distribution任务:

± % gradle distribution -q                                                
Current Version is 1.0.0-SNAPSHOT

执行release任务:

± % gradle release -q                                                     
Current Version is 1.0.0
We release now

最重要的是whenReady会影响release任务是在release任务执行之前,即使release任务不是该次构建的主要任务。