【認識 Gradle】(3)Gradle 起手式
- gradle-series
前二篇介紹在 Gradle 前較廣泛使用的編譯工具 Ant 與 Maven。接下來的課程就會以 Gradle 為主。在正式進入 Gradle 教學的這篇,我們選擇以起手式的角度切入,先不去理解繁複的原理與暫時用不到的設計概念、中心思想,而是要使用編譯工具最重要的就是把專案編譯出來,並且它確實要能動。此篇教學的要點在帶領讀者:安裝、建立第一個 Java 專案、基本指令介紹與實作歷程回顧。
此教學主要用到的資訊儘可能採用 Gradle 官方網站 的資料,它的官網已提供許多有用的資訊,向未接觸 Gradle 的讀者『導讀』這些材料是撰寫這系列教學的重要目標。
安裝 Gradle
Gradle 的安裝很容易,在使用者有安裝 JDK 1.5+ 以上版本,並設定好相關環境變數的前提下,只需由 Gradle 官網下載程式並將執行 script 或 batch file 設到 PATH 路徑內即可使用。進入網官首頁,就能看到很醒目的 Download 按鈕:
撰文時下載的最新穩定版本為 1.8 版 gradle-1.8-all.zip 將它解壓縮後,放在你所希望的安裝路徑,例如放使用者目錄下的 app 資料夾:
/Users/qrtt1/app/gradle-1.8
在 Gradle 的路徑下有這些內容,其中 bin 路徑是必需被加入 PATH 環境變數 的路徑。Gradle 提供的 script 或 batch file 就在這個路徑之下:
qty:gradle-1.8 qrtt1$ ls -alh
total 1832
drwxr-xr-x 13 qrtt1 staff 442B 9 24 09:40 .
drwxr-xr-x 72 qrtt1 staff 2.4K 10 19 12:41 ..
-rw-r--r--@ 1 qrtt1 staff 51K 9 17 02:33 LICENSE
-rw-r--r--@ 1 qrtt1 staff 838B 9 17 02:33 NOTICE
drwxr-xr-x 4 qrtt1 staff 136B 9 24 09:40 bin
-rw-r--r--@ 1 qrtt1 staff 105B 9 17 02:33 changelog.txt
drwxr-xr-x 7 qrtt1 staff 238B 9 24 09:40 docs
-rw-r--r--@ 1 qrtt1 staff 855K 9 24 09:34 getting-started.html
drwxr-xr-x 3 qrtt1 staff 102B 9 17 02:33 init.d
drwxr-xr-x 51 qrtt1 staff 1.7K 9 24 09:40 lib
drwxr-xr-x 11 qrtt1 staff 374B 9 17 02:33 media
drwxr-xr-x 33 qrtt1 staff 1.1K 9 24 09:40 samples
drwxr-xr-x 6 qrtt1 staff 204B 9 24 09:40 src
同時它也有附完整的原始碼在 src 路徑與說明文件,當使用者有興趣深入研究它的實作時,可以在其中尋找到更豐富的訊息。init.d 是放置全域初始化設定檔的目錄,每回 Gradle 啟動時,會先確認這個資料夾是否有需要執行的 Gradle Script。
當你將 Gradle 的 bin 路徑加入 PATH 環境變數後,使用 -v 參數執行能獲得 Gradle 的版本資訊。若無法執行成功,請檢查環境變數的設定以及新的環境變數是否已經生效。
qty:gradle_tutorial qrtt1$ gradle -v
------------------------------------------------------------
Gradle 1.8
------------------------------------------------------------
Build time: 2013-09-24 07:32:33 UTC
Build number: none
Revision: 7970ec3503b4f5767ee1c1c69f8b4186c4763e3d
Groovy: 1.8.6
Ant: Apache Ant(TM) version 1.9.2 compiled on July 8 2013
Ivy: 2.2.0
JVM: 1.6.0_65 (Apple Inc. 20.65-b04-462)
OS: Mac OS X 10.6.8 x86_64
Gradle 提供的版本資訊內容,有它釋出的時間與對應至版本控制系統的 Revision 代號。另外,還有 Groovy、Ant、Ivy 與 JVM 的版本。Gradle 並不是重無到有獨立建構出一套完整的專案編譯工具,它利用 Ant 與 Ivy 這些既有的編譯工具與相依性管理機制來執行主要的編譯工作,並運用 Groovy 客製化一套 Domain Specific Language (DSL),讓使用 Gradle 的開發者能用撰寫 Script 的方式寫出期望的編譯方式。
Groovy 是執行在 JVM 上的 Scripting Language,對於編寫 Gradle Script 用到的部分並不會太高深,有興趣更加深入 Groovy 的寫法的開發者可以參考它的官方文件,目前我們還沒有要深入 Groovy 的種種特點,只要先記得它是 Gradle 的 DSL 即可。
DSL 直譯成中文為『領域特定語言』,它是用在特定領域的,以我們現在的例子它用在 Building Tool 的領域。如同 SQL 也是一種領域特定語言,用在資料庫查詢領域。簡而言之,它們將語言的功能與目的有著特定的意圖,相較於 Java 就沒特定的意圖是屬於泛用型的語言。你也能用 Java 來實作編譯工作,但它並沒有事先定義實作『編譯工作』的常用元件,寫起來就比較原始且麻煩。換言之,用 Gradle 的 DSL 就事先定義了許多常用的元件,開發者可以方便地重複使用它,並加上一些針對專案需要客製的部分即可。
第一個 Gradle 專案
繼續沿用前二個系列的 Hello World,目錄結構如下:
qty:gradle_tutorial qrtt1$ tree HelloWorld
HelloWorld
├── build.gradle
└── src
└── main
├── java
│ └── tw
│ └── com
│ └── codedata
│ └── HelloWorld.java
└── resources
└── log4j.properties
7 directories, 3 files
HelloWorld.java
原始碼與 log4j.properties
設定檔內容如下:
package tw.com.codedata;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class HelloWorld {
static Log logger = LogFactory.getLog(HelloWorld.class);
public static void main(String[] args) {
logger.info("Hello World");
}
}
log4j.rootLogger=info, stdout, R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=codedata.log
log4j.appender.R.Append=true
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t [%d{yy/MM/dd HH:mm:ss:SSS}] %c - %m%n
build.gradle
內容為:
/* 引用 java plugin 獲得編譯 java 專案相關的 task $ */
apply plugin: 'java'
/* 引用 application plugin 獲得執行 java 專案相關的 task $ */
apply plugin:'application'
/* 執行 application plugin 用到的參數 $ */
mainClassName = "tw.com.codedata.HelloWorld"
/* 設定 maven repository server $ */
repositories {
mavenCentral()
}
/* 宣告專案的相依函式庫 $ */
dependencies {
compile group: 'commons-logging', name: 'commons-logging', version: '1.1.1'
compile group: 'log4j', name: 'log4j', version: '1.2.16'
}
使用 gradle 指令執行 run task
qty:HelloWorld qrtt1$ gradle run
:compileJava
:processResources
:classes
:run
INFO [main] (HelloWorld.java:11) - Hello World
BUILD SUCCESSFUL
Total time: 6.027 secs
可以觀察到它依序執行:
- compileJava
- processResources
- classes
- run
這些 task 命名與先前介紹的 Maven Build Phase 相似,不過實際上它採用的是 Ant Target Depdenency 概念,執行某個 task 前,要先執行過一些指定的 task。無論它採用何種實作方式,使用者只要知道目標是執行 run task 即可,該執行的相關 task 都會被執行。
回頭看看 build.gradle
,使用 DSL 來撰寫 Build Script 相較於 Maven 用 XML 來描述專案狀態或覆寫既有設定,它顯得更為簡易。它的『擴充』方式不是依賴在 Build Phase 綁定新的 Plugin Goal,而是採用像 Ant Build File 相依的 Import 機制,使用 apply 指定要新增的 Plug In,理解起來更為直覺。
認識 Gradle 指令
在起手式的最後,讀者可先認識一些基本的指令。先由什麼參數都不加開始,在 Gradle 專案路徑(含有 build.gradle
的路徑)下,執行 gradle:
qty:HelloWorld qrtt1$ gradle
:help
Welcome to Gradle 1.8.
To run a build, run gradle <task> ...
To see a list of available tasks, run gradle tasks
To see a list of command-line options, run gradle --help
BUILD SUCCESSFUL
Total time: 3.948 secs
它提示使用者,gradle 需要指定 task,若你不知道該執行哪個 task 請使用 tasks,執行後可以看到許多的 task,它們以特定的群組為單位集合在一起:
qty:HelloWorld qrtt1$ gradle tasks
:tasks
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
Application tasks
-----------------
distTar - Bundles the project as a JVM application with libs and OS specific scripts.
distZip - Bundles the project as a JVM application with libs and OS specific scripts.
installApp - Installs the project as a JVM application along with libs and OS specific scripts.
run - Runs this project as a JVM application
Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles classes 'main'.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles classes 'test'.
Build Setup tasks
-----------------
setupBuild - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]
Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.
Help tasks
----------
dependencies - Displays all dependencies declared in root project 'HelloWorld'.
dependencyInsight - Displays the insight into a specific dependency in root project 'HelloWorld'.
help - Displays a help message
projects - Displays the sub-projects of root project 'HelloWorld'.
properties - Displays the properties of root project 'HelloWorld'.
tasks - Displays the tasks runnable from root project 'HelloWorld'.
Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.
Rules
-----
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.
Pattern: clean<TaskName>: Cleans the output files of a task.
To see all tasks and more detail, run with --all.
BUILD SUCCESSFUL
Total time: 4.574 secs
除了先前執行過的 run task,我們能試試由 tasks 查詢到的能執行的 task,例如 distZip
qty:HelloWorld qrtt1$ gradle distZip
:compileJava
:processResources
:classes
:jar
:startScripts
:distZip
BUILD SUCCESSFUL
Total time: 6.736 secs
Gradle 產出的檔案通常在 build 目錄下,我在 build/distributions 下找到 zip 檔,它的內容如下:
qty:HelloWorld qrtt1$ unzip -l build/distributions/HelloWorld.zip
Archive: build/distributions/HelloWorld.zip
Length Date Time Name
-------- ---- ---- ----
0 10-20-13 12:01 HelloWorld/
0 10-20-13 12:01 HelloWorld/lib/
1425 10-20-13 12:01 HelloWorld/lib/HelloWorld.jar
60686 10-20-13 10:47 HelloWorld/lib/commons-logging-1.1.1.jar
481535 10-19-13 12:43 HelloWorld/lib/log4j-1.2.16.jar
0 10-20-13 12:01 HelloWorld/bin/
5088 10-20-13 12:01 HelloWorld/bin/HelloWorld
2444 10-20-13 12:01 HelloWorld/bin/HelloWorld.bat
-------- -------
551178 8 files
還能試試顯示相依性的 dependencies:
qty:HelloWorld qrtt1$ gradle dependencies
:dependencies
------------------------------------------------------------
Root project
------------------------------------------------------------
archives - Configuration for archive artifacts.
No dependencies
compile - Compile classpath for source set 'main'.
+--- commons-logging:commons-logging:1.1.1
\--- log4j:log4j:1.2.16
default - Configuration for default artifacts.
+--- commons-logging:commons-logging:1.1.1
\--- log4j:log4j:1.2.16
runtime - Runtime classpath for source set 'main'.
+--- commons-logging:commons-logging:1.1.1
\--- log4j:log4j:1.2.16
testCompile - Compile classpath for source set 'test'.
+--- commons-logging:commons-logging:1.1.1
\--- log4j:log4j:1.2.16
testRuntime - Runtime classpath for source set 'test'.
+--- commons-logging:commons-logging:1.1.1
\--- log4j:log4j:1.2.16
BUILD SUCCESSFUL
Total time: 4.915 secs
dependencies 是個相當長的 task 名稱,Gradle 允許使用者使用縮寫的方式呼叫 task,例如:
gradle d
gradle de
gradle dep
上述的縮寫都會呼叫 dependencies,若是遇到無法判斷時,Gradle 會提供候選名單,例如只輸入 t 時,Gradle 無法確定你要執行 tasks 還是 test:
qty:HelloWorld qrtt1$ gradle t
FAILURE: Could not determine which tasks to execute.
* What went wrong:
Task 't' is ambiguous in root project 'HelloWorld'. Candidates are: 'tasks', 'test'.
* Try:
Run gradle tasks to get a list of available tasks.
BUILD FAILED
Total time: 3.922 secs
你需明確指定要 ta 或是 te,給予足夠的縮寫長度讓它能判斷。
內容回顧
- 在這篇文章中,我們學習使用 Gradle 建立第一個 Java 專案,認識
build.gradle
簡單的寫法。能使用 Gradle 來編譯簡單的 Java 專案。 - java 與 application PlugIn 是你最初認識的 Gradle PlugIn,後續我們會講解其他常用的 PlugIn,並在
build.gradle
內撰寫自己的 task。 - 知道 gradle 指令的使用,會查詢現有的 task,能使用縮寫的功能呼叫名稱很長的 task