Gradle腳本基礎全攻略

【工匠若水 http://blog.csdn.net/yanbober 轉載請註明出處。點我開始Android技術交流

1 背景

在開始Gradle之前請務必保證自己已經初步瞭解了Groovy腳本,特別是閉包規則,如果還不瞭解Groovy則可以先看《Groovy腳本基礎全攻略》這一篇博客速成一下Groovy基礎,然後再看此文即可。關於Gradle速成乾貨基礎詳情也請參考Geadle官方網站,不好意思我太Low了。

這裏寫圖片描述

Gradle核心是基於Groovy的領域特定語言(DSL,具體概念參見《Groovy腳本基礎全攻略》),具有非常好的擴展性,所以不管是簡單的獨立項目還是大型的多項目構建它都能高效的提高構建任務,尤其對多項目支持是非常牛逼的;Gradle還提供了局部構建功能,譬如構建一個單獨子項目時它會構建這個子項目依賴的所有子項目;當然了他對遠程倉庫和本地庫的支持也很到位;哎呀,總之後面你就明白他的牛逼之處了。

既然Gradle核心是Groovy,Groovy本質又是Java,所以很明顯可以發現Gradle環境必須依賴JDK與Groovy庫,具體如下:

  • JDK版本必須是JDK6以上;

  • 因爲Gradle自帶Groovy庫, 所以已安裝的Groovy會被Gradle忽略;

具體Gradle環境配置好了以後如下圖:

這裏寫圖片描述

【工匠若水 http://blog.csdn.net/yanbober 轉載請註明出處。點我開始Android技術交流

2 Gradle DSL基礎

Gradle的實質是配置腳本,執行一種類型的配置腳本時就會創建一個關聯的對象,譬如執行Build script腳本就會創建一個Project對象,這個對象其實就是Gradle的代理對象。下面給出來各種類型Gradle對應的對象類型:

腳本類型 關聯對象類型
Build script Project
Init script Gradle
Settings script Settings


Gradle的三種主要對象解釋如下:

  • Project對象:每個build.gradle會轉換成一個Project對象。

  • Gradle對象:構建初始化時創建,整個構建執行過程中只有這麼一個對象,一般很少去修改這個默認配置腳本。

  • Settings對象:每個settings.gradle會轉換成一個Settings對象。

可以看見,當我們編寫指定類型Gradle腳本時我們可以直接使用關聯對象的屬性和方法;當然了,每個腳本也都實現了Script接口,也就是說我們也可以直接使用Script接口的屬性與方法。

2-1 構建腳本Build script(Project)

在Gradle中每個待編譯的工程都是一個Project(每個工程的build.gradle對應一個Project對象),每個Project在構建的時候都包含一系列Task,這些Task中很多又是Gradle的插件默認支持的。

PS:所謂的我們編寫Gradle腳本,實質大多數時候都是在編寫構建腳本Build script,所以說Project和Script對象的屬性和方法等API非常重要。

每一個Project對象和build.gradle一一對應,一個項目在構建時都具備如下流程:

  1. 爲當前項目創建一個Settings類型的實例。

  2. 如果當前項目存在settings.gradle文件,則通過該文件配置剛纔創建的Settings實例。

  3. 通過Settings實例的配置創建項目層級結構的Project對象實例。

  4. 最後通過上面創建的項目層級結構Project對象實例去執行每個Project對應的build.gradle腳本。

2-2 初始化腳本Init script(Gradle)和設置腳本Settings script(Settings)

Gradle對象:

初始化腳本Init script(Gradle)類似於Gradle的其他類型腳本,這種腳本在構建開始之前運行,主要的用途是爲接下來的Build script做一些準備工作。我們如果需要編寫初始化腳本Init script,則可以把它按規則放置在USER_HOME/.gradle/相關目錄下。譬如:

這裏寫圖片描述

初始化腳本的Gradle對象代表了Gradle的調運,我們可以通過調用Project對象的getGradle()方法獲得Gradle實例對象。

Settings對象:

在對工程進行配置(譬如多項目樹構建)時Settings實例與settings.gradle文件一一對應,它用來進行一些項目設置的配置。這個文件一般放置在工程的根目錄。譬如:

這裏寫圖片描述

2-3 Build生命週期

Gradle的構建腳本生命週期具備三大步,如下:

這裏寫圖片描述

可以看見,生命週期其實和上面構建腳本Build script的執行流程是可以關聯上的。有了這個流程圖我們接下里詳細看下每個過程。

settings.gradle文件:

除了構建腳本文件,Gradle還定義了一個約定名稱的設置文件(默認爲settings.gradle)。該文件在初始化階段被執行,對於多項目構建必須保證在根目錄下有settings.gradle文件,對於單項目構建設置文件是可選的,不過建議還是寫上。

如下是單項目構建的一個例子:

//settings.gradle
println 'This is executed during the initialization phase.'
//build.gradle
println 'This is executed during the configuration phase.'

task configured {
    println 'This is also executed during the configuration phase.'
}

task test << {
    println 'This is executed during the execution phase.'
}

task testBoth {
    doFirst {
      println 'This is executed first during the execution phase.'
    }
    doLast {
      println 'This is executed last during the execution phase.'
    }
    println 'This is executed during the configuration phase as well.'
}

運行構建結果:

> gradle test testBoth
This is executed during the initialization phase.
This is executed during the configuration phase.
This is also executed during the configuration phase.
This is executed during the configuration phase as well.
:test
This is executed during the execution phase.
:testBoth
This is executed first during the execution phase.
This is executed last during the execution phase.

BUILD SUCCESSFUL

Total time: 1 secs

Gradle多項目構建:

多項目構建總是需要指定一個樹根,樹中的每一個節點代表一個項目,每一個Project對象都指定有一個表示在樹中位置的路徑;在設置文件中我們還可以使用一套方法來自定義構建項目樹。

//分層佈局的多項目構建settings.gradle文件
include 'project1', 'project2:child', 'project3:child1'

上面例子中把project的路徑作爲了include方法的參數,譬如上面的’project3:child1’參數就指定了物理路徑的project3/child1(project3/child1是相對於多項目根路徑的相對路徑),這也同時意味着會創建’project3’和’project3:child1’兩個project。

//平面佈局的多項目構建settings.gradle文件
includeFlat 'project3', 'project4'

上面例子中includeFlat方法接受目錄名作爲參數,但是特別注意,這些項目目錄必須是根目錄的兄弟目錄。

當然了,設置文件中創建的多項目樹其實是由項目描述符來描述的,我們可以在設置文件中隨時修改這些描述符。如下:

//settings.gradle
rootProject.name = 'main'
project(':projectA').projectDir = new File(settingsDir, '../my-project-a')
project(':projectA').buildFileName = 'projectA.gradle'

可以看見,如上例子通過描述符更改名稱和項目目錄,並且建立了一個項目的文件。

Gradle構建初始化Initialization:

在初始化階段如果我們在根路徑下直接指明settings.gradle文件和相關配置則構建初始化就會直接按照我們的設置去構建項目,如果我們沒指明settings.gradle文件則Gradle會以一定的規則去尋找settings.gradle文件,然後依據尋找結果的不同去決定如何構建項目。

【工匠若水 http://blog.csdn.net/yanbober 轉載請註明出處。點我開始Android技術交流

3 Gradle構建基礎

通過上一章可以知道,每一個Gradle構建都是由一個或多個project構成,每一個project都是由一個或多個tasks構成,每個task的實質其實是一些更加細化的構建(譬如編譯class、創建jar文件等)。

任務task基礎:

如下例子我們先來直觀感受一下task的概念,具體細節後面會探討:

//創建一個名爲build.gradle的文件
task hello {
    doLast {
        println 'Hello world!'
    }
}

//這是快捷寫法,用<<替換doLast,後面解釋
task hl << {
    println 'Hello world!'
}

//創建upper的task,使用Groovy語言編寫
task upper << {
    String someString = 'mY_nAmE'
    println "Original: " + someString
    println "Upper case: " + someString.toUpperCase()
}

通過如下命令運行構建上面名爲hello的task,具體如下:

xxx@XXX:~/$ gradle hello
:hello
Hello world!

BUILD SUCCESSFUL

Total time: 1.037 secs

可以看見,gradle命令會在當前目錄中查找一個叫build.gradle的構建腳本文件,這個構建腳本定義了一個叫做hello的獨立task,並且添加了一個action,我們執行了這個task就得到了想要的結果。

在這裏再多嘴一句,我們看下task有無action的區別,如下:

//有Action的task
task actionTask << {  
    println 'I am actionTask'  
}  
//無Action的task
task noActionTask {  
    println 'I am noActionTask'  
}  

一定要記住,在上面這個例子中如果task沒有加<<則這個任務在腳本初始化initialization階段(即無論執行啥task都被執行,具體參見上一章的第一個例子)被執行,如果加了<<則在gradle actionTask後才執行。因爲沒有加<<則閉包在task函數返回前會執行,而加了<<則變成調用actionTask.doLast(),所以會等到gradle actionTask時執行。

任務task依賴:

我們通過上面task基礎感受的例子可以發現,一個build.gradle文件中定義多個task互相沒有關係,決定執行的是我們gradle命令後面跟的task名字;那我們要是讓他們之間有依賴關係咋辦呢?如下:

task taskX(dependsOn: 'taskY') << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}

運行結果如下:

xxx@XXX:~/$ gradle taskX
:taskY
taskY
:taskX
taskX

BUILD SUCCESSFUL

Total time: 1.039 secs

動態任務task:

我們還可以在Gradle中使用Groovy來創建動態task,如下:

4.times { counter ->
    task "task$counter" << {
        println "I'm task number $counter"
    }
}

運行結果如下:

xxx@XXX:~/$ gradle task1
:task1
I'm task number 1

BUILD SUCCESSFUL

Total time: 1.397 secs

使用已存在任務task:

我們除過在上面定義任務task時指明依賴以外還可以通過API爲任務加入一個依賴,如下:

4.times { counter ->
    task "task$counter" << {
        println "I'm task number $counter"
    }
}
task0.dependsOn task2, task3

運行結果如下:

xxx@XXX:~/$ gradle task0
:task0
I'm task number 2
I'm task number 3
I'm task number 0

BUILD SUCCESSFUL

Total time: 1.397 secs

或者我們還可以通過API爲任務加入一些新行爲,如下:

task hello << {
    println 'Hello Earth'
}
hello.doFirst {
    println 'Hello Venus'
}
hello.doLast {
    println 'Hello Mars'
}
hello << {
    println 'Hello Jupiter'
}

運行結果如下:

xxx@XXX:~/$ gradle hello
:hello
Hello Venus
Hello Earth
Hello Mars
Hello Jupiter

BUILD SUCCESSFUL

Total time: 1.397 secs

可以發現,doFirst和doLast可以被執行多次,<<操作符實質就是doLast。

任務task短標記:

我們可以通過美元符將一個task作爲另一個task的屬性,如下:

task hello << {
    println 'Hello world!'
}
hello.doLast {
    println "Greetings from the $hello.name task."
}

執行結果如下:

xxx@XXX:~/$ gradle hello
:hello
Hello world!
Greetings from the hello task.

BUILD SUCCESSFUL

Total time: 1.397 secs

可以看見,上面腳本中使用的name其實是任務的默認屬性, 代表當前任務的名稱。

自定義任務task屬性:

我們還可以給任務task加入自定義的屬性,如下例子:

task myTask {
    ext.myProperty = "myValue"
}

task printTaskProperties << {
    println myTask.myProperty
}

執行結果如下:

xxx@XXX:~/$ gradle printTaskProperties
:printTaskProperties
myValue

BUILD SUCCESSFUL

Total time: 1.397 secs

定義默認任務task:

Gradle允許在腳本中定義一個或多個默認任務,如下:

defaultTasks 'clean', 'run'

task clean << {
    println 'Default Cleaning!'
}

task run << {
    println 'Default Running!'
}

task other << {
    println "I'm not a default task!"
}

執行結果如下:

xxx@XXX:~/$ gradle
:clean,run
Default Cleaning!
Default Running!

BUILD SUCCESSFUL

Total time: 1.397 secs

【工匠若水 http://blog.csdn.net/yanbober 轉載請註明出處。點我開始Android技術交流

4 Gradle依賴管理基礎

大多數項目都不是完全獨立的,它們需要依賴其他項目進行編譯等,Gradle允許你告訴它你項目的依賴關係,以便找到這些依賴關係,並在你的構建中維護這些依賴關係,依賴關係可能需要從遠程的Maven等倉庫中下載,也可能是在本地文件系統中,或者是通過多項目構建另一個構建,我們稱這個過程爲依賴解析。

Gradle依賴聲明:

關於依賴聲明不解釋,直接給個例子,如下:

apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    compile group: 'org.hibernate', name: 'hibernate-core', version: '3.6.7.Final'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}

Gradle依賴配置:

在Gradle中依賴可以組合成configurations(配置),一個配置簡單地說就是一系列的依賴,通俗說也就是依賴配置;我們可以使用它們聲明項目的外部依賴,也可以被用來聲明項目的發佈。下面我們給出幾種Java插件中常見的配置,如下:

  • compile
    用來編譯項目源代碼的依賴;

  • runtime
    在運行時被生成的類需要的依賴,默認項,包含編譯時的依賴;

  • testCompile

    編譯測試代碼依賴,默認項,包含生成的類運行所需的依賴和編譯源代碼的依賴;

  • testRuntime

    運行測試所需要的依賴,默認項,包含上面三個依賴;

各種各樣的插件支持許多標準的配置,我們還可以定義自己的配置。

Gradle外部依賴:

我們可以用Gradle聲明許多種依賴,其中有一種是外部依賴(external dependency),它是在當前構建之外的一種依賴,一般存放在遠程(譬如Maven)或本地的倉庫裏。如下是一個外部依賴的例子:

dependencies {
    compile group: 'org.hibernate', name: 'hibernate-core', version: '3.6.7.Final'
}

可以看見,引用一個外部依賴需要用到group、name、version屬性。上面的寫法還有一種簡寫,如下規則:

group:name:version

這是一個簡寫的例子:

dependencies {
    compile 'org.hibernate:hibernate-core:3.6.7.Final'
}

Gradle倉庫:

有了上面的外部依賴,你指定會想Gradle是咋找到那些外部依賴文件的。其實Gradle會在一個倉庫(repository)裏找這些依賴文件,倉庫其實就是很多依賴文件的集合服務器, 他們通過group、name、version進行歸類存儲,好在Gradle可以解析好幾種不同的倉庫形式(譬如Maven等),但是Gradle默認不提前定義任何倉庫,我們必須手動在使用外部依賴之前定義自己的倉庫。

下面是一個使用MavenCentral倉庫的例子:

repositories {
    mavenCentral()
}

這是一個使用遠程Maven倉庫的例子:

repositories {
    maven {
        url "http://repo.mycompany.com/maven2"
    }
}

這是一個使用本地文件系統裏庫的例子:

repositories {
    ivy {
        // URL can refer to a local directory
        url "../local-repo"
    }
}

當然了,一個項目可以有好幾個庫,Gradle會根據依賴定義的順序在各個庫裏尋找它們,在第一個庫裏找到了就不會再在第二個庫裏找它了,否則在第二個庫找。

Gradle發佈artifacts:

依賴配置也可以用來發布文件,我們可以通過在uploadArchives任務里加入倉庫來完成。下面是一個發佈到Maven 庫的例子,Gradle將生成和上傳pom.xml,如下:

apply plugin: 'maven'

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: "file://localhost/tmp/myRepo/")
        }
    }
}

【工匠若水 http://blog.csdn.net/yanbober 轉載請註明出處。點我開始Android技術交流

5 Gradle命令

多任務調用命令:

gradle task1 task2 [...]

排除任務命令:

gradle -x task1 task2 [...]

失敗後繼續執行構建命令:

只要有任務調用失敗Gradle默認就會中斷執行,我們可以使用–continue選項在一次調用中不中斷執行,然後發現所有失敗原因。

簡化任務名命令:

當我們調用某個任務時如果名字太長我們可以採用簡化操作,但是必須保證可以唯一區分出該任務的字符,譬如:

//簡寫
gradle -x t1
//替換
gradle -x task1

選擇執行構建命令:

調用gradle命令默認會構建當前目錄下的build.gradle文件,我們可以使用-b參數選擇其他目錄的構建文件且當使用此參數時settings.gradle將不會生效。如下:

//選擇文件構建subdir/myproject.gradle
task hello << {
    println "using build file '$buildFile.name' in '$buildFile.parentFile.name'."
}

執行過程:

xxx@XXX:~/$ gradle -b subdir/myproject.gradle hello
:hello
using build file 'myproject.gradle' in 'subdir'.

BUILD SUCCESSFUL

Total time: 1.397 secs

此外我們還可以使用-p參數來指定構建的目錄,譬如在多項目構建中可以用-p替代-b參數。如下執行過程:

xxx@XXX:~/$ gradle -p subdir hello
:hello
using build file 'build.gradle' in 'subdir'.

BUILD SUCCESSFUL

Total time: 1.397 secs

獲取構建信息:

  • gradle projects命令:列出子項目名稱列表。
  • gradle tasks命令:列出項目中所有任務。
  • gradle help –task someTask命令:可以顯示指定任務的詳細信息。
  • gradle dependencies命令:列出項目的依賴列表,所有依賴會根據任務區分,以樹型結構展示。

【工匠若水 http://blog.csdn.net/yanbober 轉載請註明出處。點我開始Android技術交流

6 編寫Gradle腳本

Gradle是以Groovy語言爲基礎,基於DSL語法的自動化構建工具,一個構建腳本能夠包含任何Groovy語言元素,每個腳本都是UTF-8編碼的文件。

6-1 Project對象API

前面我們說過,Gradle在構建腳本中定義了一個project,對於構建腳本中每個project其實Gradle都創建了一個 Project類型的對象來關聯,當構建腳本執行時它會去配置所關聯的Project對象;構建腳本中每個被調用的方法和屬性都委託給了當前Project對象。

如下我們看一個使用Project屬性的例子:

println name
println project.name

上面兩個println語句的輸出是一樣的;由於name屬性沒有在當前腳本中定義,所以可以像第一個那樣使用自動委託 ,通常我們使用第二中寫法。

Project對象提供了一些標準的屬性,我們可以在構建腳本中很方便的使用他們,如下:

Name Type Default Value
project Project Project實例對象
name String 項目目錄的名稱
path String 項目的絕對路徑
description String 項目描述
projectDir File 包含構建腳本的目錄
build File projectDir/build
group Object 未具體說明
version Object 未具體說明
ant AntBuilder Ant實例對象

具體關於Project的方法詳情參閱Project的API文檔。這裏我們給出Project的apply方法的一個例子,如下:

//加載一個gradle文件
apply from: rootProject.getRootDir().getAbsolutePath() + "/common.gradle"  

6-2 Script對象API

當Gradle執行一個腳本時它會將這個腳本編譯爲實現了Script的類(在上篇博客《Groovy腳本基礎全攻略》Groovy的本質編譯class代碼那塊有介紹),也就是說所有的屬性和方法都是在Script的接口中聲明。

6-3 Gradle對象API

關於Gradle對象的詳細屬性和API介紹點我即可。這裏直接給出一個使用Gradle對象的例子,如下:

這裏寫圖片描述

6-4 Gradle變量聲明

在Gradle腳本中有兩種類型的變量可以聲明,如下:

  • 局部變量
  • 擴展變量

局部變量使用關鍵字def聲明,它只在聲明的地方可見,如下:

    def dest = "dest"

    task copy(type: Copy) {
          form "source"
          into dest

    }

在Gradle中所有被增強的對象可以擁有自定義屬性(譬如projects、tasks、source sets等),使用ext擴展塊可以一次添加多個屬性。如下:

apply plugin: "java"

ext {
    springVersion = "3.1.0.RELEASE"
    emailNotification = "[email protected]"
}

sourceSets.all { ext.purpose = null }

sourceSets {
    main {
        purpose = "production"
    }
    test {
        purpose = "test"
        }
    plugin {
        purpose = "production"
    }
}

task printProperties << {
    println springVersion
    println emailNotification
    sourceSets.matching { it.purpose == "production" }.each { println it.name}
} 

上面我們用一個ext擴展塊向Project對象添加兩個擴展屬性,當這些擴展屬性被添加後,它們就像預定義的屬性一樣可以被讀寫。

6-5 Gradle中Groovy使用

這個沒啥說的,具體可以參考《Groovy腳本基礎全攻略》這篇博客,裏面有詳細介紹。我們這裏粗略總結回憶一下即可:

  • Groovy會自動將一個屬性的引用轉換爲相應的getter/setter方法。

  • Groovy調用方法時圓括號可有可無。

  • Groovy爲List和Map集合提供了一些操作捷徑,譬如apply plugin:’java’中的plugin:’java’其實就是Groovy中的Map,apply是一個方法,省略了括弧而已。

哎呀,詳細的還是去看前一篇博客吧。

【工匠若水 http://blog.csdn.net/yanbober 轉載請註明出處。點我開始Android技術交流

7 Gradle文件操作基礎

實際使用Gradle過程中大多數時候需要操作文件,好在Gradle給我們提供了一些API來快捷處理。

定位文件:

我們可以使用Project.file()方法來定位一個文件獲取File對象(詳情參考Project的API),如下:

//相對路徑
File configFile = file('src/config.xml')
//絕對路徑
File configFile = file(configFile.absolutePath)
//項目路徑的文件對象 
File configFile = file(new File('src/config.xml'))

可以從Project的API發現file()方法能夠接收任何形式的對象參數,它會將參數值轉換爲一個絕對文件對象,通常我們可以傳一個String或File實例;如果傳的路徑是絕對路徑,則會被直接構造爲一個文件實例,否則會被構造爲項目目錄加上傳遞目錄的文件對象;當然了,file()方法還能識別URL(譬如file:/some/path.xml等)。

文件集合:

文件集合其實是一組文件,Gradle使用FileCollection接口表示文件集合,Gradle API中許多類都實現了這個接口,譬如dependency configurations等。獲取FileCollection實例的一種方法是Project.files(),我們可以傳遞任何數量的對象參數。如下:

FileCollection collection = files('src/file1.txt',
                                  new File('src/file2.txt'),
                                  ['src/file3.txt', 'src/file4.txt'])

使用迭代操作還能將其轉換爲其他的一些類型,同時我們還可以使用+操作將兩個文件集合合併,使用-操作對一個文件集合做減法。如下例子:

// 對文件集合進行迭代
collection.each {File file ->
    println file.name
}

// 轉換文件集合爲其他類型
Set set = collection.files
Set set2 = collection as Set
List list = collection as List
String path = collection.asPath
File file = collection.singleFile
File file2 = collection as File

// 增加和減少文件集合
def union = collection + files('src/file3.txt')
def different = collection - files('src/file3.txt')

我們也可以向files()方法傳遞閉包或者可回調的實例參數,當查詢集合的內容時就會調用它,然後將返回值轉換爲一些文件實例,返回值可以是files()方法支持的任何類型的對象。如下例子:

task list << {
    File srcDir

    // 使用閉合創建一個文件集合
    collection = files { srcDir.listFiles() }

    srcDir = file('src')
    println "Contents of $srcDir.name"
    collection.collect { relativePath(it) }.sort().each { println it }

    srcDir = file('src2')
    println "Contents of $srcDir.name"
    collection.collect { relativePath(it) }.sort().each { println it }
}

文件樹:

文件樹可以代表一個目錄樹結構或一個ZIP壓縮文件的內容,FileTree繼承自FileCollection,所以我們可以像處理文件集合一樣處理文件樹,使用Project.fileTree()方法可以得到FileTree實例,它會創建一個基於基準目錄的對象。如下:

/以一個基準目錄創建一個文件樹
FileTree tree = fileTree(dir: 'src/main')

// 添加包含和排除規則
tree.include '**/*.java'
tree.exclude '**/Abstract*'

// 使用路徑創建一個樹
tree = fileTree('src').include('**/*.java')

// 使用閉合創建一個數
tree = fileTree('src') {
    include '**/*.java'
}

// 使用map創建一個樹
tree = fileTree(dir: 'src', include: '**/*.java')
tree = fileTree(dir: 'src', includes: ['**/*.java', '**/*.xml'])
tree = fileTree(dir: 'src', include: '**/*.java', exclude: '**/*test*/**')

// 遍歷文件樹
tree.each {File file ->
    println file
}

// 過濾文件樹
FileTree filtered = tree.matching {
    include 'org/gradle/api/**'
}

// 合併文件樹A
FileTree sum = tree + fileTree(dir: 'src/test')

// 訪問文件數的元素
tree.visit {element ->
    println "$element.relativePath => $element.file"
}

我們還可以使用ZIP或TAR等壓縮文件的內容作爲文件樹,Project.zipTree()和Project.tarTree()方法可以返回一個FileTree實例。如下:

// 使用路徑創建一個ZIP文件
FileTree zip = zipTree('someFile.zip')

// 使用路徑創建一個TAR文件
FileTree tar = tarTree('someFile.tar')

//TarTree可以根據文件擴展名得到壓縮方式,如果我們想明確的指定壓縮方式則可以如下操作
FileTree someTar = tarTree(resources.gzip('someTar.ext'))

指定輸入文件:

Gradle中有些對象的屬性可以接收一組輸入文件,譬如JavaComplile任務的source屬性(定義編譯的源文件)。如下:

//使用一個File對象設置源目錄
compile {
    source = file('src/main/java')
}

//使用一個字符路徑設置源目錄
compile {
    source = 'src/main/java'
}

//使用一個集合設置多個源目錄
compile {
    source = ['src/main/java', '../shared/java']
}

//使用FileCollection或者FileTree設置源目錄
compile {
    source = fileTree(dir: 'src/main/java').matching {include 'org/gradle/api/**'}
}

//使用閉包設置源目錄
compile {
    source = {
        // Use the contents of each zip file in the src dir
        file('src').listFiles().findAll {it.name.endsWith('.zip')}.collect { zipTree(it) }
    }
}

compile {
    //使用字符路徑添加源目錄
    source 'src/main/java', 'src/main/groovy'
    //使用File對象添加源目錄
    source file('../shared/java')
    //使用閉包添加源目錄
    source { file('src/test/').listFiles() }
}

複製文件:

我們可以使用複製任務(Copy)進行文件複製操作,複製任務擴展性很強,它可以過濾複製文件的內容,使用複製任務要提供想要複製的源文件和一個目標目錄,如果要指定文件被複制時的轉換方式則可以使用複製規則,複製規則是一個CopySpec接口的實現,我們使用CopySpec.from()方法指定源文件,CopySpec.into()方法指定目標目錄即可。如下:

task copyTask(type: Copy) {
    from 'src/main/webapp'
    into 'build/explodedWar'
}

task anotherCopyTask(type: Copy) {
    //複製src/main/webapp目錄下的所有文件
    from 'src/main/webapp'
    //複製一個單獨文件
    from 'src/staging/index.html'
    //複製一個任務輸出的文件
    from copyTask
    //顯式使用任務的outputs屬性複製任務的輸出文件
    from copyTaskWithPatterns.outputs
    //複製一個ZIP壓縮文件的內容
    from zipTree('src/main/assets.zip')
    //指定目標目錄
    into { getDestDir() }
}

task copyTaskWithPatterns(type: Copy) {
    from 'src/main/webapp'
    into 'build/explodedWar'
    include '**/*.html'
    include '**/*.jsp'
    exclude { details -> details.file.name.endsWith('.html') &&
                         details.file.text.contains('staging') }
}

task copyMethod << {
    copy {
        from 'src/main/webapp'
        into 'build/explodedWar'
        include '**/*.html'
        include '**/*.jsp'
    }
}

//在複製時重命名文件
task rename(type: Copy) {
    from 'src/main/webapp'
    into 'build/explodedWar'
    //使用閉包映射文件名
    rename { String fileName ->
        fileName.replace('-staging-', '')
    }
    // 使用正則表達式映射文件名
    rename '(.+)-staging-(.+)', '$1$2'
    rename(/(.+)-staging-(.+)/, '$1$2')
}

文件同步任務:

同步任務(Sync)繼承自複製任務(Copy),當執行時會複製源文件到目標目錄,然後從目標目錄刪除所有非複製文件。如下:

task libs(type: Sync) {
    from configurations.runtime
    into "$buildDir/libs"
}

創建歸檔文件:

使用歸檔任務可以創建Zip、Tar、Jar、War、Ear等歸檔文件,如下:

apply plugin: 'java'

task zip(type: Zip) {
    from 'src/dist'
    into('libs') {
        from configurations.runtime
    }
}

關於文件操作的其他請參考API文檔。

【工匠若水 http://blog.csdn.net/yanbober 轉載請註明出處。點我開始Android技術交流

8 Gradle插件

8-1 Gradle插件概述

插件基礎:

關於Gradle支持的插件可以點我搜索。其實Gradle的核心只是一個空空的框架,所謂的Gradle構建便捷腳本其實都是由插件提供支持的,插件添加了新的任務。在Gradle中一般有兩種類型的插件,如下:

  • 腳本插件
    是額外的構建腳本,它會進一步配置構建,通常會在構建內部使用。腳本插件可以從本地文件系統或遠程獲取,如果從文件系統獲取則是相對於項目目錄,如果是遠程獲取則是由HTTP URL指定。

  • 二進制插件
    是實現了Plugin接口的類,並且採用編程的方式來操縱構建。

插件需要通過Project.apply()方法完成聲明應用,相同的插件可以應用多次。如下例子:

//腳本插件
apply from: 'other.gradle'

//二進制插件
apply plugin: 'java'

插件還可以使用插件ID,插件的id作爲給定插件的唯一標識符,我們可以給插件註冊一個縮寫字符的id。譬如下面例子:

//通過Java插件的id進行引用
apply plugin: JavaPlugin

使用構建腳本塊應用插件:

我們可以向構建腳本中加入插件的類路徑然後再應用插件和使用插件的任務,如下:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:0.4.1"
    }
}

apply plugin: "com.jfrog.bintray"

Gradle插件拓展:

可以看見,Gradle其實是依託於各種插件壯大的,譬如Java插件用來構建Java工程,Android插件用來構建打包Android工程,我們只需要選擇合適的插件即可,插件會爲我們提供豐富的任務用來快捷處理構建,具體詳情參考各插件API即可。

8-2 Gradle的Java插件構建實例

上面說了,插件是Gradle的擴展,它會通過某種方式配置我們的項目(譬如加入一些task);Gradle自帶許多插件,我們也可以編寫自己的插件然後開源.,Java 插件就是這樣的一個插件,該插件已經給項目定義了默認的參數(譬如Java源文件位置),所以通常我們不需要在腳本中加入太多東西。

單個基礎Java項目構建:

//把Java插件加入到項目中,也就是許多預定製的任務被自動加入到了項目裏
apply plugin: 'java'

加入上面插件以後Gradle默認希望能在src/main/java路徑下找到源代碼,在 src/test/java路徑下找到測試代碼,任何src/main/resources路徑的文件都會被包含在JAR文件裏,任何src/test/resources路徑的文件都會被加入到classpath中以運行測試代碼,所有的輸出文件將會被創建在構建目錄裏,JAR文件存放在 build/libs文件夾裏。

加入Java插件後我們可以通過gradle tasks命令來列出項目的所有任務,這樣就可以知道Java插件添加了哪些task。常用的task如下:

  • build task
    當運行gradle build命令時Gradle將會編譯和測試你的代碼,並且創建一個包含類和資源的JAR文件。

  • clean task
    當運行gradle clean命令時Gradle將會刪除build生成的目錄和所有生成的文件。

  • assemble task
    當運行gradle assemble命令時Gradle將會編譯並打包代碼,但是並不運行單元測試。

  • check task
    當運行gradle check命令時Gradle將會編譯並測試你的代碼,其他的插件會加入更多的檢查步驟。

單個具有外部依賴的Java項目構建:

當然了,一個Java項目可能會有許多外部依賴(即調用第三方JAR),爲了在項目裏引用這些 JAR包,我們需要告訴Gradle去哪裏找他們,好在Gradle支持許多倉庫,這些倉庫可以被用來提取或者放置依賴,我們可以很方便的從這些倉庫中取得第三方Jar包。如下:

//加入Maven倉庫
repositories {
    mavenCentral()
}

接着加入一些編譯階段來自於mavenCentral倉庫的依賴,如下:

dependencies {
    //編譯階段
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    //測試編譯階段
    testCompile group: 'junit', name: 'junit', version: '4.+'
}

定製構建項目:

Java插件給項目加入了一些屬性,這些屬性已經被賦予了默認的值且已經夠我們日常使用了,如果我們覺得這些默認屬性不好也可以自己修改。如下:

//定製 MANIFEST.MF 文件
sourceCompatibility = 1.5
version = '1.0'
jar {
    manifest {
        attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
    }
}

默認Java插件加入的任務是常規性的任務,但是我們可以定製任務,譬如我們可以設置一個任務的屬性、在任務中加入行爲、改變任務的依賴、完全重寫一個任務等。如下:

//測試階段加入一個系統屬性
test {
    systemProperties 'property': 'value'
}

關於哪些屬性是可用的問題,我們可以使用gradle properties命令列出項目的所有屬性。

發佈JAR文件:

通常JAR文件需要在某個地方發佈,我們可以通過Gradle方便的進行發佈,譬如下面例子將發佈到一個本地的目錄,如下:

//uploadArchives task
uploadArchives {
    repositories {
       flatDir {
           dirs 'repos'
       }
    }
}

多Java項目構建:

在Gradle中爲了定義一個多項目構建我們需要創建一個設置文件(settings.gradle),設置文件放在源代碼的根目錄,它用來指定要包含哪個項目且名字必須叫做settings.gradle。如下例子:

//多項目工程結構樹:
multiproject/
  api/
  services/webservice/
  shared/
//多項目構建settings.gradle文件
include "shared", "api", "services:webservice", "services:shared"

對於大多數多項目構建有一些配置對所有項目都是通用的,所以我們將在根項目裏定義一個這樣的通用配置(配置注入技術 configuration injection)。 根項目就像一個容器,subprojects方法遍歷這個容器的所有元素並且注入指定的配置。如下:

//多項目構建通用配置
subprojects {
    apply plugin: 'java'
    apply plugin: 'eclipse-wtp'

    repositories {
       mavenCentral()
    }

    dependencies {
        testCompile 'junit:junit:4.11'
    }

    version = '1.0'

    jar {
        manifest.attributes provider: 'gradle'
    }
}

可以看見,上面通用配置把Java插件應用到了每一個子項目中。

我們還可以在同一個構建里加入項目之間的依賴,這樣可以保證他們的先後關係。如下:

//api/build.gradle
dependencies {
    compile project(':shared')
}

至此基礎的Java插件使用就OK了,深入的請自行查看API。

【工匠若水 http://blog.csdn.net/yanbober 轉載請註明出處。點我開始Android技術交流

4 Gradle基礎總結

到此Gradle的基礎知識就完全介紹完了,我們對Gradle的框架也有了一個直觀的認識。其實編寫Gradle無非也就是對類的屬性和方法進行調運操作,至於如何調運操作依據具體插件而異,核心的生命週期和幾個對象實例搞明白基本上就能駕馭Gradle腳本了,其他的無非就是熟練度和API查找。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章