Gradle自学——构建脚本
1. 认识构建脚本
Gradle
所做的事情往往是由一个个任务组成的,而每一个任务可以认为是一个原子
操作
通过gradle
命令可以运行Gradle
构建,这个命令会去当前目录下寻找build.gradle
文件,而这个文件被称为构建脚本
当然,在使用命令之前还是需要事先配置一下GRADLE_HOME
的环境变量
接下来,开始编写第一个构建脚本,可以直接在Android项目的目录下的build.gradle
文件中进行编写
tasks.register('hello') {
doLast { // 这就是Action
println 'Hello World'
}
}
然后回到当前目录下,输入命令gradle -q hello
,这个命令是什么意思?直接gradle -h
查看帮助文档
这个参数的含义是只输出报错日志,过滤了其他日志,因此只会显示结果
以上内容仅仅是定义了一个
task
,并且向其中添加了一个Action
,作为任务执行的具体逻辑
查阅
DSL
的手册,可以找到关于doLast
这个Action
的定义,这将会在当前Task
中作为最后执行的Action
2. Task依赖
一个Task
是可以依赖于其他Task
的
tasks.register('hello') {
doLast {
println 'Hello World'
}
}
tasks.register('something') {
dependsOn tasks.hello // 依赖于另一个任务
doLast {
println '还有一件事'
}
}
既然something任务需要依赖于hello任务,那么hello就会先执行,然后轮到something,当然,这与你定义的位置无关
3. Task注册
可以非常方便地对于任务进行批量注册
10.times { i -> // 0-9,注册10个任务
tasks.register("task$i") {
doLast {
println "task $i done"
}
}
}
既然都注册了,直接挑一个执行一下
$ gradle -q task9
task 9 done
4. 操作已经存在的Task
这些任务完成注册之后,其实就存在于当前项目下的任务列表中,我们可以对其进行一些操作
tasks.named('task3') {
dependsOn ('task7', 'task8')
}
执行3的前提是7和8执行完毕
$ gradle -q task3
task 7 done
task 8 done
task 3 done
接下来,再利用doFirst
和doLast
组合对同一个已注册的Task
进行操作
正如对应的
DSL
的文档中所阐述的,doFirst
会将闭包中的操作放到Task
列表的开头执行,而doLast
则是会放到末尾,这时,就需要关注定义的顺序了
tasks.register('trueman')
tasks.named('trueman') {
doLast {
print 'Good afternoon'
}
}
tasks.named('trueman') {
doFirst {
println "In case I don't see you..."
}
}
tasks.named('trueman') {
doLast {
print ', good evening'
}
}
tasks.named('trueman') {
doLast {
print ', and good night'
}
}
$ gradle -q trueman
In case I don't see you...
Good afternoon,good evening,and good night
当前就只有trueman这一个Task
,第二个定义的Action
会在开头执行,其他使用doLast
定义操作,根据顺序,依次排向末尾,按照定义的顺序执行
当然,如果改成doFirst
有多个,那么就是后定义的反而会放到前面,因为doFirst
和doLast
只会从两端进入
5. 使用方法
即使是在其他编写业务逻辑的场景下,提取方法或者函数其实也是为了结构更加清晰,使得代码有更强的复用性
在编写构建脚本时,往往也有一些相似的操作场景可以进行方法的提取,从而将其批量地应用到多个构建过程中
// 输出符号表
tasks.register('readMapping') {
doLast {
printContent("C:\\Develop\\groovy_projects\\test\\mapping.txt")
}
}
// 输出配置
tasks.register('readConfig') {
doLast {
printContent("C:\\Develop\\config\\test\\config.json")
}
}
// 按路径读取文件,并输出内容
def printContent(String path) {
def file = new File(path)
file.eachLine { // 将内容按行输出
println it
}
}
6. 默认Task
在没有指定具体的Task
的时候,Gradle
会运行定义的默认Task
defaultTasks('eat', 'relax') // 定义了默认
tasks.register('relax') {
doLast {
println '摸鱼'
}
}
tasks.register('eat') {
doLast {
println '干饭'
}
}
tasks.register('work') {
doLast {
println '发奋图强'
}
}
$ ./gradlew -q
干饭
摸鱼
这里运行的时候并没有指定某一个Task
,那么这就会执行我们声明为默认的Task
如果处在一个多项目的构建中,每个子级项目可以定义子级的默认Task
,如果没有,在未指定Task
的情况下,那么会去找父级项目的默认Task
7. 构建脚本的外部依赖
出现了新角色,首先来看下buildscript{}
这个块的文档
这个块所做的事情是为当前项目配置构建脚本的
classpath
,又出现了新的名词,待会再看
ScriptHandler
作为闭包的委托被传给buildscript
的闭包...(啥玩意)
先查下
ScriptHandler
,它是个接口,主要的职责是为构建脚本的编译和执行提供管理
然后在这里,正好出现了classpath
,它是用于编译和执行构建脚本的,另外也可以加载构建脚本用到的插件,在构建脚本编译前,这些依赖关系会被组装完成,至少大概了解它是做什么的了
尝试一下官方提供的实例,这里需要注意,buildscript{}
的位置需要在构建脚本中的plugins{}
声明之前
buildscript {
repositories { // 仓库
mavenCentral()
}
dependencies { // 依赖
classpath group: 'commons-codec', name: 'commons-codec', version: '1.11'
}
}
然后这里面的这两个块,repositories{}
和dependencies{}
对应于ScriptHandler
接口定义的两个方法
这些注释简直如出一辙,这又是分别由
DependencyHandler
和RepositoryHandler
来处理的
DependencyHandler
将添加的依赖附加到classpath
的配置当中,并且这些依赖在编译之前会被解析完成,汇编到脚本的classpath
当中
并且,大多数的依赖需要为其声明对应的仓库,从而让RepositoryHandler
能够从仓库中找到它们
在依赖被添加到classpath
之后,就可以直接使用这些类了
tasks.register('encode') {
doLast {
byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
println new String(encodedString)
}
}
注意,这里需要导下包,平常都是IDE帮忙,这里别忘了
import org.apache.commons.codec.binary.Base64 // 导包
输出结果
$ ./gradlew -q encode
aGVsbG8gd29ybGQK
如果构建多个模块的项目,那么外层的项目级中声明buildscript{}
,下层的模块是都可以进行共享的
调用buildEnvironment
可以查看依赖的解析情况,这是每一个项目中都会自带的一个Task
$ ./gradlew -q buildEnvironment
这就是你的classpath
中解析的各种依赖