儘管我們可以僅使用單個元件來建立可工作的應用程式,但有時候更廣泛的做法是將應用程式劃分為多個更小的模組。
由於這是一個非常普通的案例,因此每個成熟的構建工具都必須支援這項功能,Gradle也不例外。倘若Gradle專案擁有多於一個元件,我們就將其稱之為多專案構建(multi-project build)。
這篇教程描述瞭如何使用Gradle建立一個多專案構建。
我們先來看一看Gradle構建的一些需求。
擴充套件閱讀:如果你對Gradle不太熟悉,在閱讀這篇教程前你應該先閱讀以下文章。
- 《Gradle入門系列(1):簡介》幫助你安裝Gradle,描述Gradle構建的基本概念,以及如何使用Gradle外掛在構建中新增功能。
- 《Gradle入門系列(2):第一個Java專案》描述如何使用Gradle建立Java專案,並將應用程式打包為可執行jar檔案。
- 《Gradle入門系列(3):依賴管理》描述如何在Gradle專案中管理依賴。
Gradle Build 的需求
我們的示例程式擁有兩個模組:
core
模組包含一些通用的元件,它們能夠被程式的其他模組使用。在我們的例子上,只包含一個類:MessageService
類返回‘Hello World!’字串。該模組只有一個依賴:它包含一個單元測試,使用Junit 4.11。
app
模組包含HelloWorld
類,是程式的開端,它從MessageService
物件中獲取資訊,並將接收到的資訊寫入一個日誌檔案中。該模組擁有兩個依賴:它需要core
模組,還使用Log4j 1.2.17作為日誌庫。
我們的Gradle構建還有其他兩個需求:
- 我們必須要使用Gradle執行程式。
- 我們必須要建立一個可執行的二進位制釋出,而且不能使用所謂的“fat jar”方式。
如果你還不清楚怎樣使用Gradle執行程式,以及建立可執行的二進位制釋出,在閱讀這篇教程前,你應該先閱讀以下文章。
《Gradle入門系列(4):建立二進位制釋出版本》
我們繼續來探討一下如何建立一個多專案構建來滿足我們的需求。
建立一個多專案構建
下一步,我們將建立一個多專案的Gradle構建,包括兩個子專案:app
和 core。
初始階段,先要建立Gradle構建的目錄結構。
建立目錄結構
由於core和app模組都使用Java語言,而且它們都使用Java專案的預設專案佈局,我們根據以下步驟建立正確的目錄結構:
- 建立core模組的根目錄(core),並建立以下子目錄:
src/main/java
目錄包含core模組的原始碼。src/test/java
目錄包含core模組的單元測試。
- 建立app模組的根目錄(app),並建立以下子目錄:
src/main/java
目錄包含app模組的原始碼。src/main/resources
目錄包含app模組的資原始檔。
現在,我們已經建立了所需的目錄,下一步是配置Gradle構建,先對包含在多專案構建中的專案進行配置。
對包含在多專案構建中的專案進行配置
我們可以通過以下步驟,對包含在多專案構建中的專案進行配置:
- 在根專案的根目錄下建立
settings.gradle
檔案,一個多專案Gradle構建必須含有這個檔案,因為它指明瞭那些包含在多專案構建中的專案。 - 確保app和core專案包含在我們的多專案構建中。
我們的settings.gradle
檔案如下:
1 2 |
include 'app' include 'core' |
擴充套件閱讀:
配置 core 專案
我們可以通過以下步驟對core專案進行配置:
- 1. 在core專案的根目錄下建立build.gradle檔案。
- 2. 使用Java外掛建立一個Java專案。
- 3. 確保core專案從Maven2中央倉庫(central Maven2 repository)中獲取依賴。
- 4. 宣告JUnit依賴(版本4.11),並使用testCompile配置項,該配置項表明:core專案在它的單元測試被編譯前,需要JUnit庫。
core專案的build.gradle檔案如下:
1 2 3 4 5 6 7 8 9 |
apply plugin: 'java' repositories { mavenCentral() } dependencies { testCompile 'junit:junit:4.11' } |
擴充套件閱讀:
我們繼續對app專案進行配置。
配置App專案
在配置app專案之前,我們先來快速瀏覽一下對一些特殊依賴的依賴管理,這些依賴都是同一個多專案構建的一部分,我們將其稱之為”專案依賴“。
如果多專案構建擁有專案A和B,同時,專案B的編譯需要專案A,我們可以通過在專案B的build.gradle檔案中新增以下依賴宣告來進行依賴配置。
1 2 3 |
dependencies { compile project(':A') } |
擴充套件閱讀
現在,我們可以按照以下步驟配置app專案:
- 在app專案的根目錄下建立build.gradle檔案。
- 用Java外掛建立一個Java專案。
- 確保app專案從Maven2中央倉庫(central Maven2 repository)中獲取依賴。
- 配置所需的依賴,app專案在編譯時需要兩個依賴:
- Log4j (version 1.2.17)
core
模組
- 建立二進位制釋出版本
app專案的build.gradle檔案如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
apply plugin: 'application' apply plugin: 'java' repositories { mavenCentral() } dependencies { compile 'log4j:log4j:1.2.17' compile project(':core') } mainClassName = 'net.petrikainulainen.gradle.client.HelloWorld' task copyLicense { outputs.file new File("$buildDir/LICENSE") doLast { copy { from "LICENSE" into "$buildDir" } } } applicationDistribution.from(copyLicense) { into "" } |
我們繼續,下面是移除core和app專案的構建指令碼中的重複配置。
移除重複配置
當我們對多專案構建中的子專案進行配置時,我們在core和app專案的構建指令碼中新增了重複的配置。
- 由於兩個專案都是Java專案,因此它們都使用Java外掛。
- 兩個專案都使用Maven2中央倉庫(central Maven2 repository)。
換句話說,兩個構建指令碼都包含以下配置:
1 2 3 4 5 |
apply plugin: 'java' repositories { mavenCentral() } |
讓我們將這項配置轉移到根專案的build.gradle檔案中,在此之前,我們必須先學習一下如何在根專案的build.gradle檔案中配置子專案。
如果我們想要在一個稱為core的子專案中新增配置,那麼就必須在根專案的build.gradle檔案中新增以下片段:
1 2 3 |
project(':core') { //Add core specific configuration here } |
換句話說,如果想要將重複的配置轉移到根專案的構建指令碼中,就必須將以下配置新增到build.gradle檔案中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
project(':app') { apply plugin: 'java' repositories { mavenCentral() } } project(':core') { apply plugin: 'java' repositories { mavenCentral() } } |
不過這種做法在實質上並沒有改變什麼,在構建指令碼中依然還存在重複配置,唯一的區別是重複配置現在轉移到了根專案的build.gradle檔案中。讓我們來消滅這些重複配置。
如果我們想要在根專案的子專案中新增通用的配置,需要將以下片段新增到根專案的build.gradle檔案中:
1 2 3 |
subprojects { //Add common configuration here } |
在根專案的build.gradle檔案中移除了重複配置後,程式碼如下:
1 2 3 4 5 6 7 |
subprojects { apply plugin: 'java' repositories { mavenCentral() } } |
如果我們的配置項是被多專案構建中的所有專案所共享的,那麼需要在根專案的build.gradle檔案中新增以下片段:
1 2 3 |
allprojects { //Add configuration here } |
Additional Reading:
- Gradle User Guide: 57.1 Cross project configuration
- Gradle User Guide: 57.2 Subproject configuration
現在,我們可以從子專案的構建指令碼中移除重複配置,子專案新的構建指令碼如下:
core/build.gradle
檔案如下:
1 2 3 |
dependencies { testCompile 'junit:junit:4.11' } |
app/build.gradle
檔案如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
apply plugin: 'application' dependencies { compile 'log4j:log4j:1.2.17' compile project(':core') } mainClassName = 'net.petrikainulainen.gradle.client.HelloWorld' task copyLicense { outputs.file new File("$buildDir/LICENSE") doLast { copy { from "LICENSE" into "$buildDir" } } } applicationDistribution.from(copyLicense) { into "" } |
現在,我們已經建立了一個多專案Gradle構建,下面讓我們來直觀的感受一下我們做了些什麼。
我們剛剛做了什麼?
在我們的多專案構建的根目錄下執行命令gradle projects
,可以看到如下輸出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
> gradle projects :projects ------------------------------------------------------------ Root project ------------------------------------------------------------ Root project 'multi-project-build' +--- Project ':app' --- Project ':core' To see a list of the tasks of a project, run gradle <project-path>:tasks For example, try running gradle :app:tasks BUILD SUCCESSFUL |
正如我們所看到的,這條命令列出了根專案的子專案(app和core),這意味著我們剛才已經建立了一個多專案Gradle構建,它擁有兩個子專案。
在我們的多專案構建的根目錄下執行命令gradle tasks
,可以看到如下輸出(僅列出相關部分):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
> 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 |
正如我們所看到的,我們可以使用Gradle執行程式,並建立一個可執行的二進位制釋出,它沒有使用所謂的“fat jar”方式。這表明我們已經滿足了本文中要求實現的Gradle構建的所有需求。
我們繼續來看一下,從這篇教程中我們學到了什麼。
總結
這篇教程教會了我們三部分內容:
- 一個多專案構建必須在根專案的根目錄下包含
settings.gradle
檔案,因為它指明瞭那些包含在多專案構建中的專案。 - 如果需要在多專案構建的所有專案中加入公用的配置或行為,我們可以將這項配置加入到根專案的build.gradle檔案中(使用
allprojects
)。 - 如果需要在根專案的子專案中加入公用的配置或行為,我們可以將這項配置加入到根專案的build.gradle檔案中(使用
subprojects
)。
P.S. 你可以從Github上獲取這篇教程的演示程式。