自动化构建工具与gradle
一、定义
我们在学习c语言的时候都知道,c语言源程序从源程序到二进制程序,经过了预处理(Preprocessing), 编译(Compilation), 汇编(Assemble), 链接(Linking)的过程,在使用gcc进行编译时只需要gcc test.c -o test
就可以生成,或者复杂点,一步步生成中间代码:
gcc -e test.c -o test.i
gcc -s test.i -o test.s
gcc -c test.s -o test.o
gcc test.o -o test
那么复杂一些的工程呢,里面有各种数不胜数的源程序、头文件,都按模块、功能分好了类,总不能手工一个个编译吧,你可能会说:“那就用makefile啊”,没错,makefile可以帮助你按照顺序编译程序或者执行一些功能,它其实就是一种自动化构建工具(Build Automation)。
Build tools are programs that automate the creation of executable applications from source code. Building incorporates compiling, linking and packaging the code into a usable or executable form. In small projects, developers will often manually invoke the build process. This is not practical for larger projects, where it is very hard to keep track of what needs to be built, in what sequence and what dependencies there are in the building process. Using an automation tool allows the build process to be more consistent.
自动化构建工具就是自动将源码生成为可执行文件的工具。
常用的组建自动化工具就有Make、Rake、Cake、MS build、Ant、Gradle等。
原来我一直在用自动化构建工具啊。
二、它们能干什么
一般自动化构建工具有如下的feature:
- 丰富的插件库
- 编译管理工具
- 源码管理工具
- 各种可用的UI
- 和主流IDE兼容
- 并行测试或编译
- 依赖管理
- 版本冲突解决
- 协同调试工具
- 增量编译
- 最小化重新编译
- 自动事件/编译
- 框架的自动初始化
- 代码和资源的及时更新
- 构建缓存、可缓存任务
- 构建指标、性能跟踪
三、常用自动化构建工具
- 评价最高的自动化构建工具包括: Gradle, Codeship, TeamCity, and Travis CI.
- 其他的自动化构建工具包括: Jenkins, CircleCI, Bamboo, and Apache Maven.
四、关于Gradle
在写安卓app时,经常因为要引入第三方库或者修改一些系统信息而修改gradle,有时候build gradle的需要很长时间,这个过程中Gradle干了写什么?
这一部分主要翻译自Łukasz Wasylkowski的文章《Understanding Android Gradle build files》,链接见文末
1.gradle是什么
Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。 面向Java应用为主。当前其支持的语言限于Java、Groovy和Scala,计划未来将支持更多的语言。
2.Groovy
-
语法
gradle文件使用groovy脚本语言编写的,它的语法很容易理解,但需要注意以下几点:
-
调用函数时有一个或多个参数不需要括号。
def printAge(String name, int age) { print("$name is $age years old") } def printEmptyLine() { println() } printAge "John", 24 // Will print "John is 24 years old" printEmptyLine() // Will print empty line
-
最后一个参数是closure时(lambda),可以写在括号外面。
def callWithParam(String param, Closure
closure) { closure(param) } callWithParam("param", { println it }) // Will print "param" callWithParam("param") { println it } // Will print "param" callWithParam "param", { println it } // Will print "param" -
当参数中有定义map的元素时,它们将会被转化为一个map作为第一个元素,其他参数排在map参数之后。
def printPersonInfo(Map
person) { println("${person.name} is ${person.age} years old") } def printJobInfo(Map job, String employeeName) { println("$employeeName works as ${job.title} at ${job.company}") } printPersonInfo name: "John", age: 24 printJobInfo "John", title: "Android developer", company: "Tooploox"
-
-
closures
闭包是开放的、匿名的、可以执行的代码块,它们有参数、返回值,它们可以被分配给某个对象。
class WriterOne { def printText(str) { println "Printed in One: $str" } } class WriterTwo { def printText(str) { println "Printed in Two: $str" } } def printClosure = { printText "I come from a closure" } printClosure.delegate = new WriterOne() printClosure() // will print "Printed in One: I come from a closure printClosure.delegate = new WriterTwo() printClosure() // will print "Printed in Two: I come from a closure
3.关于gradle
-
脚本文件
Gradle主要用到三个脚本文件:
-
build.gradle
文件中的build 脚本 . 它们针对Project
对象执行; -
settings.gradle
文件中的settings 脚本, 针对Settings
对象执行; - init 脚本 用于全局设置(针对
Gradle
实例执行)
-
-
projects
gradle中含有一个或多个project,project有task组成,至少存在一个root project,它的里面可能还有subproject。按照惯例,root project主要提供了project布局、通用设置、插件路径。
4.创建一个基于gradle的Android工程项目
一般的安卓目录如下:
├── settings.gradle # [1]
├── build.gradle # [2]
├── gradle
│ └── wrapper
└── app
├── gradle.properties # [3]
├── build.gradle # [4]
└── src
[1]这是root project的设置文件,针对setting对象执行
[2]Root project的build设置
[3]App project的的配置文件,针对app的 Settings
[4]App project’的build设置
1.创建一个gradle项目
新建一个example文件夹,cd到其目录,然后执行gradle projects
,就会发现已经新建好一个gradle project了。
2.设置projects层次结构
如果我们想要一个和默认的安卓项目相似的结构,我们需要一个setting.gradle
文件,它配置了实例和project
实例的层次。
setting.gradle
文件中使用 void include(String[] projectPaths)
方法可以添加一个project,我们用它来添加一个app
的subproject。
echo "include ':app'" > settings.gradle
gradle projects
3构建Android subproject
现在我们将设置root project的build.gradle
文件。
我们可以通过apply
方法将com.android.application
导入到app
project中。
void apply(Closure closure)
void apply(Mapoptions)
void apply(Action super ObjectConfigurationAction> action)
mkdir app
echo apply plugin: 'com.android.application' > app/build.gradle
gradle app:tasks
但是输出了错误信息:
错误之处找不到'com.android.application'的位置,这很容易理解,因为我们没有为它指定路径,我们在root或subproject的build.gradle
中通过添加buildscript
来设置路径。
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
}
}
继续出错,没有指定buildToolsVersion版本,在app的build.gradle
添加如下代码,然后运行:
android {
buildToolsVersion "25.0.1"
compileSdkVersion 25
}
android
方法定义了buildToolsVersion
and compileSdkVersion
两个方法。
又有报错了,提示找不到Android SDK,需要在local.properties
中定义或者添加环境变量。
我们就在root下新建一个local.properties
并写入SDK的路径,然后运行:
echo sdk.dir=C\:\\Users\\getia\\AppData\\Local\\Android\\Sdk> local.properties
gradle app:tasks
又又报错了,这次是AndroidManifest.xml
,我们在app/src/main目录下新建AndroidManifest.xml
文件并向其中添加以下语句,再次运行:
mkdir app\src
mkdir app\src\main
echo apply plugin: 'com.android.application' > app/src/main/AndroidManifest.xml;
gradle app:tasks
终于成功了,我流下了没有技术的眼泪。
4.添加依赖(Dependencies)
dependencies {
compile 'io.reactivex.rxjava2:rxjava:2.0.4'
testCompile 'junit:junit:4.12'
annotationProcessor 'org.parceler:parceler:1.1.6'
}
Dependencies是一个特殊的代码块,如果你查看Dependencies的文档DependencyHandler ,你会发现根本就没有compile
或者testCompile
方法。因为它使用了groovy语言的另一种特性methodMissing,它在没有找到对应方法名或参数表时调用:当调用了一个没有定义的方法,如果它的参数大于1个并且这个名字的方法被配置(*)过,那么就根据它的参数数目和类型,调用doAdd
方法。
(*) 每一个插件都可以给 dependencies handler添加配置. 比如
java
插件 定义列compile
,compileClasspath
,testCompile
和一些其他配置。Android 插件annotationProcessor
,
Compile 等配置。
TestCompile
doAdd
方法是私有的, i它可以被 公有方法add
调用。 所以我们可以这么来重写Dependencies代码块(*) :
dependencies {
add('compile', 'io.reactivex.rxjava2:rxjava:2.0.4')
add('testCompile', 'junit:junit:4.12')
add('annotationProcessor', 'org.parceler:parceler:1.1.6')
(*) 一般情况下不要这么做
参考文献
https://www.techopedia.com/definition/16359/build-tool
https://www.trustradius.com/build-automation
https://medium.com/@wasyl/understanding-android-gradle-build-files-e4b45b73cc4c