Gradle 實戰

官方文檔

文章涉及到的 demo 代碼:github.com/linkaipeng/…html

實現 Task 的多種方式

Build script 直接定義

task 直接寫在 build.gradle.kts 文件裏面 如:java

tasks.register("buildScriptTask") {
    doLast {
        println("hello buildScript~")
    }
}
複製代碼

執行:./gradlew -q buildScriptTaskandroid

-q : quiet. Log errors only.git

定義在獨立項目

build script 中直接定義的方式,做用範圍僅僅是在當前的 build.gradle.kts 文件中,那麼若是有的 task 但願被多個構建腳本所應用,那麼能夠將其放在獨立的項目中,再由 build.gradle.kts 引用。github

一、繼承 DefaultTask,使用註解 @TaskAction 指定任務執行的方法api

open class MyHelloTask : DefaultTask() {

    @TaskAction
    fun hello() {
        println("hello MyHelloTask task ~")
    }
}
複製代碼

二、須要使用到的項目進行依賴,(後面 plugin 會講到,這裏先不講)bash

三、項目 build.gradle.kts 中引用app

import me.linkaipeng.task.MyHelloTask

...

tasks.register<MyHelloTask>("myHelloTask")
複製代碼

執行 ./gradlew -q myHelloTaskmaven

Task 獲取參數

配置文件 gradle.properties 獲取

gradle.properties 中加入ide

me.linkaipeng.propertie.hi=hi hi
複製代碼

獲取代碼

val propFromFile = project.properties["me.linkaipeng.propertie.hi"]
println("propFromFile = $propFromFile")
複製代碼

命令行獲取

在執行任務的命令行後面加上 -Pxxx,好比

./gradlew -q readPropertiesTask -Phi2=tete
複製代碼

那麼就輸入了一個名爲 hi2 的參數;

若是要輸入多個參數,那麼

./gradlew -q readPropertiesTask -Phi2=tete -Phi3=rr
複製代碼

Task 執行順序控制

dependsOn

task("developTask") {
    group = "software"
    description = "develop software."
    doLast {
        println("develop")
    }
}

task("compileTask") {
    group = "software"
    dependsOn("developTask")
    doLast {
        println("compile")
    }
}
複製代碼

好比,編譯任務要依賴開發任務,那麼能夠用 dependsOn("developTask") 指定依賴關係;

執行 ./gradlew -q compileTask

結果:

develop
compile
複製代碼

mustRunAfter

task("developTask") {
    group = "software"
    description = "develop software."
    doLast {
        println("develop")
    }
}

task("compileTask") {
    group = "software"
    mustRunAfter("developTask")
    doLast {
        println("compile")
    }
}

task("testingTask") {
    group = "software"
    dependsOn("developTask")
    dependsOn("compileTask")
    doLast {
        println("testing")
    }
}
複製代碼

好比,執行測試任務的時候,須要依賴到開發和編譯任務,可是必需要求開發任務在編譯任務以前,那麼能夠經過mustRunAfter("developTask")指定

執行 ./gradlew -q testingTask

假如沒有指定的話,那麼會按照字母順序執行,結果爲:

compile
develop
testing
複製代碼

有指定編譯任務必須在開發以後執行,那麼結果則爲:

develop
compile
testing
複製代碼

finalizedBy

指定任務執行以後,最後執行的任務,好比,測試任務完成後進行打包操做

如今增長打包任務

task("packagingTask") {
    group = "software"
    doLast {
        println("packaging")
    }
}
複製代碼

而後在測試任務中指定

task("testingTask") {
    group = "software"
    dependsOn("developTask")
    dependsOn("compileTask")
    finalizedBy("packagingTask")
    doLast {
        println("testing")
    }
}
複製代碼

執行 ./gradlew -q testingTask

結果:

develop
compile
testing
packaging
複製代碼

shouldRunAfter

相似 mustRunAfter ,優先級 mustRunAfter 更高

跳過任務(Skip Task)

顧名思義,就是條件性的不執行某個任務

onlyIf

相似條件語句,只有知足當前條件,則執行

val skipDemoTask by tasks.registering {
    doLast {
        println("skipDemoTask ~")
    }
}

skipDemoTask {
    onlyIf { !project.hasProperty("skip") }
}
複製代碼

不跳過,執行 ./gradlew -q skipDemoTask

當檢測到參數 skip,則跳過

跳過,執行 ./gradlew -q skipDemoTask -Pskip

StopExecutionException

藉助異常中止執行

val exceptionDemoTask by tasks.registering {
    doLast {
        if (project.hasProperty("exception")) {
            throw StopExecutionException()
        }
        println("exceptionDemoTask ~")
    }
}
複製代碼

./gradlew -q exceptionDemoTask -Pexception

enabled 值

經過控制 task 的 enabled,達到跳過效果,false 的時候,不執行

val enableDemoTask by tasks.registering {
    doLast {
        println("enableDemoTask ~")
    }
}
enableDemoTask {
    enabled = false
}
複製代碼

執行 ./gradlew -q enableDemoTask

實戰:自定義參數 + buildConfigField 實現動態更改環境

能夠在構建命令後面使用 -P 傳入命令

傳入參數:

./gradlew -q testTask -Phost=test.test.com
複製代碼

獲取參數:

project.properties.keys.map {
    if("host" == it) {
        println("propertie key = $it, value = ${project.properties[it]}")
    }
}
複製代碼

Gradle Plugin

官方文檔: docs.gradle.org/current/use…

實現 Plugin 的三種方式

Build script

插件直接寫在 build.gradle.kts 文件裏面 如:

class TestPlugin: Plugin<Project> {

    override fun apply(target: Project) {
        target.task("testTask") {
            doLast {
                print("This is testTask in TestPlugin.")
            }
        }
    }
}

apply<TestPlugin>()
複製代碼

執行:./gradlew -q testTask

buildSrc 項目

buildSrc 以獨立 module 存在,位置和目錄結構以下:

.
├── build.gradle.kts
├── buildSrc
│   ├── build.gradle.kts
│   └── src
│       └── main
│           └── kotlin
└── settings.gradle.kts
複製代碼
  • buildSrc/build.gradle.kts

在 build.gradle.kts 中聲明插件的 name、id 等信息

gradlePlugin {
    plugins {
        create("PrintMethodNamePlugin") {
            id = "print-method-plugin"
            implementationClass = "com.test.plugin.PrintMethodNamePlugin"
        }
    }
}
複製代碼

這樣項目中就可使用:

plugins {
    id("com.android.application")
    ...
    id("print-method-plugin")
}
複製代碼

執行:./gradlew transformClassesWithPrintMethodNameTransformForDebug

獨立項目

costTimePlugin
.
├── build.gradle.kts
├── buildSrc
│   ├── build.gradle.kts
│   └── src
│       └── main
│           └── kotlin
│           └── resources
│                └── META-INF.gradle-plugins
│                     └── me.linkaipeng.cost-time.properties
└── settings.gradle.kts
複製代碼
  • resources/META-INF.gradle-plugins/me.linkaipeng.cost-time.properties

必須存在,是爲了聲明插件讓項目識別到,內容以下:

implementation-class=me.linkaipeng.plugin.CostTimeStandAlongPlugin
複製代碼
  • 傳到本地倉庫/遠程倉庫

build.gradle.kts 文件聲明(詳細見源代碼):

plugins {
    ...
    `maven-publish`
}

group = "me.linkaipeng.plugin"
version = "1.0.3"

... 

publishing {
    repositories {
        maven {
            url = uri("../repo")
        }
    }
    publications {
        register("mavenJava", MavenPublication::class) {
            from(components["java"])
            artifact(sourcesJar.get())
        }
    }
}

複製代碼

工程 build.gradle.kts 配置本地 repo 路徑和依賴:

repositories {
    maven {
        url = uri("file:///Users/linkaipeng/Documents/demos/GradleKotlinDemo/repo/")
    }
}

dependencies {
    ...
    classpath("me.linkaipeng.plugin:costTimePlugin:1.0.3")
}

複製代碼

這樣項目中就可使用:

plugins {
    id("com.android.application")
    ...
    id("me.linkaipeng.cost-time")
}
複製代碼

id 便是 剛剛定義的 properties 文件的文件名

執行:./gradlew transformClassesWithCostTimeStandAlongTransformForDebug

如何選擇使用哪一種方式建立

Build script

You can include the source for the plugin directly in the build script. This has the benefit that the plugin is automatically compiled and included in the classpath of the build script without you having to do anything. However, the plugin is not visible outside the build script, and so you cannot reuse the plugin outside the build script it is defined in.

  • 編寫簡單,無須配置
  • 外部不可用到

buildSrc project

You can put the source for the plugin in the rootProjectDir/buildSrc/src/main/groovy directory (or rootProjectDir/buildSrc/src/main/java or rootProjectDir/buildSrc/src/main/kotlin depending on which language you prefer). Gradle will take care of compiling and testing the plugin and making it available on the classpath of the build script. The plugin is visible to every build script used by the build. However, it is not visible outside the build, and so you cannot reuse the plugin outside the build it is defined in.

  • 項目內可見
  • 適用於邏輯較爲複雜的插件
  • 外部沒法使用

Standalone project

You can create a separate project for your plugin. This project produces and publishes a JAR which you can then use in multiple builds and share with others. Generally, this JAR might include some plugins, or bundle several related task classes into a single library. Or some combination of the two.

  • 適合較爲複雜的場景
  • 能夠獨立打包發佈

Gradle Transform

  • getName()

用來定義 transform 任務的名稱。

  • getInputTypes()

用來限定這個 transform 能處理的文件類型,通常來講咱們要處理的都是 class 文件,就返回 TransformManager.CONTENT_CLASS,固然若是你是想要處理資源文件,可使用TransformManager.CONTENT_RESOURCES 。

  • getScopes()

指定的的就是哪些文件了。好比說咱們若是想處理 class 文件,但 class 文件能夠是當前module的,也能夠是子 module 的,還能夠是第三方 jar 包中的.

Type 做用域
PROJECT 只處理當前項目
SUB_PROJECTS 只處理子項目
PROJECT_LOCAL_DEPS 只處理當前項目的本地依賴,例如 jar, aar
SUB_PROJECTS_LOCAL_DEPS 只處理子項目的本地依賴,例如 jar, aar
EXTERNAL_LIBRARIES 只處理外部的依賴庫
PROVIDED_ONLY 只處理本地或遠程以provided形式引入的依賴庫
TESTED_CODE 測試代碼
  • inIncremental()

是否支持增量編譯。

  • transform()

獲取輸入的 class 文件,而後作些修改,最後輸出修改後的 class 文件。

transform 步驟

  • 獲取輸入文件

一種是本 module 本身的 src下的源碼編譯後的 class 文件,一種是第三方的 jar 包文件,咱們須要分開單獨處理。

  • 獲取輸出路徑

輸入文件有了,咱們要先肯定輸出路徑,這裏要注意,輸出路徑必須用特殊方式獲取,而不能本身隨意指定,不然下一個任務就沒法獲取你此次的輸出文件了,編譯失敗。

  • 處理輸入文件

對輸入的文件進行處理,也是核心的步驟;作代碼插入、修改等操做就是在這個步驟進行操做。

演示 DEMO

其餘

使用 kotlin 來寫 Gradle 腳本

相關文章
相關標籤/搜索