Gradle自定義插件和發布
這篇文章講解的是如何自定義gradle插件,并以本地依賴和遠程依賴的方式來集成。
本文大體結構和內容基于:gradle官網的教程:開發自定義gradle插件
約定俗成的說法:
- 插件消費者項目:使用對應插件的項目。
- 開發插件的項目:獨立的,用來開發gradle插件的項目。
開始:
自定義gradle插件的三種形式:
- 直接在項目中寫一個插件,并由build.gradle直接應用來使用。
- 在獨立的項目中開發插件,并以本地依賴的形式集成。
- 在獨立的項目中開發插件,并以遠程依賴的形式集成。
1 直接在項目中寫插件并應用
2 在獨立的項目中開發一個插件,本地依賴
2.1 新建一個獨立的java類項目
用idea或者android studio新建一個module或者新建一個project都可以,只要是能夠輕易打出jar包的項目即可。
2.1.1 build.gradle中引入gradle api的依賴
首先,自定義插件類的編寫,需要實現org.gradle.api.Plugin
接口,這個不是jdk的類,而是gradle提供的接口,因此必須對gradle的api做一個依賴。
那么在項目的build.gradle
中寫入:
plugins {
id 'groovy'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
//我們需要實現gradle的Plugin<T>接口,來做自定義插件,因此依賴gradle api
implementation gradleApi()
//依賴gradle提供的groovy sdk,在編寫自定義插件的時候,用groovy更快。
implementation localGroovy()
}
sourceCompatibility = "7"
targetCompatibility = "7"
我的例子并沒有用到groovy,所以去掉上面的localGroovy
,并將groovy
插件換成java
插件也可以。
2.1.2 創建并配置.properties文件。
其次,我們寫出來的插件最終作為jar包被插件消費者項目引用,那么插件消費者項目要如何在jar包中找到我們org.gradle.Plugin
接口的實現類呢?以及我們在哪里定義我們的插件的id呢?
那么,需要在路徑:src/main/resources/META-INF/gradle-plugins/
下,創建一個aa.bb.cc.properties
名字的文件,里面容為:
implementation-class=你的Plugin<T>接口實現類的全路徑名,例如:aa.bb.cc.MyPlugin
那么此時,消費者項目能夠找到插件接口實現類,并且,properties
文件的名字就作為你的插件id。
2.1.3 寫一個簡單的插件,生成一個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");
}
});
}
});
}
}
以上,所有的代碼編寫工作完成了。
貼出此時項目的結構:
/customPlugin
│ .gitignore
│ build.gradle
│
└─src
└─main
├─java
│ └─com
│ └─william
│ └─customplugin
│ GreetingPlugin.java
│
└─resources
└─META-INF
└─gradle-plugins
com.william.customplugin.properties
2.2 構建項目的產物:jar包
注意,在這一步,有兩種實現方案:
- 直接用build類型的gradle腳本打出一個jar包,然后將jar包復制粘貼到插件消費者項目。
- 用gradle的
maven
或者maven-publish
插件,來將打包的jar包發布到指定的本地路徑中。
我們先看第一種方式:
2.2.1 用build類型的gradle腳本打包
執行
/customPlugin
$ gradlew build
構建產物在:./build/libs/customPlugin.jar
此時拿到了jar包。其中包含了我們寫的org.gradle.Plugin
接口的實現類。
2.2.2 用maven類型的腳本打包并發布在指定的本地路徑
打開插件開發項目,我們需要發布一個maven類型的軟件包。gradle為maven類型的軟件包發布提供了兩種插件:maven
和maven-publish
,前者已經被廢棄,現在最新的是后者,我們這里用后者插件來實現構件一個maven類型的軟件包。
我們參考的是maven-publish
文檔的最簡單的發版配置:
group = 'org.example'
version = '1.0'
publishing {
publications {
myLibrary(MavenPublication) {
from components.java
}
}
repositories {
maven {
name = 'myRepo'
url = "file://${buildDir}/repo"
}
}
}
將上述的配置移植到我們的插件開發項目的build.gradle中,如下:
plugins {
id 'groovy'
id 'maven-publish'
}
group = "com.william.customplugin"
version = "1.0"
publishing {
publications {
myLibrary(MavenPublication) {
from components.java
}
}
//倉庫配置
repositories {
maven {
//name這個屬性是用來指定倉庫名字的,貌似在這里沒什么用,注釋掉。
//name = 'myRepo'
//指定發布倉庫的路徑
url = "./build/repo"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation gradleApi()
implementation localGroovy()
}
sourceCompatibility = "7"
targetCompatibility = "7"
接下來就是執行一個task,來構建并發布了。
maven-publish
腳本帶來了task publish
,他會執行所有的發布任務。而我們這里只有一個發布任務,所以執行他就好了。
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,現在跑去./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
輸出的整個包多了很多東西,我們用普通的`build`命令打出來的jar包,只有一個單獨的jar包,而用`maven-publish`插件打出來的包,囊括了完整的全路徑名,帶有pom文件用于描述依賴,帶有maven元數據等等,這是一個完整的、可用于分發的軟件了
2.3 在插件消費者項目中使用插件
2.3.1 直接使用本地的jar包中的插件。
拿到jar包后,把他放在一個可以被找到的路徑,我把他放在了插件消費者項目的根目錄的gradle_plugin_libs/
目錄下。
└─gradle_plugin_libs
customPlugin.jar
一般來說,安卓項目都采用的是gradle的multi-project-build組織類型,所以我打算在根目錄下讓gradle對我的腳本進行一個依賴,以便子項目不需要再自己去聲明對腳本的依賴。
即rootProject/build.gradle
下:
buildscript{
repositories{
//...
}
dependencies{
classpath 'com.android.tools.build:gradle:3.4.1'
//...
//注意,之類不能用classpath,因為classpath指定的是以標準的maven格式構建的軟件包。
//我們這里是直接用jar的,classpath識別不了,會報錯。因此用classpath files()
classpath files ('./gradle_plugin_libs/customPlugin.jar')
}
}
//..
隨意地找一個項目的構建腳本,寫上:apply plugin: 'com.william.customplugin'
。(注意,插件Id是properties文件的名字)
執行命令:gradlew hello
--> 輸出: task hello---doLast
大功告成~
2.3.2 使用本地maven倉庫下的jar包中的插件
將2.2.2節構建出的軟件包整個復制到gradle_plugin_libs/
中。
修改根目錄構建腳本:rootProject/build.gradle
buildscript{
repositories{
//...
//指定一個maven倉庫的路徑
maven {
url uri('./gradle_plugin_libs')
}
}
dependencies{
//...
//用標準的classpath方法,來找到我們的插件
classpath('com.william.customplugin:customPlugin:1.0')
}
}
//..
執行gradlew hello
,輸出和上一節一樣。
大功告成~
2.4 如何測試插件?
待補充
3 在獨立的項目中開發一個插件,遠程依賴
上面我們對插件進行了本地依賴的方式來使用,現在我們將我們的插件打出的軟件包上傳到遠程倉庫上,讓別的開發者也能快速地集成。
3.1 新建一個獨立的java類項目
我們就用上述的那個插件開發項目,不用再新建了。
3.2 將構建的jar包上傳到一個遠程倉庫
遠程倉庫有兩個類型:
- 大家所熟知的中央倉庫:例如
mavenCentral
,jcenter
,jitpack
- 普通的遠程倉庫
他們本質上都是一樣的,只是那3個中央倉庫是用的人最多的。
我們現在通過bintray
這個軟件包發行平臺,來上傳并管理我們的軟件包。
bintray
用于上傳maven類型的軟件包有3種方式,我們的其中一種是通過gradle的方式:https://github.com/bintray/gradle-bintray-plugin。這是bintray官方開發的上傳插件,專門用于打包和上傳maven類型的軟件包到bintray上面,選項多,可自定義程度高,使用復雜。
在github上有一個快捷方便的第三方開發者開發的插件:https://github.com/novoda/bintray-release
只需要一個代碼塊就能完成bintray上傳的配置,這里我們用這個簡單的插件來做演示,當然大家可以根據自己業務的需求選擇負責度和功能性更高的官方插件。
3.2.1 創建bintray賬號
在這一步,要完成的事項如下:
- 創建bintray賬號
- 創建一個要上傳該軟件包的倉庫
- 在上述倉庫下創建一個package,即創建一個軟件包,軟件包需要和你后面上傳的軟件包名字相同。(我很納悶為什么一定要創建了一個包才能上傳,不能上傳的時候沒有包bintray就自己創建嗎,真是奇怪,我在這一步停滯了很久...)
3.2.2 配置bintray上傳腳本
plugins {
id 'groovy'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation gradleApi()
implementation localGroovy()
}
sourceCompatibility = "7"
targetCompatibility = "7"
//以上是默認的內容,以下是上傳腳本的配置,可以說是非常簡潔了。
//應用插件
apply plugin: 'com.novoda.bintray-release'
//上傳配置
publish {
userOrg = "huangwilliam33333"http://組織,如果沒有創建組織,就直接填寫用戶名。
groupId = 'com.william'//group
artifactId = 'customPlugin'//module
publishVersion = '1.0'//版本
desc = '自定義測試用的插件'//描述
website = 'htttp://www.baidu.com'//網站,隨便填
bintrayUser = "huangwilliam33333"http://用戶名
bintrayKey = "xxx"http://bintray秘鑰
dryRun = false//如果這個是true,則只是運行,而不會上傳,要配置成false
}
執行gradlew clean build bintrayUpload
上傳成功。
3.3 在另一個項目中依賴這個遠程倉庫,并使用該插件
到插件消費者項目中:
我們還是在根目錄應用插件:
buildscript {
ext.kotlin_version = '1.3.41'
repositories {
google()
jcenter()
//因為還沒有將軟件包加入到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 如何測試插件
待補充
參考資料:
https://stackoverflow.com/questions/35302414/adding-local-plugin-to-a-gradle-project