本篇參考Gradle官方文檔筷畦,主要是從Android開發(fā)者的視角继效,介紹在使用Gradle進行構(gòu)建的過程中涉及到的一些基礎(chǔ)概念!不對之處训措,敬請指正伪节。下面直接切入正題!
腳本語言
- Groovy: 動態(tài)語言绩鸣;腳本文件后綴為.gradle怀大,eg:build.gradle
- Kotlin: 靜態(tài)語言;腳本文件后綴為.gradle.kts, eg:build.gradle.kts呀闻;使用kotlin書寫gradle構(gòu)建腳本化借,可以獲得更好的IDE(IDEA、AndroidStudio)支持捡多,自動補全蓖康、編譯檢查、重命名等垒手。
腳本類型
- init.gradle
- settings.gradle
- build.gradle
- 其他腳本
gradle在構(gòu)建時蒜焊,會對腳本文件進行編譯,生成對應(yīng)的腳本對象科贬。如將gradle腳本編譯成一個實現(xiàn)Script
接口的對象泳梆;將build.gradle.kts編譯成KotlinBuildScript
類型對象,將settings.gradle.kotlin編譯成KotlinSettingsScript
類型對象榜掌。因此优妙,在腳本文件中,可以訪問對應(yīng)腳本對象中聲明的屬性與方法憎账。
基本概念
- Settings: 一個Settings對象對應(yīng)settings.gradle腳本套硼,由Gradle在配置階段負責創(chuàng)建該對象,并執(zhí)行settings.gradle腳本來對Settings對象進行配置胞皱。settings.gradle腳本使用
include
聲明此次構(gòu)建包含哪些工程(即包含哪些Project)熟菲,并指定這些工程的路徑看政、名稱等朴恳。 - Project:Gradle中抽象的概念抄罕,一般而言,一個Project與一個需要參與構(gòu)建的軟件工程或模塊相對應(yīng)于颖,如Android工程中的App Module或者Library Module等呆贿。每一個Project對象與一個build.gradle腳本文件關(guān)聯(lián),Gradle在初始化階段會為參與構(gòu)建的每一個模塊創(chuàng)建一個Project對象森渐,并且在配置階段做入,會執(zhí)行對應(yīng)的build.gradle腳本來對與之關(guān)聯(lián)的Project對象進行配置。在build.gradle腳本中通過
project
頂層屬性可以引用到關(guān)聯(lián)的Project對象同衣。 - Task:很明顯竟块,代表構(gòu)建過程中的一個子任務(wù),負責完成構(gòu)建過程中的某些操作
關(guān)于settings.gradle
文件耐齐,有以下幾點需要注意:
- 單項目構(gòu)建該文件可選浪秘,但多項目構(gòu)建這個文件是必須的,因為需要在這個文件中聲明哪些子項目需要參與構(gòu)建埠况,也包括子項目路徑耸携、名稱等
- Gradle允許在任意子項目中進行多項目的構(gòu)建,那Gradle如何決定此次構(gòu)建是多項目還是單項目構(gòu)建呢辕翰?如果該構(gòu)建目錄中存在settings.gradle文件夺衍,那么就依據(jù)該文件來進行構(gòu)建;如果不存在該文件喜命,那么會向上一級目錄查詢文件是否存在(注意:只會向父目錄查詢沟沙,而不是一直向上級目錄遞歸查詢),如果存在就依據(jù)該文件進行構(gòu)建壁榕,否則此次構(gòu)建就是一個單項目的構(gòu)建矛紫。因此,如果需要在多項目的一個工程目錄結(jié)構(gòu)中進行單項目的構(gòu)建护桦,我們可以在目標子項目的根目錄下創(chuàng)建一個settings.gradle文件
構(gòu)建流程
- Initialize Phase:初始化階段
在該階段主要是創(chuàng)建Settings對象含衔,并執(zhí)行settings.gralde腳本配置該對象,決定哪些Project參與構(gòu)建二庵,同時創(chuàng)建這些Project對象贪染。settings.gradle腳本中使用include(projectpath)
或者includeFlat(projectname)
來聲明包含的Project工程,并允許配置這些project的名稱催享、工程路徑等杭隙。下面對一個settings.gradle文件進行說明:
// include two projects, 'foo' and 'foo:bar'
// directories are inferred by replacing ':' with '/'
include 'foo:bar' //(1)
// include one project whose project dir does not match the logical project path
include 'baz' //(2)
project(':baz').name = ‘myBaz’
project(':baz').projectDir = file('foo/baz')
// include many projects whose project dirs do not match the logical project paths
file('subprojects').eachDir { dir ->
include dir.name //(3)
project(":${dir.name}").projectDir = dir
}
//add a project with path :flatProject, name flatProject and project directory $rootDir/../flatProject
includeFlat('flatProject') //(4)
(1)include方法參數(shù)為projectpath,非文件路徑因妙,因此不能包含傳統(tǒng)的目錄分隔符號'/'痰憎,取而代之使用需要使用冒號:分隔(在project path中冒號代表的是root project)票髓;projectpath的最后一個節(jié)點作為project名稱,且默認情況下gradle會將projectpath轉(zhuǎn)為相對于rootProject的路徑铣耘。如foo:bar
projectpath洽沟,會對應(yīng)兩個project,分別為foo
與foo:bar
蜗细,且工程根路徑分別為$rootProjectDir/foo
與$rootProjectDir/foo/bar
裆操。
(2) 添加了一個baz工程,名稱自定義為myBaz
炉媒,默認此工程的根目錄為$rootProjectDir/baz
踪区,但此目錄不存在,gradle允許我們修改此路徑吊骤,這里我們修改為$rootProjectDir/foo/baz
缎岗。
(3) 遍歷subproject目錄下的所有子目錄,添加為一個project白粉,并設(shè)置projectdir传泊;
(4) includeFlat方法也能添加一個project,但查找工程projectDir的策略與include不一致蜗元,它會查找相對于rootProject父目錄下的文件路徑或渤。因此,這里flatProject的工程路徑為$rootProject/../flatProject
奕扣。
Configure Phase:配置階段
在該階段薪鹦,gradle會執(zhí)行build.gradle腳本文件對Project進行配置Execute Phase:執(zhí)行階段
配置階段完成之后,Gradle會構(gòu)建出一張基于task的有向無環(huán)圖惯豆,執(zhí)行階段即負責執(zhí)行這些task
軟件模塊:Module
這里的Module并不是指Android工程中的Module池磁,而是指向一個依賴,是一個可以隨著時間不斷更新的軟件模塊楷兽,如Google Guava庫地熄。每一個Module都有一個名稱module name,隨著時間的推移芯杀,模塊會不斷改善并重新發(fā)布端考,而模塊的每一次發(fā)布都會有一個版本號,使用module version來描述揭厚。Module一般會被組織到Reposity中却特,通過module name以及module version可以精確定位到該模塊。
產(chǎn)物:Artifact
產(chǎn)物特指由一次build生成的一個文件或一個目錄筛圆,如一個Jar裂明、Zip、AAR等太援。產(chǎn)物的生成就是為了給其他用戶或Project使用的闽晦。
伴隨著Module的每一次發(fā)布扳碍,都會有相應(yīng)的產(chǎn)物,稱之為artifact
仙蛉,一個模塊可以產(chǎn)出多個產(chǎn)物笋敞,如.jar包、.aar包等捅儒,而每一個artifact都可以有自己獨立的Transitive Dependencies液样。這些產(chǎn)物都會有對應(yīng)的描述信息,保存在module的metadata文件中(如maven repository中該文件為pom.xml)巧还。在進行Module依賴解析時,Gradle會根據(jù)需要從repository中選擇合適的產(chǎn)物下載使用坊秸,且默認情況下還會自動解析Transitive Dependencies麸祷。
此外,Configuration也可以有相應(yīng)的產(chǎn)物褒搔,這樣在聲明依賴時阶牍,就可以指定具體使用哪個configuration artifact了。如:implementation(project(path = ":api", configuration = "spi"))
- 定義產(chǎn)物
val myJar by tasks.registering(Jar::class)
val someFile = file("$buildDir/somefile.txt")
artifacts {
add("taskArtifact", myJar) //taskArtifact指向myJar
add("fileArtifact", someFile)
}
何以為家星瘾,Module的容器:Repository
通常情況下走孽,依賴是以modules
的方式存在和引用的,在聲明依賴時琳状,我們需要告知Gradle上哪去獲取這些依賴磕瓷。依賴保存的位置或路徑稱之為repository
。 類型主要有:
- Flat directory repository:
flatDir()
- Maven Central repository:
mavenCentral()
- JCenter Maven repository:
jcenter()
- Google Maven repository:
google()
- Local Maven repository:
mavenLocal()
- Custom Maven repositories:
maven {url = uri("http://repo.mycompany.com/maven2")}
在構(gòu)建腳本中念逞,我們需要使用repositories{}
DSL聲明這些repository的位置困食,它可以指向本地或遠程的倉庫。如下所示:
buildscript {
repositories {
google() -- (1)
jcenter() -- (2)
flatDir("name" to "libs", "dirs" to "libs") -- (3)
}
dependencies {
//dependency declaration
}
}
腳本中我們聲明了依賴查找的3個目標倉庫翎承,(1)是google的倉庫硕盹,(2)是jcenter倉庫、(3)則是通過
flatDir
方法傳入本地文件系統(tǒng)的路徑來指定本地倉庫叨咖,這里創(chuàng)建了一個本地reposity瘩例,命名為libs,路徑為$rootProject/libs甸各。(Android工程中經(jīng)常使用flatDir來包含本地的aar依賴包)
在構(gòu)建過程中垛贤,Gradle會在聲明的reposity倉庫中查找定位我們聲明的依賴,從遠程下載或從本地目錄痴晦、倉庫獲取這些依賴的產(chǎn)物用于構(gòu)建南吮,并保存在本地緩存中。這個過程稱為依賴解析誊酌。Gradle根據(jù)聲明的依賴部凑,按Repository聲明的順序露乏,從上到下依次查找。只要找到了一項就返回涂邀,不會繼續(xù)往下查找瘟仿!因此,需要注意Repository聲明的順序比勉。
物以類聚劳较,依賴的組織形式: Configuration
Gradle允許針對不同的構(gòu)建場景聲明不同的依賴集合;如在編譯打包發(fā)布時浩聋,我們不希望把測試用例的代碼观蜗,以及跑測試用例需要依賴到的其他三方庫與代碼一起編譯打包。因此衣洁,我們需要把這兩種使用場景進行劃分墓捻,不同的場景各自聲明自己感興趣的依賴,這樣在不同場景下構(gòu)建時坊夫,可以只解析和使用自己聲明的依賴砖第,避免了代碼的混亂,也有利于代碼的組織管理以及維護环凿。在Gradle中梧兼,Configuration
是一組有名稱的依賴的集合,代表了該依賴組的一個使用場景(或作用域)智听,如implementation configuration
是編譯project所需依賴的集合羽杰,testImplementation configuration
是編譯測試用例所需依賴的集合。Gradle要求每一個依賴都必須顯示的指定其所屬的Configuration瞭稼。
Gradle框架本身以及我們?nèi)粘R玫母黝惒寮紩A(yù)先根據(jù)不同的使用場景定義出不同的configuration供使用忽洛,如Android插件中包含的implementation
、api
环肘、compileOnly
欲虚、runtimeOnly
等,當然我們也可以進行自定義悔雹。
- 自定義Configuration:為某些特殊依賴場景自定義Configuration复哆。
- 繼承Configuration:所有加入到父親中的依賴,兒子都能直接繼承和使用腌零。
- 一個Configuration可以包含一系列的Artifact供其他Project依賴使用梯找!
val bar by configurations.registering //委托屬性創(chuàng)建
configurations {
create("baz") //直接創(chuàng)建
bar.get().extendsFrom(getByName("implementation")) //繼承
providedCompile
compile.extendsFrom providedCompile
}
dependencies {
implementation "group:module:version"
testImplementation "group:module1:version"
bar "group:module2:version"
}
我們在日常開發(fā)中使用較多的
mtl-publish
maven發(fā)布插件,其實也自定義了一個名為providedCompile
的Configuration益涧,并將compile
從其繼承锈锤,如上代碼片段所示。
由于存在以上的繼承關(guān)系,因此在構(gòu)建過程中providedCompile與compile的作用其實是一樣的久免,都能引用到依賴庫aar包中的代碼和資源文件
但在生成pom描述文件時(該文件中收集了該模塊的所有三方依賴)浅辙,會將所有providedCompile的依賴修改為provided依賴!
(注:如果一開始就使用provided阎姥,那么在開發(fā)階段將無法引用到三方依賴庫aar包中的資源记舆,因此在引用依賴庫資源時編譯會失敗)
你依賴我,我依賴它:Transitive Dependency
Module一般會提供額外的元數(shù)據(jù)呼巴,來詳細描述該module的詳細信息(如.module泽腮、.pom、ivy.xml文件)衣赶。如該module在repository中的坐標信息(group诊赊、name、version)屑埋,作者author信息等豪筝。Module之間也可以相互依賴,因此在這些元數(shù)據(jù)中摘能,包含一類特殊的數(shù)據(jù),用于聲明該module依賴的其他module敲街,如JUnit 5 platform module需要依賴platform commons module团搞。這些依賴稱之為傳遞依賴(transitive dependencies)
,默認情況下多艇,Gradle會自動解析和使用該module聲明的其他module依賴逻恐,當然我們也可以針對性的配置這些依賴的解析規(guī)則。
依賴類型
-
Module Dependency:最常用的依賴類型峻黍,指向一個reposity中的目標模塊复隆,通過group、name姆涩、version來定位挽拂。依賴聲明方式如下:
configurationname(dependencyNotion, dependencyConfiguration)
。configurationname指定依賴所屬的Configuration(Gradle要求每個依賴都必須顯示指定其宿主Configuration)骨饿、dependencyNotion是依賴的聲明亏栈,如字符串形式或map形式,格式一般為{group}:{name}:{version}[{:classifier}@{extension}]
宏赘。classifier代表同一個module的不同Artifact變種绒北,extension指定Artifact的擴展名。最后還可以傳入一個dependencyConfiguration配置Action察署,用于配置該依賴的一些屬性闷游,如change屬性或transitive屬性、force屬性及exclude規(guī)則等等,詳情可查閱該Action的運行上下文ExternalModuleDependency
提供的接口脐往。栗子:
dependencies {
runtimeOnly(group: 'org.springframework', name: 'spring- core', version:'2.5') {
because("demenstrate the reason we pick this version")
isTransitive = true //是否解析傳遞依賴休吠,默認為true
isChanging = true //是否為可變版本,過期后會重新獲取
isForce = false //當有依賴沖突時钙勃,是否強制使用該版本依賴
exclude(group="name", name="name") //解析該依賴的transitive dependency時蛛碌,不解析被exclude的部分
}
}
-
File Dependency:本地文件依賴∠皆矗可以直接依賴本地的jar蔚携、aar包等,而不需要上傳至repository中克饶。文件依賴使用
FileCollection
來指定酝蜒。如下:
dependencies {
//files構(gòu)建一個FileCollection對象,包含了相對于當前腳本目錄的 libs/a.jar和libs/b.jar兩個依賴文件
runtimeOnly(files("libs/a.jar", "libs/b.jar"))
//通過libs路徑構(gòu)建一個目錄樹對象FileTree矾湃,該類繼承于FileCollection亡脑,通過include匹配該目錄下的所有.jar文件作為依賴
runtimeOnly(fileTree("libs") { include("*.jar") })
implementation files("$buildDir/classes") {
//通過builtBy指定文件依賴的產(chǎn)物是由compile這個task生成的
//因此,會優(yōu)先執(zhí)行該task生成文件依賴
builtBy 'compile'
}
}
-
Project Dependency:本地工程依賴邀跃。通過
project(projectPath)
方法來引用一個本地工程霉咨,projectPath參數(shù)為工程路徑。如project(":A:A1:A2")
拍屑。
依賴聲明
- 直接指定具體的版本號進行依賴
dependencies {
implementation 'group:name:5.0.2.RELEASE'
}
- 不指定版本途戒,而是通過Dependency Constraint來統(tǒng)一限定版本,可以指定在腳本中聲明過的依賴版本,也可以指定Transitive Dependency依賴的版本僵驰。這種方式允許我們在一個地方集中進行聲明
dependencies {
implementation 'group:name'
}
dependencies {
constraints {
implementation 'group:name:5.0.2.RELEASE'
}
}
- 動態(tài)版本依賴:解析至符合要求的最新的版本進行依賴
dependencies {
implementation 'group:name:5.+'
}
- Rich version declaration: 使用strictly喷斋、require、prefer蒜茴、reject規(guī)則進行聲明星爪。很少使用
dependencies {
implementation("org.slf4j:slf4j-api") {
version {
strictly("[1.7, 1.8[")
prefer("1.7.25")
}
}
}
- 使用
classifier
與@
符號指定Artifact的種類與擴展名:一般而言,在進行依賴解析時粉私,Gradle默認查找的依賴產(chǎn)物是JAR包顽腾,如果無法獲取將解析失敗。當JAR包類型的Artifact不存在(如為ZIP包毡鉴、AAR包等)崔泵,或者存在多個類型不同的Artifact、亦或是我們不想解析Transitive Dependency時猪瞬,可以使用@符號指定依賴產(chǎn)物的擴展名憎瘸。同一個模塊也可能會輸出不同變種的Artifact,比如經(jīng)過混淆或者未經(jīng)過混淆的陈瘦,可以使用classifier進行聲明幌甘。
dependencies {
//min指定變種為已混淆;@js指定擴展名
js 'jquery:jquery:3.2.1:min@js'
}
- 在聲明依賴時,可以額外的傳遞一個依賴配置lambda表達式Action锅风,該Action在
ExternalModuleDependency
的上下文中執(zhí)行酥诽,可以通過調(diào)用該類中的接口對依賴進行更細粒度的配置;具體查閱API文檔皱埠。
依賴解析
Gradle允許我們自定義依賴解析的規(guī)則肮帐,可以幫助我們解決依賴沖突。說明如下:
- Dependency Resolve Rules:依賴解析規(guī)則
dependencies {
//version scheme
implementation("group:name:default")
}
configurations.all { //this: Configuration
resolutionStrategy.eachDependency { //this: DependencyResolveDetails
//1. change dependency version
if (requested.group == "group") {
useVersion("1.2")
because("why")
}
//2.deal with version scheme
if (requested.group == "group" && requested.version == "default") {
useVersion("1.1")
because("why")
}
//3.change group/name
if (requested.group == "group") {
useTarget("group2:name2:1.1")
because("why")
}
}
}
- Dependency Substitution Rules: 依賴替換規(guī)則
configurations.all { //this: Configuration
resolutionStrategy.dependencySubstitution { //this: DependencySubstitutions
//1. substitute module dependency with project dependency
substitute(module("org.utils:api")).apply {
with(project(":projectA"))
because("tell me why")
}
//2. substitute project dependency with module dependency
substitute(project(":projectC")).apply {
with(module("org.utils:api:1.3"))
because("tell me why")
}
}
}
- Changing configuration dependencies prior to resolution:依賴配置
configurations {
implementation {
withDependencies {//this:DependencySet
val dep = find { it.name == "to-modify" } as ExternalModuleDependency
dep.version {
strictly("1.2")
}
dep.isChanging = false
dep.isTransitive = true
dep.exclude("group", "module")
}
}
}
依賴檢查與構(gòu)建檢查
通過不同方式边器,可以輸出Project的依賴列表训枢、依賴關(guān)系以及依賴沖突如何解決,以及最終所解析至的依賴版本忘巧。
- Build時使用
--scan
生成詳細的報告恒界,可以查看詳細的各個階段的耗時、task運行情況砚嘴、依賴解析情況等十酣。 - 運行插件提供的task輸出依賴關(guān)系,如Android插件提供的
androidDependencies
Task际长。 - 運行Gradle提供的
dependencies
Task耸采,通過--configuration選項指定要輸出哪個Configuration的依賴關(guān)系
./gradlew dependencies --configuration implementation
- 運行Gradle提供的
dependencyInsight
Task,可以跟蹤某一個自定義Configuration下某個具體的依賴工育。追蹤依賴的引用鏈洋幻,以及依賴最終解析的結(jié)果和原因(為什么解析到這個版本);
configurations {
create("scm")
}
dependencies {
"scm"("junit:junit:4.12")
}
./gradlew dependencyInsight --configuration scm --dependency junit:junit
如何解決依賴沖突
- 檢查依賴沖突翅娶。當出現(xiàn)依賴沖突時,Gradle可能會幫我們解決好唯,但結(jié)果可能不是我們想要的竭沫。這里可以使用failOnVersionConflict(),當出現(xiàn)沖突時骑篙,構(gòu)建直接失敗蜕提,需要手動解決
- 在一些情況下,我們可能會同時依賴倉庫中的版本以及依賴本地的Project工程靶端,我們想使用本地工程谎势,方便修改和調(diào)試!這種情況下可以通過配置preferProjectModules()杨名,在出現(xiàn)上述沖突時脏榆,選擇編譯本地工程版本(前提是Project的name需要跟modulename一致)
configurations.all {
resolutionStrategy {
failOnVersionConflict()
preferProjectModules()
}
}
考慮如下場景:A 模塊依賴 B1 和 C, D 模塊依賴了 B2台谍,其中 B1 和 B1 是同一個Module B的兩個不同版本须喂;同時,工程中我們同時依賴了 A 和 D。這種情況下坞生,會存在針對Module B的依賴沖突仔役!默認情況下Gradle會嘗試幫我們解決依賴沖突,解決的方式是使用最新的版本是己;這里的最新不是判斷版本號大小又兵,應(yīng)該是根據(jù)發(fā)布時間來決定!現(xiàn)在我們自己手動進行依賴沖突的解決:
- 這里是由于 A 或者 D 的Transitive Dependency引發(fā)了沖突卒废,那么可以配置 transitive 屬性為false沛厨,即不進行傳遞依賴的解析,這種情況可能需要自己手動添加部分依賴升熊。
(1) 方案一:針對 A 或 D 配置 transitive俄烁。這里針對A配置,不解析A模塊的傳遞依賴级野,因此依賴中不再包含 B1 和 C页屠,這里需要手動添加依賴 C
dependencies {
implementation A {
transitive = false
}
implementation C
implementation D {
//transitive = false
}
}
(2) 方案二:針對 A 或 D 配置 exclude規(guī)則
dependencies {
implementation A {
exclude B1
}
implementation D {
//exclude B2
}
}
- 使用force強制依賴某個版本,如強制依賴 B1 或者 B2
configurations.all {
resolutionStrategy {
force B1
// force B2
}
}
- 根據(jù)上一節(jié)蓖柔,配置依賴解析規(guī)則辰企。進行版本聲明、依賴替換等况鸣。不再贅述
啟用插件
- Script Plugin:指包含額外構(gòu)建邏輯的一個腳本文件牢贸,可以是一個本地文件系統(tǒng)的腳本文件或一個遠程http地址指向的腳本文件,通過apply方法指定from(腳本文件的路徑或遠程地址)來啟用镐捧,引入后Gradle自動解析并執(zhí)行該腳本文件潜索;
//groovy dsl
apply from: 'other.gradle'
apply from: 'http://www.example.com/other.gradle'
//kotlin dsl
apply(from="other.gradle.kts")
apply(from="http://www.example.com/other.gradle.kts")
- Binary Plugin: 通常為一個類或一個Jar包,插件類需要實現(xiàn)gradle提供的
Plugin
接口懂酱,當插件啟用時竹习,其實是調(diào)用了Plugin.apply(org.gradle.Project)
方法。每一個插件對應(yīng)一個唯一ID列牺,通過plugin id來啟用插件整陌,如java, com.android.application, com.android.libary;插件實現(xiàn)可以直接定義在腳本文件中的一個實現(xiàn)類,或從遠程倉庫repositories中拉取瞎领。如以下代碼泌辫,我們從google倉庫中拉取了android插件并啟用:
//rootProject: build.gradle.kts
buildscript {
repositories {
google() // (1)
}
dependencies {
classpath ‘com.android.tools.build:gradle:3.4.0’ //(2)
}
}
//appProject: app/build.gradle.kts
plugins {
id("com.android.application") //(3)
}
//apply(plugin="com.android.application")
在主工程目錄下的build.gradle.kts文件中,一般都會包含如上的片段代碼:使用buildscript{}塊來聲明腳本運行時的依賴九默。repositories{}塊聲明工程中依賴查找的倉庫震放,這里聲明了google()倉庫,腳本中使用的依賴都將首先嘗試從該reposity中進行解析獲然缥鳌澜搅;在(2)中又聲明了classpath依賴伍俘,指向了com.android.tools.build:gradle:3.4.0。意思就是將這個依賴包(從google倉庫解析并下載jar包)添加到腳本文件編譯執(zhí)行的classpath中去勉躺。這樣一來癌瘾,在腳本文件中我們就可以import以及使用這個依賴包(jar包)中包含的相關(guān)類和方法、屬性了饵溅。比如在(3)的位置妨退,我們啟用了com.android.application這個插件,這個插件的代碼其實就是包含在com.android.tools.build:gradle:3.4.0這個依賴聲明中的蜕企。
project and task path
- project path: 以一個可選的冒號開頭(代表RootProject咬荷,而不能以projectname來指定),其余的部分是以冒號分隔的project names轻掩,后一個project是前一個project的子project幸乒;如:
:projectA:projectB
包含rootProject,projectA和projectB唇牧,A和B都是root的子工程罕扎,A是第一級兒子工程,B是第二級丐重;同時B也是A的兒子工程腔召。 - task path: ${projectPath:taskName}
apply方法解析
apply方法的作用主要有:
- 啟動某個插件
- 應(yīng)用某個腳本文件到某個目標對象(委托對象)。腳本文件可以為本地路徑或遠程http協(xié)議的gradle腳本扮惦,由Gradle負責下載解析
如下面的示例:
task configure {
doLast {
def pos = new java.text.FieldPosition(10)
// Apply the script
apply from: 'other.gradle', to: pos
println pos.beginIndex
println pos.endIndex
}
}
// Set properties.
beginIndex = 1
endIndex = 5
在apply from: 'other.gradle', to: pos
語句中臀蛛,apply允許我們傳遞一個map對象。在這個map中崖蜜,我們使用from這個key聲明被應(yīng)用的原始腳本文件為other.gradle浊仆,使用to這個key聲明委托對象為pos。這樣一來在other.gradle中的方法或?qū)傩远紝⒁詐os這個類型為FieldPosition的對象作為委托對象豫领,腳本中訪問的未聲明方法和屬性會在delegate對象中尋找氧卧,如beginIndex和endIndex其實是訪問了委托對象的屬性。該Feature在KotlinDSL中尚未得到支持氏堤。
SourceSet
Gradle Java Support引入了SourceSet
的概念來構(gòu)建基于源代碼的Project,因為我們一般都是以類型來對源代碼以及資源文件進行分類和組織搏明,如應(yīng)用代碼鼠锈、單元測試代碼、集成測試代碼星著,它們通常被分開并放在不同的目錄下购笆。每一個邏輯分組可以有自己的一組源代碼文件依賴、classpath虚循。SourceSet通常涉及以下幾個和編譯相關(guān)的方面:
- 源代碼文件所在的路徑
- 編譯所需的其他classpath或者一系列依賴(在Dependency塊中通過
{sourceSet}Configuration
引入依賴同欠,如testCompileOnly样傍、testImplementation
)。其中sourceSet為名稱(如test、androidTest
),而Configuration為依賴類型(如compileOnly化撕、implementation
)福侈。 - 編譯結(jié)果輸出的目錄
Gradle會自動為定義的每一個SourceSet生成一些編譯Task以及Dependency Configuration。如compile<SourceSet>Java 颜屠、process<SourceSet>Resources、<SourceSet>Implementation等,除了main
這個SourceSet外蚊荣。main是一個特殊的SourceSet,用于工程的主生產(chǎn)代碼(used for the project’s production code)莫杈。
The
main
source set
Most language plugins, Java included, automatically create a source set called main,
which is used for the project’s production code. This source set is special in that its
name is not included in the names of the configurations and tasks, hence why you
have just a compileJava
task and compileOnly
and implementation
configurations
rather than compileMainJava
, mainCompileOnly
and mainImplementation
respectively互例。
除了main
這個SourceSet外,一般插件還會生成其他的SourceSet,如Android的插件還提供了test
及androidTest
筝闹,分別用于跑測試用例和android的測試用例媳叨。我們可以配置sourceSet配置額外的代碼、資源路徑丁存、exclude規(guī)則等
sourceSets {
main {
java {
srcDir 'thirdParty/src/main/java'
}
}
}
SourceSet Properties
-
name — (RO) String
The name of the source set, used to identify it. -
output — (RO) SourceSetOutput
The output files of the source set, containing its compiled classes and resources. -
output.classesDirs — FileCollection
Default value: name, e.g. build/classes/java/main
The directories to generate the classes of this source set into. May contain directories for other
JVM languages, e.g. build/classes/kotlin/main. -
output.resourcesDir — File
Default value: name, e.g. build/resources/main
The directory to generate the resources of this source set into. -
compileClasspath — FileCollection
Default value: ${name}CompileClasspath configuration.The classpath to use whencompiling the source files of this source set. -
java — (read-only) SourceDirectorySet
The Java source files of this source set. Contains only .java files found in the Java source
directories, and excludes all other files -
java.srcDirs — Set<File>
Default value: src/$name/java, e.g. src/main/java
The source directories containing the Java source files of this source set -
java.outputDir — File
Default value: name, e.g. build/classes/java/main
The directory to generate compiled Java sources into -
resources — SourceDirectorySet
The resources of this source set. Contains only resources, and excludes any .java files found in the resource directories. -
resources.srcDirs — Set<File>
Default value: [src/$name/resources]
The directories containing the resources of this source set. -
allJava — SourceDirectorySet
Default value: Same as java property
All Java files of this source set. Some plugins, such as the Groovy Plugin, add additional Java
source files to this collection -
allSource — SourceDirectorySet
Default value: Sum of everything in the resources and java properties
All source files of this source set of any language. This includes all resource files and all Java source files. Some plugins, such as the Groovy Plugin, add additional source files to this
collection
configuration與artifacts
subprojects {
apply(plugin = "java")
group = "org.gradle.sample"
version = "1.0"
}
project(":api") {
configurations {
create("spi") -----------(1)
}
dependencies {
"implementation"(project(":shared"))
}
tasks.register<Jar>("spiJar") {
archiveBaseName.set("api-spi")
from(project.the<SourceSetContainer>()["main"].output)
include("org/gradle/sample/api/**")
}
artifacts {
add("spi", tasks["spiJar"]) -------(2)
}
}
project(":services:personService") {
dependencies {
"implementation"(project(":shared"))
"implementation"(project(path = ":api", configuration = "spi")) ---------(3)
"testImplementation"("junit:junit:4.12")
"testImplementation"(project(":api"))
}
}
關(guān)于FlatDir
flatDir可以指定一個本地的目錄作為一個備選的倉庫肩杈,Gradle在搜索該倉庫,查找對應(yīng)的Module解寝;在下面的示例中扩然,我們指定aars目錄作為一個本地倉庫,其中包含名為'moduleA.aar'文件以及'moduleB.aar'文件聋伦。在(1)的依賴聲明中夫偶,我們指定依賴的name為moduleA、擴展名為'aar', Gradle最終會解析到aars目錄下的'moduleA.aar'文件觉增。在(2)的依賴聲明中兵拢,我們使用常規(guī)的'group:name:version@ext'聲明,最終Gradle也會解析到aars目錄下的'moduleB.aar'文件逾礁。因為说铃,使用flatDir指定本地倉庫時,Gradle會忽略group或者version部分嘹履,只要aars目錄中存在'moduleB.aar'或者'moduleB-1.0.0.aar'這樣的文件腻扇,Gradle都會成功解析到該依賴。
repositories {
flatDir {
dirs 'aars'
}
}
dependencies {
implementation(name: 'moduleA', ext: 'aar') // (1)
implementation('groupname.fake:moduleB:1.0.0') //(2)
}