Gradle與Makefile構建工具的對比
隨著Android Studio的普及,越來越多的Android開發者也要開始了解和學習Gradle這款強大的代碼構建工具了。我們在學習和了解一項新事物的時候,最快速的方法往往是與已知的事物進行比較,對於熟悉Makefile編譯機制的Linux程序員而言,認識和掌握Gradle最好的方法莫過於比較它們之間的區別了,本文不準備詳細介紹Gradle的方方面面,而是希望通過與Makefile的對比幫助Gradle初學者更快速地理解Gradle的基礎和原理。
Makefile是一種管理和編譯 Linux C/C++ 項目的工具,而Gradle也是一種代碼構建工具,只不過是針對Java語言的,它同樣可以通過一些配置文件和腳本來完成代碼的依賴、第三方庫的引入、編譯的自動化配置等功能。
首先說說Makefile,它是由一個個"規則"組成,每個"規則"都是由"目標"、"依賴"、"命令"構成, 一個最簡單的Makefile如下所示:
1 2 3 4 5 6 7 8 9 |
.PHONY: clean
all: hello
hello: hello.c
gcc -o hello hello.c
clean:
rm hello
|
這裏有三個"目標",分別是: "all","hello","clean"
當執行"make"命令時,編譯器會默認查找目標"all" ,如果沒有"all"則會查找Makefile文件的第一個目標。本示例中有"all"目標,它依賴"hello"目標,因此編譯器會轉而先構建"hello"目標,構建前, 編譯器會先檢測"hello"目標是否需要更新(通過判斷"hello"文件與它所依賴的源文件"hello.c"的修改時間),如果需要,則執行後面的命令,而需要編譯的源文件則是通過手動或者相關函數的方式添加到編譯命令的參數中去的。
這就是makefile最核心的構建思想,通過一個個"目標"、"依賴"、"命令"來完成整個項目的關聯和編譯。那麽,我們也根據這一思想來看看Gradle是怎麽實現的。
1. Gradle是怎麽識別源文件的 ?
Gradle是采取了一種"約定優於配置"的思想來完成這一點的,它通過"Plugin"來約定項目的目標和源文件的布局,例如: Java Plugin 定義的源文件布局如圖所示:
由該圖可以很清楚地看到,Java Plugin 直接約定好了源文件的目錄結構,在Gradle中,一般把這些源碼目錄的配置叫做"SourceSet",同理,Android提供的Android Plugin同樣也定義了自己的源文件布局,還加入了如 jni, AndroidManifest.xml 等目錄和文件。對於Gradle項目而言,需要在每個代碼模塊的"build.gradle"文件中定義使用哪一種Plugin, 例如:
1 2 3 4 5 6 7 8 |
// 普通的Java項目
apply plugin: ‘java‘
//Android Application
apply plugin: ‘com.android.application‘
//Android Library
apply plugin: ‘com.android.library‘
|
2. Gradle是如何識別編譯目標的 ?
在Gradle中,與Makefile的"目標"相對應的概念是"任務",即"task",Gradle的構建過程,就是執行一條一條"task"任務的過程, 同樣,一個"task"也是可以依賴另一個"task"的,這樣,通過這種依賴關系,就可以將整個項目中各個"task"鏈接到一起了。
與Makefile中手寫目標的方式不同,Gradle將所有的"task"的定義全部交給Plugin來完成,由於每一種工程類型(Java項目、Android項目等),其構建過程都是大同小異的,因此為每一種類型的項目定義好了一種通用的Plugin,以後同類型的項目就可以直接使用該Plugin了,非常的靈活和方便。
當我們在項目中執行"gradle build"命令時,可以看到該項目使用的Plugin具體定義了哪些"task",例如一個典型的Java Plugin定義的"task"如下所示:
1 2 3 4 5 6 7 8 9 10 11 |
:compileJava
:processResources
:classes
:jar
:assemble
:compileTestJava
:processTestResources
:testClasses
: test
:check
:build
|
當然,雖然同是Android類型的工程,但不同的項目畢竟還是有配置上的差異,因此,Gradle的Plugin中也可以定義和導出一些特定的"元素"用於傳遞用戶自定義的配置信息,例如: Google提供的 "com.android.application" Plugin 就定義了一個"android"元素,開發者可以在build.gradle中配置該元素的細節,比如定義一些: "項目的ID"、"sdk的版本"、"是否混淆"等等,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
android {
compileSdkVersion 21
buildToolsVersion "21.1.1"
defaultConfig {
applicationId "com.jhuster.test"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0.0"
}
}
|
3. Gradle命令怎麽用 ?
gradle命令與make命令類似,都是用來執行編譯/清理任務的,make命令默認查找當前目錄下的Makefile文件,並且開始構建命令參數中所指定的"目標",例如:
1 2 |
$ make all
$ make clean
|
同樣,gradle命令也是類似的用法,例如:
1 2 3 4 |
$gradle build // 構建和打包整個項目
$gradle clean // 清除之前的構建
$gradle test // 執行測試
$gradle compileJava // 編譯java
|
4. Gradle是如何引入第三方庫的 ?
對於Makefile而言,通常需要將第三方庫的源碼下載下來,編譯為庫後,放入到工程目錄下,然後修改Makefile文件,在gcc/g++的編譯鏈接參數中引用該第三方庫的,例如:
1 2 |
hello: hello.c
gcc -o hello hello.c -L /libs/foo .a
|
而Gradle則是通過build.grade文件中的dependencies來配置的,例如:
1 2 3 4 |
dependencies {
compile files( ‘libs/foo.jar‘ ) // 以jar的方式引用
compile project( ‘:foo‘ ) // 以library工程源碼的方式引用
}
|
另外,Gradle還支持類似Ubuntu軟件源倉庫的方式來引用第三方庫,開發者可以將自己的第三方庫上傳到一些支持Gradle的中心倉庫中,這樣,其他人就可以通過配置build.gradle直接完成對第三方庫的引用了,代碼構建的時候Gradle會自動完成第三方庫的下載和鏈接。比較常用的兩個個中心倉庫: jcenter,mavenCentral。
例如: 我們引用jcenter倉庫的okhttp庫,則build.gradle配置如下:
1 2 3 4 5 6 7 8 9 |
allprojects {
repositories {
jcenter()
}
}
dependencies {
compile ‘com.squareup.okhttp:okhttp:2.4.0‘
}
|
5. 小結
Gradle真的很強大很靈活,它將最核心的構建規都則交給了Plugin來完成,這樣,輕松實現了構建過程的通用性,開發者只需要關註業務邏輯,以及少許的配置即可完成自動化編譯和部署的過程,相比於Linux手寫makefile要方便很多。好了,關於Gradle和Makefile的簡單對比就介紹到這裏了,希望對Gradle的初學者有所幫助,有任何疑問或者建議歡迎留言或者來信[email protected]交流,或者關註我的新浪微博 @盧_俊 獲取最新的文章和資訊。
Gradle與Makefile構建工具的對比