自定義插件

Gradle插件打包可重用的構建邏輯片斷,可用於許多不一樣的項目和構建。 Gradle容許實現本身的插件,所以能夠重用構建邏輯,並與其餘人共享。java

可使用任何語言實現Gradle插件,前提是實現最終編譯爲JVM字節碼。 在咱們的示例中,咱們將使用Groovy做爲實現語言。 Groovy,Java或Kotlin都是用於實現插件的語言的不錯選擇,由於Gradle API旨在與這些語言一塊兒使用。 一般,使用Java或Kotlin實現的靜態類型的插件將比使用Groovy實現的相同插件執行得更好。android

有幾個地方能夠放置插件的源代碼。git

Build script

能夠直接在構建腳本中包含插件的源代碼。這樣作的好處是插件能夠自動編譯幷包含在構建腳本的類路徑中,而無需執行任何操做。可是,插件在構建腳本以外是不可見的,所以您沒法在其定義的構建腳本以外重用該插件。github

buildSrc project

能夠將插件的源代碼放在rootProjectDir / buildSrc / src / main / groovy目錄(或rootProjectDir / buildSrc / src / main / java或rootProjectDir / buildSrc / src / main / kotlin中。)Gradle將負責編譯和測試插件並使其在構建腳本的類路徑中可用。 該插件對於構建使用的每一個構建腳本都是可見的。 可是,它在構建以外是不可見的,所以您沒法在其定義的構建以外重用該插件。bash

Standalone project

能夠爲插件建立單獨的項目。 該項目生成併發佈一個JAR,而後您能夠在多個版本中使用它並與其餘人共享。 一般,此JAR可能包含一些插件,或將多個相關的任務類捆綁到單個庫中。 或二者的某種組合。閉包

基於 Build script 編寫插件

要建立Gradle插件,須要編寫一個實現Plugin接口的類。 當插件應用於項目時,Gradle會建立插件類的實例並調用實例的Plugin.apply()方法。 項目對象做爲參數傳遞,插件可使用它來配置項目。併發

## build.gradle
class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.task('hello') {
            doLast {
                println 'Hello from the GreetingPlugin'
            }
        }
    }
}

// Apply the plugin
apply plugin: GreetingPlugin
複製代碼
> gradle -q hello
Hello from the GreetingPlugin
複製代碼

須要注意的一點是,爲應用它的每一個項目建立了一個新的插件實例。 另請注意,Plugin類是泛型類型。 此示例使其接收Project類型做爲類型參數。 插件能夠改成接收類型爲Settings的參數,在這種狀況下,插件能夠應用於設置腳本或Gradle類型的參數,在這種狀況下,插件能夠應用於初始化腳本中。app

使插件可配置

大多數插件須要從構建腳本中獲取一些配置。執行此操做的一種方法是使用擴展對象。Gradle項目具備關聯的ExtensionContainer對象,該對象包含已應用於項目的插件的全部設置和屬性。能夠經過向此容器添加擴展對象來爲插件提供配置。擴展對象只是一個Java Bean兼容類。maven

##build.gradle
class GreetingPluginExtension {
    String message = 'Hello from GreetingPlugin'
}

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        // Add the 'greeting' extension object
        def extension = project.extensions.create('greeting', GreetingPluginExtension)
        // Add a task that uses configuration from the extension object
        project.task('hello') {
            doLast {
                println extension.message
            }
        }
    }
}

apply plugin: GreetingPlugin

// Configure the extension
greeting.message = 'Hi from Gradle'
複製代碼
> gradle -q hello
Hi from Gradle
複製代碼

在此示例中,GreetingPluginExtension是一個普通的Groovy / Kotlin對象,其中包含一個名爲message的屬性。 擴展對象將添加到名稱爲greeting的插件列表中。 而後,此對象可用做與擴展對象同名的項目屬性。ide

一般,您須要在單個插件上指定幾個相關屬性。 Gradle爲每一個擴展對象添加配置塊,所以您能夠將設置組合在一塊兒。

##build.gradle
class GreetingPluginExtension {
    String message
    String greeter
}

class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        def extension = project.extensions.create('greeting', GreetingPluginExtension)
        project.task('hello') {
            doLast {
                println "${extension.message} from ${extension.greeter}"
            }
        }
    }
}

apply plugin: GreetingPlugin

// Configure the extension using a DSL block
greeting {
    message = 'Hi'
    greeter = 'Gradle'
}
複製代碼
> gradle -q hello
Hi from Gradle
複製代碼

在此示例中,能夠在greeting閉包內將多個設置組合在一塊兒。 構建腳本(greeting)中閉包塊的名稱須要與擴展對象名稱匹配。 而後,當執行閉包時,擴展對象上的字段將根據標準Groovy閉包委託功能映射到閉包內的變量。

使用自定義任務和插件中的文件

在開發自定義任務和插件時,最好在接受文件位置的輸入配置時保持靈活性。 爲此,您能夠利用Project.file(java.lang.Object)方法儘量晚地將值解析爲文件。

## build.gradle
class GreetingToFileTask extends DefaultTask {

    def destination

    File getDestination() {
        project.file(destination)
    }

    @TaskAction
    def greet() {
        def file = getDestination()
        file.parentFile.mkdirs()
        file.write 'Hello!'
    }
}

task greet(type: GreetingToFileTask) {
    destination = { project.greetingFile }
}

task sayGreeting(dependsOn: greet) {
    doLast {
        println file(greetingFile).text
    }
}

ext.greetingFile = "$buildDir/hello.txt"
複製代碼
> gradle -q sayGreeting
Hello!
複製代碼

在此示例中,咱們將greet任務的destination屬性配置爲閉包/提供程序,使用Project.file(java.lang.Object)方法對其進行評估,以將閉包/提供程序的返回值轉換爲最後的File對象。在上面的示例中,咱們在配置爲將其用於任務後指定greetingFile屬性值。 這種延遲評估是在設置文件屬性時接受任何值,而後在讀取屬性時解析該值的關鍵優點。

將擴展屬性映射到任務屬性

經過擴展從構建腳本捕獲用戶輸入並將其映射到自定義任務的輸入/輸出屬性是一種有用的模式。 構建腳本做者僅與擴展名定義的DSL進行交互。 命令式邏輯隱藏在插件實現中。

Gradle提供了一些能夠在任務實現和擴展中使用的類型來幫助您完成此任務。

一個獨立的項目

如今咱們將把咱們的插件移到一個獨立的項目中,這樣咱們就能夠發佈它並與其餘人共享。 這個項目只是一個Groovy項目,它生成一個包含插件類的JAR。 這是項目的簡單構建腳本。 它應用Groovy插件,並將Gradle API添加爲編譯時依賴項。

##build.gradle
plugins {
    id 'groovy'
}

dependencies {
    implementation gradleApi()
    implementation localGroovy()
}
複製代碼

那麼Gradle如何找到插件實現呢? 答案是你須要在jar的META-INF / gradle-plugins目錄中提供一個與你的插件的id匹配的屬性文件。

示例:爲自定義插件接線

src/main/resources/META-INF/gradle-plugins/org.samples.greeting.properties

implementation-class=org.gradle.GreetingPlugin
複製代碼

請注意,屬性文件名與插件ID匹配,而且放在resources文件夾中,而implementation-class屬性標識插件實現類。

建立插件ID

插件ID以相似於Java包的方式(即反向域名)徹底限定。 這有助於避免衝突,並提供了一種對具備相似全部權的插件進行分組的方法。

插件ID應該是反映命名空間(指向您或您的組織的合理指針)的組件的組合,以及它提供的插件的名稱。例如,若是您有一個名爲「foo」的Github賬戶,而且您的插件名爲「bar」,則合適的插件ID多是com.github.foo.bar。

發佈插件到本地

## build.gradle
classpath "com.github.dcendents:android-maven-gradle-plugin:1.5"
複製代碼
## java-publish.gradle
apply plugin: 'maven-publish'

def javadocJar = task("javadocJar", type: Jar, dependsOn: javadoc) {
    classifier = 'javadoc'
    from javadoc.destinationDir
}
def sourcesJar = task("sourcesJar", type: Jar) {
    classifier = 'sources'
    from sourceSets.main.java.srcDirs
}

publishing {
    publications {
        Component(MavenPublication) {
            from components.java
            groupId = group
            artifactId = POM_ARTIFACT_ID
            version = version

            artifact sourcesJar
            artifact javadocJar
        }
    }
}

task buildAndPublishToLocalMaven(type: Copy, dependsOn: ['build', 'publishToMavenLocal']) {
    group = POM_NAME

    // save artifacts files to artifacts folder
    from configurations.archives.allArtifacts.files
    into "${rootProject.buildDir}/outputs/artifacts/"
    rename { String fileName ->
        fileName.replace("release.aar", "${version}.aar")
    }

    doLast {
        println "* published to maven local: ${project.group}:${project.name}:${project.version}"
    }
}
複製代碼
## gradle.properties
POM_ARTIFACT_ID=greeting-gradle-plugin
POM_NAME=greeting
POM_PACKAGING=jar
複製代碼

點擊以後會在本地倉庫生成對應的jar包:

在另外一個項目中使用插件

找到項目的根目錄的build.gradle文件:

buildscript {
    repositories {
        mavenLocal()

        google()
        jcenter()
    }

    dependencies {
        // classpath
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath("com.zpw.greeting:greeting-gradle-plugin:1.0")
    }
}
複製代碼

而後在項目app的build.gradle文件中添加:

apply plugin: 'com.zpw.greeting_plugin'
複製代碼

爲插件提供配置DSL

可使用擴展對象爲插件提供配置。 使用擴展對象還擴展了Gradle DSL,爲插件添加項目屬性和DSL塊。 擴展對象只是一個常規對象,所以您能夠經過向擴展對象添加屬性和方法來提供嵌套在此塊中的DSL元素。

嵌套的DSL元素

當Gradle建立任務或擴展對象時,Gradle會修改實現類以混合DSL支持和可擴展性。 要建立嵌套的DSL元素,可使用ObjectFactory類型建立相似裝飾的對象。 而後,能夠經過插件擴展的屬性和方法使這些裝飾對象對DSL可見:

class GreetingPluginExtension {
    String message
}
複製代碼
class GreetingPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        def extension = project.extensions.create('greeting', GreetingPluginExtension)

        project.task('hello') {
            doLast {
                println "${extension.message}"
            }
        }
    }
}
複製代碼
apply plugin: 'com.zpw.greeting_plugin'
greeting {
    message = 'Hi'
}
複製代碼
> gradle -q hello
Hi
複製代碼
相關文章
相關標籤/搜索