Gradle自定義插件和發(fā)布

Gradle自定義插件和發(fā)布

這篇文章講解的是如何自定義gradle插件,并以本地依賴和遠(yuǎn)程依賴的方式來(lái)集成。

本文大體結(jié)構(gòu)和內(nèi)容基于:gradle官網(wǎng)的教程:開發(fā)自定義gradle插件

約定俗成的說(shuō)法:

  1. 插件消費(fèi)者項(xiàng)目:使用對(duì)應(yīng)插件的項(xiàng)目。
  2. 開發(fā)插件的項(xiàng)目:獨(dú)立的,用來(lái)開發(fā)gradle插件的項(xiàng)目。

開始:

自定義gradle插件的三種形式:

  1. 直接在項(xiàng)目中寫一個(gè)插件,并由build.gradle直接應(yīng)用來(lái)使用。
  2. 在獨(dú)立的項(xiàng)目中開發(fā)插件,并以本地依賴的形式集成。
  3. 在獨(dú)立的項(xiàng)目中開發(fā)插件,并以遠(yuǎn)程依賴的形式集成。

1 直接在項(xiàng)目中寫插件并應(yīng)用

2 在獨(dú)立的項(xiàng)目中開發(fā)一個(gè)插件,本地依賴

2.1 新建一個(gè)獨(dú)立的java類項(xiàng)目

用idea或者android studio新建一個(gè)module或者新建一個(gè)project都可以,只要是能夠輕易打出jar包的項(xiàng)目即可。

2.1.1 build.gradle中引入gradle api的依賴

首先,自定義插件類的編寫,需要實(shí)現(xiàn)org.gradle.api.Plugin接口,這個(gè)不是jdk的類,而是gradle提供的接口,因此必須對(duì)gradle的api做一個(gè)依賴。

那么在項(xiàng)目的build.gradle中寫入:

plugins {
    id 'groovy'
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    //我們需要實(shí)現(xiàn)gradle的Plugin<T>接口,來(lái)做自定義插件,因此依賴gradle api
    implementation gradleApi()
    //依賴gradle提供的groovy sdk,在編寫自定義插件的時(shí)候,用groovy更快。
    implementation localGroovy()
}

sourceCompatibility = "7"
targetCompatibility = "7"

我的例子并沒有用到groovy,所以去掉上面的localGroovy,并將groovy插件換成java插件也可以。

2.1.2 創(chuàng)建并配置.properties文件。

其次,我們寫出來(lái)的插件最終作為jar包被插件消費(fèi)者項(xiàng)目引用,那么插件消費(fèi)者項(xiàng)目要如何在jar包中找到我們org.gradle.Plugin接口的實(shí)現(xiàn)類呢?以及我們?cè)谀睦锒x我們的插件的id呢?

那么,需要在路徑:src/main/resources/META-INF/gradle-plugins/下,創(chuàng)建一個(gè)aa.bb.cc.properties名字的文件,里面容為:

implementation-class=你的Plugin<T>接口實(shí)現(xiàn)類的全路徑名,例如:aa.bb.cc.MyPlugin

那么此時(shí),消費(fèi)者項(xiàng)目能夠找到插件接口實(shí)現(xiàn)類,并且,properties文件的名字就作為你的插件id。

2.1.3 寫一個(gè)簡(jiǎn)單的插件,生成一個(gè)task
package com.william.customplugin;

import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;

/**
 * date: 2019/9/6 0006
 *
 * @author hwj
 * description 插件
 */
public class GreetingPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        project.task("hello", new Action<Task>() {
            @Override
            public void execute(Task task) {
                task.doLast(new Action<Task>() {
                    @Override
                    public void execute(Task task) {
                        System.out.println("task hello---doLast");
                    }
                });
            }
        });
    }
}

以上,所有的代碼編寫工作完成了。

貼出此時(shí)項(xiàng)目的結(jié)構(gòu):

/customPlugin

│  .gitignore
│  build.gradle
│
└─src
    └─main
        ├─java
        │  └─com
        │      └─william
        │          └─customplugin
        │                  GreetingPlugin.java
        │
        └─resources
            └─META-INF
                └─gradle-plugins
                        com.william.customplugin.properties

2.2 構(gòu)建項(xiàng)目的產(chǎn)物:jar包

注意,在這一步,有兩種實(shí)現(xiàn)方案:

  1. 直接用build類型的gradle腳本打出一個(gè)jar包,然后將jar包復(fù)制粘貼到插件消費(fèi)者項(xiàng)目。
  2. 用gradle的maven或者maven-publish插件,來(lái)將打包的jar包發(fā)布到指定的本地路徑中。

我們先看第一種方式:

2.2.1 用build類型的gradle腳本打包

執(zhí)行

/customPlugin
$ gradlew build

構(gòu)建產(chǎn)物在:./build/libs/customPlugin.jar

此時(shí)拿到了jar包。其中包含了我們寫的org.gradle.Plugin接口的實(shí)現(xiàn)類。

2.2.2 用maven類型的腳本打包并發(fā)布在指定的本地路徑

打開插件開發(fā)項(xiàng)目,我們需要發(fā)布一個(gè)maven類型的軟件包。gradle為maven類型的軟件包發(fā)布提供了兩種插件:mavenmaven-publish,前者已經(jīng)被廢棄,現(xiàn)在最新的是后者,我們這里用后者插件來(lái)實(shí)現(xiàn)構(gòu)件一個(gè)maven類型的軟件包。

我們參考的是maven-publish文檔的最簡(jiǎn)單的發(fā)版配置:

group = 'org.example'
version = '1.0'

publishing {
    publications {
        myLibrary(MavenPublication) {
            from components.java
        }
    }

    repositories {
        maven {
            name = 'myRepo'
            url = "file://${buildDir}/repo"
        }
    }
}

將上述的配置移植到我們的插件開發(fā)項(xiàng)目的build.gradle中,如下:

plugins {
    id 'groovy'
    id 'maven-publish'
}

group = "com.william.customplugin"
version = "1.0"

publishing {
    publications {
        myLibrary(MavenPublication) {
            from components.java
        }
    }

    //倉(cāng)庫(kù)配置
    repositories {
        maven {
            //name這個(gè)屬性是用來(lái)指定倉(cāng)庫(kù)名字的,貌似在這里沒什么用,注釋掉。
            //name = 'myRepo'
            
            //指定發(fā)布倉(cāng)庫(kù)的路徑
            url = "./build/repo"
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation gradleApi()
    implementation localGroovy()
}

sourceCompatibility = "7"
targetCompatibility = "7"

接下來(lái)就是執(zhí)行一個(gè)task,來(lái)構(gòu)建并發(fā)布了。

maven-publish腳本帶來(lái)了task publish,他會(huì)執(zhí)行所有的發(fā)布任務(wù)。而我們這里只有一個(gè)發(fā)布任務(wù),所以執(zhí)行他就好了。

gradlew publish

===>

14:45:49: Executing task 'publish'...

Executing tasks: [publish] in project C:\AndroidProject\KotlinSimpleTest\customPlugin

> Task :customPlugin:generatePomFileForMyLibraryPublication
> Task :customPlugin:compileJava UP-TO-DATE
> Task :customPlugin:compileGroovy NO-SOURCE
> Task :customPlugin:processResources UP-TO-DATE
> Task :customPlugin:classes UP-TO-DATE
> Task :customPlugin:jar UP-TO-DATE
> Task :customPlugin:publishMyLibraryPublicationToMavenRepository
> Task :customPlugin:publish

BUILD SUCCESSFUL in 4s
5 actionable tasks: 2 executed, 3 up-to-date
14:45:53: Task execution finished 'publish'.

OK,現(xiàn)在跑去./build/repo下面找我們的軟件包吧。

├─repo
│  └─com
│      └─william
│          └─customplugin
│              └─customPlugin
│                  │  maven-metadata.xml
│                  │  maven-metadata.xml.md5
│                  │  maven-metadata.xml.sha1
│                  │
│                  └─1.0
│                          customPlugin-1.0.jar
│                          customPlugin-1.0.jar.md5
│                          customPlugin-1.0.jar.sha1
│                          customPlugin-1.0.pom
│                          customPlugin-1.0.pom.md5
│                          customPlugin-1.0.pom.sha1

輸出的整個(gè)包多了很多東西,我們用普通的`build`命令打出來(lái)的jar包,只有一個(gè)單獨(dú)的jar包,而用`maven-publish`插件打出來(lái)的包,囊括了完整的全路徑名,帶有pom文件用于描述依賴,帶有maven元數(shù)據(jù)等等,這是一個(gè)完整的、可用于分發(fā)的軟件了

2.3 在插件消費(fèi)者項(xiàng)目中使用插件

2.3.1 直接使用本地的jar包中的插件。

拿到j(luò)ar包后,把他放在一個(gè)可以被找到的路徑,我把他放在了插件消費(fèi)者項(xiàng)目的根目錄的gradle_plugin_libs/目錄下。

└─gradle_plugin_libs
        customPlugin.jar

一般來(lái)說(shuō),安卓項(xiàng)目都采用的是gradle的multi-project-build組織類型,所以我打算在根目錄下讓gradle對(duì)我的腳本進(jìn)行一個(gè)依賴,以便子項(xiàng)目不需要再自己去聲明對(duì)腳本的依賴。

rootProject/build.gradle下:

buildscript{
    repositories{
        //...
    }
    dependencies{
        classpath 'com.android.tools.build:gradle:3.4.1'
        //...
        
        //注意,之類不能用classpath,因?yàn)閏lasspath指定的是以標(biāo)準(zhǔn)的maven格式構(gòu)建的軟件包。
        //我們這里是直接用jar的,classpath識(shí)別不了,會(huì)報(bào)錯(cuò)。因此用classpath files()
        classpath files ('./gradle_plugin_libs/customPlugin.jar')
    }
}
//..

隨意地找一個(gè)項(xiàng)目的構(gòu)建腳本,寫上:apply plugin: 'com.william.customplugin'。(注意,插件Id是properties文件的名字)

執(zhí)行命令:gradlew hello--> 輸出: task hello---doLast

大功告成~

2.3.2 使用本地maven倉(cāng)庫(kù)下的jar包中的插件

將2.2.2節(jié)構(gòu)建出的軟件包整個(gè)復(fù)制到gradle_plugin_libs/中。

修改根目錄構(gòu)建腳本:rootProject/build.gradle

buildscript{
    repositories{
        //...

        //指定一個(gè)maven倉(cāng)庫(kù)的路徑
        maven {
            url uri('./gradle_plugin_libs')
        }
    }
    dependencies{
        //...
        
        //用標(biāo)準(zhǔn)的classpath方法,來(lái)找到我們的插件
        classpath('com.william.customplugin:customPlugin:1.0')
    }
}
//..

執(zhí)行gradlew hello,輸出和上一節(jié)一樣。

大功告成~

2.4 如何測(cè)試插件?

待補(bǔ)充

3 在獨(dú)立的項(xiàng)目中開發(fā)一個(gè)插件,遠(yuǎn)程依賴

上面我們對(duì)插件進(jìn)行了本地依賴的方式來(lái)使用,現(xiàn)在我們將我們的插件打出的軟件包上傳到遠(yuǎn)程倉(cāng)庫(kù)上,讓別的開發(fā)者也能快速地集成。

3.1 新建一個(gè)獨(dú)立的java類項(xiàng)目

我們就用上述的那個(gè)插件開發(fā)項(xiàng)目,不用再新建了。

3.2 將構(gòu)建的jar包上傳到一個(gè)遠(yuǎn)程倉(cāng)庫(kù)

遠(yuǎn)程倉(cāng)庫(kù)有兩個(gè)類型:

  1. 大家所熟知的中央倉(cāng)庫(kù):例如mavenCentraljcenter,jitpack
  2. 普通的遠(yuǎn)程倉(cāng)庫(kù)

他們本質(zhì)上都是一樣的,只是那3個(gè)中央倉(cāng)庫(kù)是用的人最多的。

我們現(xiàn)在通過bintray這個(gè)軟件包發(fā)行平臺(tái),來(lái)上傳并管理我們的軟件包。

bintray用于上傳maven類型的軟件包有3種方式,我們的其中一種是通過gradle的方式:https://github.com/bintray/gradle-bintray-plugin。這是bintray官方開發(fā)的上傳插件,專門用于打包和上傳maven類型的軟件包到bintray上面,選項(xiàng)多,可自定義程度高,使用復(fù)雜。

在github上有一個(gè)快捷方便的第三方開發(fā)者開發(fā)的插件:https://github.com/novoda/bintray-release

只需要一個(gè)代碼塊就能完成bintray上傳的配置,這里我們用這個(gè)簡(jiǎn)單的插件來(lái)做演示,當(dāng)然大家可以根據(jù)自己業(yè)務(wù)的需求選擇負(fù)責(zé)度和功能性更高的官方插件。

3.2.1 創(chuàng)建bintray賬號(hào)

在這一步,要完成的事項(xiàng)如下:

  1. 創(chuàng)建bintray賬號(hào)
  2. 創(chuàng)建一個(gè)要上傳該軟件包的倉(cāng)庫(kù)
  3. 在上述倉(cāng)庫(kù)下創(chuàng)建一個(gè)package,即創(chuàng)建一個(gè)軟件包,軟件包需要和你后面上傳的軟件包名字相同。(我很納悶為什么一定要?jiǎng)?chuàng)建了一個(gè)包才能上傳,不能上傳的時(shí)候沒有包bintray就自己創(chuàng)建嗎,真是奇怪,我在這一步停滯了很久...)
3.2.2 配置bintray上傳腳本
plugins {
    id 'groovy'
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation gradleApi()
    implementation localGroovy()
}

sourceCompatibility = "7"
targetCompatibility = "7"

//以上是默認(rèn)的內(nèi)容,以下是上傳腳本的配置,可以說(shuō)是非常簡(jiǎn)潔了。

//應(yīng)用插件
apply plugin: 'com.novoda.bintray-release'

//上傳配置
publish {
    userOrg = "huangwilliam33333"http://組織,如果沒有創(chuàng)建組織,就直接填寫用戶名。
    groupId = 'com.william'//group
    artifactId = 'customPlugin'//module
    publishVersion = '1.0'//版本
    desc = '自定義測(cè)試用的插件'//描述
    website = 'htttp://www.baidu.com'//網(wǎng)站,隨便填
    bintrayUser = "huangwilliam33333"http://用戶名
    bintrayKey = "xxx"http://bintray秘鑰
    dryRun = false//如果這個(gè)是true,則只是運(yùn)行,而不會(huì)上傳,要配置成false
}

執(zhí)行gradlew clean build bintrayUpload

上傳成功。

3.3 在另一個(gè)項(xiàng)目中依賴這個(gè)遠(yuǎn)程倉(cāng)庫(kù),并使用該插件

到插件消費(fèi)者項(xiàng)目中:

我們還是在根目錄應(yīng)用插件:

buildscript {
    ext.kotlin_version = '1.3.41'
    repositories {
        google()
        jcenter()
        
        //因?yàn)檫€沒有將軟件包加入到Jcenter,所以要指明maven地址
        maven{
            url  "https://dl.bintray.com/huangwilliam33333/maven"
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        classpath 'com.novoda:bintray-release:0.9.1'
            
        //依賴插件
        classpath('com.william:customPlugin:1.0')
    }
}

插件的使用和前面一樣。

3.4 如何測(cè)試插件

待補(bǔ)充

參考資料:

bintray第三方簡(jiǎn)易上傳插件

bintray官方上傳插件

講到了bintray創(chuàng)建了倉(cāng)庫(kù)后還要?jiǎng)?chuàng)建軟件包才能上傳

https://stackoverflow.com/questions/35302414/adding-local-plugin-to-a-gradle-project

gradle官方文檔:publishing artifact

gradle官方文檔:maven publish plugin

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容