自動化構建工具與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<String> 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<String, Object> person) { println("${person.name} is ${person.age} years old") } def printJobInfo(Map<String, Object> 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(Map<String, ?> options)
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
<variant>Compile
,<variant>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