之前写了个 gradle 脚本,输出 Android 模块最终的依赖,最近发现在多个项目使用时需要将脚本拷来拷去,有点费事儿, 于是打算将其发布为 gradle plugin,使用的时候直接依赖 plugin 即可。
gradle plugin 发布配置
在插件模块的 build.gradle 里配置:
plugins {
id 'kotlin'
id 'com.gradle.plugin-publish' version '2.0.0'
}
kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
dependencies {
implementation gradleApi()
implementation "com.android.tools.build:gradle:8.13.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib:2.2.20"
implementation "org.jetbrains.kotlin:kotlin-reflect:2.2.20"
}
group = 'io.github.y4n9b0'
version = '1.0.0'
gradlePlugin {
website = 'https://github.com/y4n9b0/GradlePlugin'
vcsUrl = 'https://github.com/y4n9b0/GradlePlugin'
plugins {
flatDeps {
// 在 app 模块需要通过 id 引用这个插件
id = 'io.github.y4n9b0.flatDeps'
// 实现这个插件的类的路径
implementationClass = 'y4n9b0.flatDeps.FlatDepsPlugin'
displayName = 'Gradle flatDeps plugin'
description = 'Gradle plugin to flat dependencies!'
tags.set(['dependence', 'flat'])
}
}
}
gradle plugin publish task & artifacts
配置好 plugin gradle 脚本并 sync 后,就可以在 gradle task 面板上看到 plugin 发布任务。
这里以本地发布测试为例,发布 gradle plugin 任务:
./gradlew clean :flatDeps:publishToMavenLocal
生成的产物如下:
|____flatDeps
| |____io.github.y4n9b0.flatDeps.gradle.plugin
| | |____1.0.0
| | | |____io.github.y4n9b0.flatDeps.gradle.plugin-1.0.0.pom
| | |____maven-metadata-local.xml
| |____1.0.0
| | |____flatDeps-1.0.0-javadoc.jar
| | |____flatDeps-1.0.0-sources.jar
| | |____flatDeps-1.0.0.jar
| | |____flatDeps-1.0.0.module
| | |____flatDeps-1.0.0.pom
| |____maven-metadata-local.xml
gradle plugin 发布一般包含两个任务,一个是 main task,跟普通的 library 发布类似,是插件实现本体(真正的代码);
另一个是 marker task,插件 marker artifact(标记文件)。
上面的发布任务./gradlew clean :flatDeps:publishToMavenLocal汇总了这两个任务(严格地说,是汇总了所有的 mavenLocal task,只是这里碰巧只有这两个 mavenLocal task)
gradle plugin main task & artifacts
./gradlew clean :flatDeps:publishPluginMavenPublicationToMavenLocal
|____flatDeps
| |____1.0.0
| | |____flatDeps-1.0.0-javadoc.jar
| | |____flatDeps-1.0.0-sources.jar
| | |____flatDeps-1.0.0.jar
| | |____flatDeps-1.0.0.module
| | |____flatDeps-1.0.0.pom
| |____maven-metadata-local.xml
gradle plugin marker task & artifacts
./gradlew clean :flatDeps:publishFlatDepsPluginMarkerMavenPublicationToMavenLocal
|____flatDeps
| |____io.github.y4n9b0.flatDeps.gradle.plugin
| | |____1.0.0
| | | |____io.github.y4n9b0.flatDeps.gradle.plugin-1.0.0.pom
| | |____maven-metadata-local.xml
marker 任务名中的 FlatDeps 是插件 build.gradle 里配置的 plugin 变量:
gradlePlugin {
plugins {
flatDeps {
}
}
}
marker 任务是用来标记插件本体的,实际真正生效的还是插件本体。
是不是有种脱裤子放屁多此一举的感觉?
这是因为我们引用插件的方式跟引用 library 的方式不一致:
// 引用 plugin
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'io.github.y4n9b0.flatDeps' version '1.0.0'
}
// 引用 library
dependencies {
implementation fileTree(dir: "libs", include: ['*.jar', '*.aar'])
implementation 'androidx.core:core-ktx:1.17.0'
}
插件的引用和解析机制和 library 不一致,需要先寻找 marker,再根据 marker pom 文件解析找到插件本体进行使用。
当然了,为什么要把插件引用设计成这屌逼样,就不得而知了😳。
发布到 jitpack
一开始打算把插件托管到 jitpack,使用 jitpack 有个好处是发布任务可以自动和 github 项目的 release tag 关联, 还有一个好处是之前也用过 jitpack 发布 library,相对比较熟悉,毕竟连水都是选择阻力最小的路径流淌。
配置好相关脚本后,发布到 jitpack 后发现确实可以生成两个任务的产物,但是路径不太对,导致最终引用的时候提示找不到插件。在网上也搜索了相关文章,比如 Plugins published without Gradle Plugin Portal,试过、不行、遂弃!
发布到 gradle plugin portal
gradle plugin portal 是 gradle 官方的 plugin 托管中心,配置好任务后执行如下命令即可发布:
./gradlew clean :flatDeps:publishPlugins
一些注意事项
-
gradle plugin portal 需要注册,我使用的是 github 账号关联注册,这里有个坑,关联注册的时候要小心从 github 自动导入的邮箱,导入的邮箱并不是在 github 使用的真实邮箱,而是类似 xxx@users.noreply-github.com,该邮箱无法生成 API key,也无法发布插件,注册之前一定要先改为真实的邮箱。而我就比较悲催地没在意,最后只能找官方支持更改邮箱,BTW 官方支持邮箱😊:plugin-portal-support@gradle.com
- gradle plugin portal 发布插件需要使用 API key,在个人 profile 页面生成 API key 后将其配置在
$USER_HOME/.gradle/gradle.properties供登录使用:gradle.publish.key=${your gradle publish key} gradle.publish.secret=${your gradle publish secret} - 发布插件有许多准则要求,详见 https://plugins.gradle.org/docs/publish-plugin,说几点主要的:
- 一个新插件首次发布后需要官方批准通过,一般等待时间是1-2个工作日,通过的插件后续发布不再需要批准。
- 插件必须能关联到作者,插件发布必须要配置对应的项目公开地址,大概率审批的时候会去阅读项目源码。
- github 项目的插件使用 io.github 开头,不能使用 com.github(已废弃)。
- 插件要有具体的功能,不能是 hello world 之类的,而且功能必须要普罗大众,个人特定使用的插件应该发布在私有仓库。