03.Gradle 自定义插件
7.0 8.0 变更
新版本 Gradle 配置更改
随着 Gradle 和 Android Gradle Plugin 的版本升级,以及 Kotlin DSL 的加入,Gradle 的配置和使用方式发生了很大的变化。
- 插件仓库和依赖仓库的配置从之前的根工程下的
build.gradle
文件移到了settings.gradle.kts
文件中 - 插件的引入方式也从之前的 classpath 更改为插件 Id: 根目录
plugins{}
- 插件的使用也是在子模块中由 apply 方式变更为 id 方式:
plugins{}
插件配置变更
变更前:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
buildscript {
ext.kotlin_version = '1.5.0'
repository {
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.17"
// 自定义 gradle 插件
classpath "com.sharpcj.plugin:abc:1.0.6"
}
}
allprojects {
repositories {
mavenCentral()
jcenter()
maven {
url "http://xx.xx.xx.xx:xxxx/xx/xx/"
}
}
}
变更后:
根目录下的 buildscript
变更到 settings.gradle
中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
maven {
allowInsecureProtocol = true
url "http://xx.xx.xx.xx:xxxx/xx/xx/"
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
maven {
allowInsecureProtocol = true
url "http://xx.xx.xx.xx:xxxx/xx/xx/"
}
}
}
Gradle 7.0.x 以上对于 http 协议的的仓库地址,需要显示声明:allowInsecureProtocol = true
根目录下 build.gradle:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 全局 buildescript 依旧可以用
buildscript {
ext.kotlin_version = '1.5.0'
// 自定义 gradle 插件
dependencies {
classpath "me.hacket:abc:1.0.6"
}
}
plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
id 'com.google.protobuf' version '0.8.17' apply false
}
apply plugins → plugins {}
app 模块的 build.gradle
配置:
1
apply plugin: 'com.android.application'
更改为 plugins{}
根工程下的 build.gradle.kts
配置:
1
2
3
4
5
plugins {
id("com.android.application") version "8.1.2" apply false //引入插件
id("org.jetbrains.kotlin.android") version "1.8.10" apply false //引入插件
}
// apply false:表示只是将插件引入,并不会自动应用插件到项目中(插件里的任务不会自动执行,只有手动调用里面的任务或者配置使用插件后任务才会执行),可以按需在模块中配置使用;subproject使用该插件就不用带version
app 模块的 build.gradle
配置:
1
2
3
plugins {
id("com.android.application") // 使用插件,不用带version
}
apply plugin 和 plugins 区别
根据官方文档,使用 plugin 分两步:
- 解析(Resolve): 找到包含给定插件的 jar 的正确版本,并将其添加到脚本类路径中
- 应用(apply):即调用 plugin,应用插件也分两种
- 脚本 plugin 它的写法是
apply from 'other.gradle'
, - 二进制 plugin 执行 Plugin.apply(T)
- 脚本 plugin 它的写法是
other.gradle
指文件目录与文件名或 Http Url
apply plugin(legacy)
apply plugin 老写法,它的解析和应用是分开的
1
2
3
4
5
6
7
8
9
10
11
12
13
//Using legacy plugin application
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:2.2.2.RELEASE"
}
}
apply plugin: "org.springframework.boot"
plugins DSL
plugins DSL 是一种新的插件的应用方式,这种方式只能在 Gradle 2.1 以上才可以使用;它的解析和应用是合并的
1
2
3
4
plugins {
id «plugin id»
id «plugin id» version «plugin version» [apply «false»]
}
对于核心 plugin,比如 com.android.application
、java
,版本号可以省略(新写法隐藏了传统的类似 Maven GAV 的 GroupId 和 ArtifectId,在 “Gradle plugin portal” 里注册会保存两者的映射,即从 com.jfrog.bintra
映射到 com.jfrog.bintray.gradle:gradle-bintray-plugin
,名字不一样,不小心的话,会找不到的):而后面的 apply 加上 boolean 值用来控制是否要立即应用该插件。比如我只想在工程的某个子工程中应用该插件:
1
2
3
4
5
6
7
8
plugins {
id "xyz" version "1.0.0" apply false
}
subprojects { subproject ->
if (subproject.name == "subProject") {
apply plugin: 'xyz'
}
}
这种情况下在子工程 apply 的版本号可以省略。
1
2
3
4
5
//Using the plugins DSL:
plugins {
id "org.springframework.boot" version "2.2.2.RELEASE"
}
使用 plugins{}这种引入插件的方式更简单些,用的 plugin 是核心 plugin 或者发布在 Gradle plugin repository 才可以。参考 Gradle官方插件库
## Plugin Management
pluginManagement{}
只能出现在 settings.gradle(.kts)
脚本中,且必须在初始化脚本 Initialization Script
: 要在文件的第一行
setting.gradle.kts
1
2
3
4
5
6
7
8
9
10
11
12
13
// settings.gradle.kts
pluginManagement {
plugins {
// 插件
}
resolutionStrategy {
// 解析策略
}
repositories {
// 要解析的插件的存放仓库
}
}
rootProject.name = "plugin-management"
init.gradle.kts
1
2
3
4
5
6
7
8
9
10
settingsEvaluated {
pluginManagement {
plugins {
}
resolutionStrategy {
}
repositories {
}
}
}
repositories{} 自定义 repositories
默认 plugins{}
从 Gradle Plugin Portal 解析插件,如果要从私有的 Maven/Ivy
仓库解析,可以自定义 repositories:
1
2
3
4
5
6
7
pluginManagement {
repositories {
maven(url = "./maven-repo")
gradlePluginPortal()
ivy(url = "./ivy-repo")
}
}
plugins{} 插件管理
pluginManagement
中的plugins
可以管理所有的 plugin,在这里引入的 plugin 所有 project 可用- 可以从
gradle.properties
读取 plugin version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// settings.gradle.kts
pluginManagement {
val helloPluginVersion: String by settings
plugins {
id("com.example.hello") version "${helloPluginVersion}"
}
}
// gradle.properties
helloPluginVersion=1.0.0
// build.gradle.kts
plugins {
id("com.example.hello")
}
resolutionStrategy{} 插件解析策略
resolutionStrategy{}
允许你更新插件的版本,更改插件的 GAV。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.namespace == "com.example") {
useModule("com.example:sample-plugins:1.0.0")
}
}
}
repositories {
maven {
url = uri("./maven-repo")
}
gradlePluginPortal()
ivy {
url = uri("./ivy-repo")
}
}
}
注意:Maven/Ivy 插件仓库必须包含 plugin marker artifacts,保证能得到该插件的真实依赖地址。可通过 [[04.Gradle三方插件#java-gradle-plugin介绍]] 和 [[Gradle 和 Maven#Gradle插件之maven-publish]] 插件 publish 自动发布带 marker artifact。
dependencyResolutionManagement
Gradle 8.x 自定义插件
https://docs.gradle.org/current/userguide/custom_plugins.html
Kotlin DSL 版本
自定义插件步骤
build.gradle.kts
配置
- 通过
File → new → new Module→
创建一个 module,手动创建可能会失败不了src/main[/java/kotlink/groovy]
目录 - 配置
build.gradle.kts
,添加依赖和插件如下:- 引入
java-gradle-plugin
:会自动引入java-library
插件、添加gradleApi()
依赖;生成marker-artifact
信息,适配plugins{}
引入插件 - 引入
org.jetbrains.kotlin.jvm
插件,支持 kotlin 编写插件,不加上这个发布的 jar 包没有 kotlin 的类;可以直接在 java sourceSets 中编写代码 - 添加
maven-publish
插件,如果 apply 了java-gradle-plugin
插件会默认配置一个 MavenPublication
- 引入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
plugins {
id("java-gradle-plugin") //会自动引入java-library、gradleApi()
id("org.jetbrains.kotlin.jvm") //支持kotlin编写插件,不加上这个发布的jar包没有kotlin的类
id("maven-publish") //maven发布插件
}
// GAV
val myGroup = "me.hacket.plugin"
val myArtifactsId = "myArtifactsId"
val myVersion = "1.0.1"
val pluginId = "me.hacket.myPluginId"
gradlePlugin {
plugins {
create("myJavaGradlePluginName") { // 这个name随意
group = myGroup
version = myVersion
id = pluginId
implementationClass = "me.hacket.mygradleplugin.GreetingPlugin"
}
}
}
// 默认, 不建议
//publishing {
// repositories {
// maven {
// url = uri("$rootDir/custom_plugin_repo")
// }
// }
//}
// 自定义
publishing {
publications {
create<MavenPublication>("MyMaven") {
// 这个name随意,到时是出现在publishing group作为task的名字一部分
// MavenPublication是一个类型,这里是创建一个MavenPublication类型的对象
groupId = myGroup
// artifactId = myArtifactsId
version = myVersion
from(components["java"])
}
}
repositories {
maven {
url = uri("$rootDir/custom_plugin_repo")
}
}
}
dependencies {
compileOnly("com.android.tools.build:gradle:8.1.2")
// https://docs.gradle.org/nightly/userguide/test_kit.html#sub:test-kit-automatic-classpath-injection
testImplementation(gradleTestKit())
testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
tasks.named<Test>("test") {
useJUnitPlatform()
}
java-gradle-plugin
插件中 gradlePlugin
配置的 id
就是我们在 plugins{}
中的 id 引入的值
定义插件
- 目录结构
 |
- extensions 代码,带子扩展的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
open class MyPluginExtension(project: Project) {
var title: String? = null
var chapter: Int? = 0
var subExtension: SubExtension? = null
init {
subExtension = project.extensions.create("sub", SubExtension::class.java)
}
fun string(): String {
return "title: $title, chapter: $chapter, author: ${subExtension?.author}"
}
}
open class SubExtension {
var author: String? = null
}
- plugin 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class GreetingPlugin : Plugin<Project> {
companion object {
const val prefix = "----------------------->>>>>>>>>>>>>>>>>>>>>>>>> "
}
override fun apply(project: Project) {
// Add the 'greeting' extension object
println("$prefix Hello from GreetingPlugin.")
val extension: MyPluginExtension? = project.extensions.create(
"myPluginExtension",
MyPluginExtension::class.java
)
project.afterEvaluate {
println("$prefix afterEvaluate Extension: ${extension?.string()}")
}
// 定义一个task
// project.tasks.create
project.task("hello") {
it.group = "test"
it.doLast {
println("$prefix doLast extension2: ${extension?.string()}")
}
}
}
}
创建 task
[[Gradle Task基础#Task 的创建及配置]]
发布插件
发布到 mavenLocal 或本地的一个 repo
- 配置好 maven-publish
./gradlew publish
发布到 Gradle Plugin Portal
官方文档:Publishing Plugins to the Gradle Plugin Portal
- 在 Gradle Plugin Portal 创建账户
- Create an account
- Create an API key
- Add your API key to your Gradle configuration
- 添加插件
1
2
3
plugins {
id("com.gradle.plugin-publish") version "1.2.1"
}
- 配置插件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
gradlePlugin {
// ...
plugins {
create("greetingsPlugin") {
id = "<your plugin identifier>"
displayName = "<short displayable name for plugin>"
description = "<human-readable description of what your plugin is about>"
tags = listOf("tags", "for", "your", "plugins")
implementationClass = "<your plugin class>"
}
}
}
// 对应https://plugins.gradle.org/plugin/org.ysb33r.gradletest?_gl=1*v49j7n*_ga*OTMzMDk2MTguMTY5NTU0NzM1Nw..*_ga_7W7NC6YNPT*MTcwOTM2NjAyMC4zNS4xLjE3MDkzNjg5OTIuNjAuMC4w
- 发布
1
2
3
4
5
/gradlew publishPlugins
./gradlew publishPlugins --validate-only
./gradlew publishPlugins -Pgradle.publish.key=<key> -Pgradle.publish.secret=<secret>
使用
- 在根目录
build.gradle.kts
引入插件
1
2
3
4
5
6
7
8
9
plugins {
id("com.android.application") version "8.2.0" apply false
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
id("org.jetbrains.kotlin.jvm") version "1.8.10" apply false
id("com.android.library") version "8.2.0" apply false
// 先声明插件, 后面才能使用,否则resolution eachPlugin Strategy找不到version
id("me.hacket.myPluginId") version "1.0.1" apply false
}
- app
build.gradle.kts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("me.hacket.myPluginId")
}
myPluginExtension {
title = "Hello from myPluginExtension"
chapter = 2
sub {
author = "Hacket"
}
}
android {
// ...
}
- 执行 task
1
./gradlew hello
- 注意适配 plugins [[Gradle自定义插件总结#适配plugins方式]]
示例代码
https://github.com/hacket/GradlePluginDemos/tree/master/GradlePlugin8.0-Kotlin-New