文章

Gradle自定义插件总结

Gradle自定义插件总结

Gradle 自定义插件总结

Gradle plugin build.gradle[.kts] 配置

src 下对应的 java 目录没有被 AS 识别出来

问题:AGP8.1.3,AS(2024.3 最新版本)

build.gradle.kts 文件上方提示: standalone script under build root isn’t highlighted as standalone

![600](https://raw.githubusercontent.com/hacket/ObsidianOSS/master/obsidian/obsidianobsidianobsidian202403020108681.png)
![image.png600](https://raw.githubusercontent.com/hacket/ObsidianOSS/master/obsidian/obsidian202403020108681.png)

src/main/java 在 AS 中也没有变颜色

![image.png300](https://raw.githubusercontent.com/hacket/ObsidianOSS/master/obsidian/obsidian202403020036540.png)

问题分析:如果是手动创建文件夹,然后创建 build.gradle.kts,AS 识别不了,可能是 AS 的 bug?

解决:通过 File→New→New Module 来创建 module,这样就可以解决了

publish 的 jar 包没有包括 classes

  • 用对应语言写插件,就放到对应 sourceSets 文件夹下去(与 jar 包中是否有 classes 没关系)
    • java 编写的就放`main/java
    • groovy 编写的就放 main/groovy
    • kotlin 编写的就是 main/kotlin

image.png

  • 解决
    • build.gradle(.kts) 中需要添加 groovy 插件: apply plugin: 'groovy',即使放在 java 目录也是可以的
    • 如果在 java 目录写 kotlin 代码,需要引入 id("org.jetbrains.kotlin.jvm") 否则发布的 jar 包没有 kotlin 类的,放在 kotlin 目录也是可以的

使用 java-gradle-plugin 插件

参考:[[04.Gradle三方插件#java-gradle-plugin]]

extension 相关

extension 定义相关

extension 类需要可继承的

  • 如果是 kotlin class,需要 open 修饰
  • 一般用 abstract class

extension 定义需要 create 后再使用

否则会报错:

Could not find method dingPgyerConfig() for arguments [build_6l85nymtuc9s9srndt732is8y$_run_closure1@29e705c9] on project ':Google' of type org.gradle.api.Project.

自定义的 extension 定义获取不到值,在 afterEvaluate 后获取

create 之后立即使用,这个时候 bean 里面是默认值,也就是说 build.gradle 中的信息还没有加载进去,需要在整个 gradle 配置完成之后 bean 中才会填充上相应的值。

需要在 project.afterEvaluate{} 中获取

1
2
3
4
5
6
7
8
final DemoExtension extension = project.getExtensions().create("demoConfig", DemoExtension.class);
System.out.println(TAG + "extension: " + extension); // 这里获取不到值
project.afterEvaluate(new Action<Project>() {
    @Override
    public void execute(Project project) {
        System.out.println(TAG + "extension afterEvaluate: " + extension); // 这里可以获取
    }
});

获取已创建的 extention

findByName
1
DingTalkExtension dingTalkExtension = target.getExtensions().findByName("dingPgyerConfig")
findByType
1
2
3
4
5
6
7
8
9
DingTalkExtension config = project.getExtensions().findByType(DingTalkExtension.class)

subprojects {  
	afterEvaluate {  
		extensions.findByType<KotlinProjectExtension>()?.apply {  
			jvmToolchain(11)  
		}  
	}  
}

extention 嵌套

extension 嵌套,其实就是嵌套个内部类

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  
}

使用:

1
2
3
4
5
6
7
myPluginExtension {
    title = "Hello from myPluginExtension"
    chapter = 2
    sub {
        author = "Hacket"
    }
}

获取 App 和 Lib 的 extention

1
2
3
4
5
6
7
8
9
10
11
// 获取app的Extention
static AppExtension getConfig(Project project) {
    AppExtension config = project.getExtensions().findByType(AppExtension.class)
    return config
}

// 获取lib的Extention
static LibraryExtension getConfig(Project project) {
    LibraryExtension config = project.getExtensions().findByType(LibraryExtension.class)
    return config
}

闭包类型是否应用插件

1
2
3
4
5
6
7
class DingTalkExtension {
    public Closure<Boolean> groovyEnableByVariant
    boolean isEnable(String variantName) {
        if (groovyEnableByVariant == null) return true
        return groovyEnableByVariant.call(variantName)
    }
}

使用:

1
2
3
4
5
6
7
apply plugin: 'dingPgyerPlugin'
dingPgyerConfig {
    pgyerApiKey = "[google]70885395bcdfd5ebdb72a5856c95596c"
    groovyEnableByVariant = { variantName ->
        variantName.toLowerCase().contains("release") // release才开启
    }
}

判断是否有某个插件

判断是否应用了 com.android.application 插件,代码来自 walle

1
2
3
4
5
6
7
8
9
10
11
12
class GradlePlugin implements org.gradle.api.Plugin<Project> {
    public static final String sPluginExtensionName = "walle";
    @Override
    void apply(Project project) {
        // 旧的写法
//        if (!project.plugins.hasPlugin("com.android.application")) {
//            throw new ProjectConfigurationException("Plugin requires the 'com.android.application' plugin to be configured.", null)
//        }
        if (!Project.getPluginManager().hasPlugin("com.android.application")) {
            throw new ProjectConfigurationException("Plugin requires the 'com.android.application' plugin to be configured.", null)
	}
}

Task 相关

Configuration Task 和 task 的输入输出

  • 方式 1:register(xx, configuration)
1
2
3
4
5
6
7
8
9
10
11
12
13
val pgyerTaskName = Constants.TASK_UPLOAD_PGYER + variantName  
val uploadPgyerTaskProvider = target.tasks.register(  
	Constants.TASK_UPLOAD_PGYER + variantName,  
	PgyUploadApkTask::class.java  
) {  
	// Configuration会执行  

	// 输入  
	// PgyUploadApkTask依赖于variant.outputs中的第一个  
	it.apkFileProperty.set(variant.outputs.first().outputFile)  
	// 输出  
	it.outputFilProperty.set(target.layout.buildDirectory.file("output.txt"))  
}
  • 方式 2:configure { }
1
2
3
4
5
6
7
8
val dingTalkTaskProvider = target.tasks.register(  
    dingTalkTaskName,  
    SendBuildResultToDingTalkTask::class.java  
)  
// 配置  
dingTalkTaskProvider.configure {  
    it.apkFileProperty.set(variant.outputs.first().outputFile)  
}

插件中的 http 请求时老是 timeout

通过 OkHttp 请求,老是 timeout

  • 原因:网络问题
  • 解决:把全局代理关掉;抓包软件关掉

Gradle 插件中的 task 要用子线程

子线程配合 CountDownLatch

  • 可以开子线程,在 Task 中可以用 CountDownLatch 等待子线程执行完毕,不然你的 task 可能就执行完毕了

Retrofit+RxJava 做网络请求

如做一个上传蒲公英的插件,上传蒲公英需要 3 个接口才能完成上传:

  1. key 上传文件存储标识唯一 key 通过该接口,开发者可以获取预上传 url 和相关的签名参数 POST https://www.pgyer.com/apiv2/app/getCOSToken
  2. 上传文件到第上一步获取的 URL POST 在这一步中上传 App 成功后,App 会自动进入服务器后台队列继续后续的发布流程。所以,在这一步中 App 上传完成后,并不代表 App 已经完成发布。一般来说,一般 1 分钟以内就能完成发布。要检查是否发布完成,请调用下一步中的 API。
  3. 检测应用是否发布完成,并获取发布应用的信息 GET https://www.pgyer.com/apiv2/app/buildInfo

如果在创建 Retrofit 时的 CallAdapterFactory 是 create():

1
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())

会出现 task 执行完毕了,上传 APK 并没有完成
原因是 RxJava3CallAdapterFactory.create() 是异步,导致 task 执行完毕了
解决:用 RxJava3CallAdapterFactory.createSynchronous() 同步的即可。

封装第三方插件

封装 maven-publish 三方插件

maven-publish.md

根据 Variant(productFlavor+buildType) 引用 Gradle 插件(按需加载插件)

背景:只想在某一些 buildType 或者 flavor 中应用这个插件。
分析:这个问题的根本原因在于 Variant(buildType + flavor) 不是一个 Gradle 概念,而是源于 Android Gralde Plugin(AGP)的多渠道配置。所以从 Gradle 平台的角度很难向上提供便捷的支持:一个工程内的插件引入是全局的、平台性的,不以某一个插件的意志(AGP)而改变下层平台的机制。

案例:
mashi 的 preview 包,配置了混淆,配置了 Firebase 插件,估计 Firebase 插件是根据是否设置了混淆来自动执行 uploadCrashlyticsMappingFileDevPreview,但 preview 是测试包,并不需要上传 mapping 到 Firebase,测试包我们用的 bugly。所以就出现了打 preview 包的时候也执行了该 task,而打包这台机器又 404 了,所以就上传超时了,gg 了。

1
2
Execution failed for task ':app:uploadCrashlyticsMappingFileDevPreview'.
11:55:07 > org.apache.http.conn.HttpHostConnectException: Connect to firebasecrashlyticssymbols.googleapis.com:443 [firebasecrashlyticssymbols.googleapis.com/142.251.43.10] failed: Operation timed out (Connection timed out)

根据 Variant 去 apply 插件 – 无效

1
2
3
4
5
6
7
8
9
10
11
flavorDimensions += "server"
productFlavors {
    // ...
    create("production") {
        dimension = "server"
        applicationIdSuffix = ".production"
        versionNameSuffix = "-production"
        versionCode = 2
        apply("xxx") // 无效
    }
}
  • create("…"){} 的闭包上下文是 this:ApplicationProductFlavor
  • apply(…) 则为挂载于 PluginAware 的一个扩展方法(以 build.gradle.kts),作用于整个、Project,这样一来你的插件还是会被全局引入。

同样的经典误区 1:

1
2
3
4
5
6
7
8
9
10
11
create("production") {
    dimension = "server"
    applicationIdSuffix = ".production"
    versionNameSuffix = "-production"
    versionCode = 2
    packagingOptions {
        jniLibs {
            excludes.add("...")
        }
    }
}
  • packageingOptions 是 CommonExtension 接口的一个方法,并不属于 ApplicationProductFlavor,也即它是为整个 Android Gradle Plugin 进行设置,而不是单一 flavor,如果你在每个 flavor 中都进行不同的设置,最后一次的设置会覆盖前面所有的

同样的经典误区 2:自定义插件根据 variant 去 apply 第三方插件

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
class ThirdManagerPlugin : Plugin<Project> {
    companion object {
        private const val EXTENSION_NAME = "thirdConfig"
        private const val ANDROID_EXTENSION_NAME = "android"
    }
    override fun apply(target: Project) {
        val container = target.extensions
        container.create(EXTENSION_NAME, ThridManagerExtension::class.java)
        target.afterEvaluate {
            val extension = container.findByName(EXTENSION_NAME)
            val androidExtension = container.findByName(ANDROID_EXTENSION_NAME) as? AppExtension
                ?: return@afterEvaluate
            val variants = androidExtension.applicationVariants
            variants.all { variant ->
                val variantName = variant.baseName.capitalize()
                when {
                    variantName.toLowerCase().contains("debug") -> {
                    }
                    variantName.toLowerCase().contains("release") -> {
                        val map = mapOf("plugin" to "me-hacket-helloworld")
                        target.apply(map) // 无效,config后,release的variant也调用了apply,也就全局apply了,插件就生效了
                    }
                }
            }
        }
    }
}

根据命令去 apply 插件 – 部分有效

1
2
3
4
5
6
7
8
9
10
11
if (gradle.startParameter.taskRequests.toString().contains("production")) {
    apply(...)
}

// 在root build.gradle下设置ext,其他module可以直接使用
ext {
    // 命名为develop而不是debug, 规避ext可能存在重命名的风险
    develop = gradle.startParameter.taskNames.any { it.toLowerCase().contains('debug') }
    test = gradle.startParameter.taskNames.any { it.toLowerCase().contains('debug') || it.toLowerCase().contains('preview') }
    println("develop=$develop , test=${test}")
}

输入:

1
./gradlew clean assembleDebug

输出:

1
2
taskNames=[clean, assembleDebug]
taskRequests=[DefaultTaskExecutionRequest{args=[clean, assembleDebug],projectPath='null'}]
  • 如果是 ./gradlew clean assembleD 那就不行了

禁用对应 Variant 的插件 Task – 有效 (总是引用插件,但禁用掉不需要的 Task)

whenTaskAdded{…} 是 Gradle 平台的 API,它才不管你上层注册的 Task 分不分 Variant,它只管把所有注册后且确定会添加进运行图(是个有向无环图 DAG)的 Task 在这里提供一个回调的时机给开发者。与此同时,几乎所有的 Android 生态协同插件都会基于 Variant 的名字去给自己 Task 命名(如果它需要是一个 VariantAware Task 的话),例如 “UploadArchiveWithLogForProductionRelease”。这个不成文的规则给了我们字符串匹配的机会,也即你看到的下面代码:

1
2
3
4
5
6
7
8
9
10
plugins { id("com.example.ua") }
// or apply("com.example.ua")
// ...
tasks.whenTaskAdded {
    if (name.contains("production", true)
            && name.contains("release", true)
            && name.contains("UploadArchive", true)) {
        enabled = false
    }
}
  • mashi 案例:开发包 测试包开启 bugly,release 包开启 Firebase Crashlytics
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
tasks.whenTaskAdded { task ->
    def taskName = task.name
    if (taskName.contains('SymtabFile')) {
        // release包不开启bugly,开发||测试包开启
        if (task.name.contains('uploadDevPreviewSymtabFile')) {
            task.enabled = true
            println("tasks.whenTaskAdded task(${task.name}) enabled=${task.enabled}")
        } else {
            task.enabled = false
            println("tasks.whenTaskAdded task(${task.name}) enabled=${task.enabled}")
        }
    }
    if (taskName.contains('CrashlyticsMappingFile')) {
        // release包开启Firebase,开发||测试包不开启
        if (task.name.contains('uploadCrashlyticsMappingFileProductRelease')
                || task.name.contains('injectCrashlyticsMappingFileIdProductRelease')
        ) {
            task.enabled = true
            println("tasks.whenTaskAdded task(${task.name}) enabled=${task.enabled}")
        } else {
            task.enabled = false
            println("tasks.whenTaskAdded task(${task.name}) enabled=${task.enabled}")
        }
    }
}

在 Task 注册时拦截 – 可以,高效

配置 Extension:

1
2
3
4
5
6
7
8
9
10
11
12
13
class DingTalkExtension { 
    public String pgyerApiKey
    public Closure<Boolean> groovyEnableByVariant
    // For Gradle Groovy DSL
    void enableByVariant(Closure<Boolean> selector) {
        groovyEnableByVariant = selector.dehydrate()
    }
    boolean isEnable(String variantName) {
        if (groovyEnableByVariant == null) return true
        return groovyEnableByVariant.call(variantName)
    }
    // ...
}

插件不生成对应的 task:

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
class SendApkToDingTalkPlugin implements Plugin<Project> { 
    @Override
    void apply(Project target) {
        target.afterEvaluate {
            DingTalkExtension dingTalkExtension2 = target.getExtensions().findByName("dingPgyerConfig")

            ExtensionContainer container = target.getExtensions()
            AppExtension androidExtension = container.findByName(Constants.ANDROID_EXTENSION_NAME)
            DomainObjectSet<ApplicationVariant> variants = androidExtension.getApplicationVariants()
            variants.all { ApplicationVariant variant ->
                String variantName = variant.baseName.capitalize()

                boolean isEnable = dingTalkExtension2.isEnable(variantName)
                if (!isEnable) {
                    return
                }

                // 创建蒲公英上传task
                def pgyerTask = target.tasks.create("${Constants.TASK_UPLOAD_PGYER}$variantName", PgyerUploadTask.class)
                pgyerTask.init(variant, target)

                // 创建发送钉钉消息task
                def dingTalkTask = target.tasks.create("${Constants.TASK_SEND_DINGTALK}$variantName", SendMsgToDingTalkTask.class)
                dingTalkTask.init(variant, target)

                def provider = variant.getAssembleProvider()
                provider.get().dependsOn(target.getTasks().findByName("clean"))
                pgyerTask.dependsOn(provider.get())
                dingTalkTask.dependsOn(pgyerTask)
            }
        }
    }
}

使用:

1
2
3
4
5
6
7
8
9
apply plugin: 'ding-pgyer'
dingPgyerConfig {
    pgyerApiKey = "[google]70885395bcdfd5ebdb72a5856c95596c"
    apiToken = "[google]632474cbdf7cce9a68cd8ece8d8aecc27eead23d888874aca73f435bd0014c83"
    atMobiles = ["[google]13510590884","[google]13168757017"]
    groovyEnableByVariant = { String variant ->
        variant.name.toLowerCase().contains("release") // release才开启
    }
}

Ref

遇到的问题

如何查看 Gradle 源码?

查看 build.gradle[.kts] 源码

groovy 集成

build.gradleCtrl + 鼠标左键 查看 dependencies,出现的 Project.class 看不到源码

![image.png400](https://raw.githubusercontent.com/hacket/ObsidianOSS/master/obsidian/20240304155809.png)

解决:

distributionUrl 中的 xxx-bin.zip 改成包含源码的 xxx.all.zip,再点击进去就能看到源码了

1
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip

kts 集成

点击 Index 就可以查看 Gradle 源码了

image.png

如何查看插件源码?

groovy

  1. 编译后,在 External Libraries 查看
1
2
compile gradleApi()
compile 'com.android.tools.build:gradle:8.1.3'
  1. 手动 Ctrl+ 左键点击 api 进去还是查看的是 class 文件,定位到该 class,然后手动打开该文件就是源码了

image.png

kts 方式

External Libraries 下可以看到有很多 Script,里面就是源码

![image.png400](https://raw.githubusercontent.com/hacket/ObsidianOSS/master/obsidian/20240304160912.png)

image.png

适配 plugins 方式

未发布到 gradle plugin portal 仓库的三方插件适配

plugins 的方式引入:因为插件并没有发布到 gradle plugin portal 仓库中,所以需要自己修改插件的解析规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pluginManagement {
    resolutionStrategy {
        // 定义id和插件库映射关系
        def modules = [
                'android-aspectjx'       : 'io.github.wurensen:gradle-android-plugin-aspectjx:2.1.0-SNAPSHOT',
        ]
        eachPlugin {
            println "id=" + requested.id.id
            def module = modules.get(requested.id.id)
            if (module != null) {
                useModule(module)
            }
        }
    }
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

maven-publish 适配 plugins

只使用了 maven-publish 插件,未使用 java-gradle-plugin 插件

先看看插件 module 的配置:

  • build.gradle.kts
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
plugins {  
    id("java")  
    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.0"  
  
val pluginId = "me.hacket.myPluginId"  
  
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(gradleApi()) //gradleApi()是一个函数,返回一个对象,不加上会找不到gradle相关Api  
    compileOnly("com.android.tools.build:gradle:8.1.2")  
}
  • 发布后
![image.png250](https://raw.githubusercontent.com/hacket/ObsidianOSS/master/obsidian/20240302153604.png)

再看看使用插件的项目配置

  • 根目录的 build.gradle.kts
1
2
3
4
5
6
7
8
9
// Top-level build file where you can add configuration options common to all sub-projects/modules.  
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.0" apply false  
  • app build.gradle.kts
1
2
3
4
5
6
plugins {  
    id("com.android.application")  
    id("org.jetbrains.kotlin.android")  
  
    id("me.hacket.myPluginId")  
}

构建后报错

问题分析:

这是由于 plugins 方式引入插件,通过 id 和 version 没找到可实现的依赖,找不到就报错了

解决 1(只修改插件使用方): app build.gradle.kts 不使用全局的 plugins 方式引入,改成 classpath 方式,其他地方不用变

1
2
3
4
5
6
7
8
9
10
11
12
buildscript {  
    dependencies{  
        classpath("me.hacket.plugin:myGradlePlugin:1.0.1")  
    }  
}  
// Top-level build file where you can add configuration options common to all sub-projects/modules.  
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 
}

解决 2:plugins 方式

去除 app build.gradle.kts 中的配置,还是使用 plugins 方式,但是需要手动将 plugins 方式通过 id 和 version 手动查找插件的依赖,在 setting.gradle.kts 配置

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
pluginManagement {  
    repositories {  
        google()  
        mavenCentral()  
        gradlePluginPortal()  
        maven {  
            url = uri("$rootDir/custom_plugin_repo")  
        }  
    }
    resolutionStrategy {  
        eachPlugin {  
            if (requested.id.name == "myPluginId") {  
                val module = "me.hacket.plugin:myGradlePlugin:${requested.version}"
                useModule(module)  
            }  
        }  
    }  
}  
dependencyResolutionManagement {  
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)  
    repositories {  
        google()  
        mavenCentral()  
    }  
}  
include(":app")  
include(":myGradlePlugin")  
//...

小结:

  • 只通过 maven-publish 插件发布,未和 java-gradle-plugin 插件配合,需要引入 javajava-library 插件
  • 需要手动添加依赖 compileOnly(gradleApi()),否则找不动 Gradle 相关 API
  • maven-publish 插件不会生成 Plugin Marker Artifacts,要使用 plugins 方式需要手动映射;手动映射通过 pluginManagement resolutionStrategy 来添加

maven-publish 配合 java-gradle-plugin 插件适配 plugins

插件 module build.gradle.kts 配置:

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
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 {  
    publications {  
  
        create<MavenPublication>("maven") {  
            // 这个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")  
}

生成的 publish 相关 task

![image.png400](https://raw.githubusercontent.com/hacket/ObsidianOSS/master/obsidian/20240302160425.png)

publish 生成的 jar 包:

![image.png400](https://raw.githubusercontent.com/hacket/ObsidianOSS/master/obsidian/20240302160524.png)

me.hacket.myPluginId.gradle.plugin-1.0.1.pom 文件的 dependency 就是插件实际的 GAV,符合 plugin.id:plugin.id.gradle.plugin:plugin.version 格式的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>  
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">  
  <modelVersion>4.0.0</modelVersion>  
  <groupId>me.hacket.myPluginId</groupId>  
  <artifactId>me.hacket.myPluginId.gradle.plugin</artifactId>  
  <version>1.0.1</version>  
  <packaging>pom</packaging>  
  <dependencies>  
    <dependency>  
      <groupId>me.hacket.plugin</groupId>  
      <artifactId>myGradlePlugin</artifactId>  
      <version>1.0.1</version>  
    </dependency>  
  </dependencies>  
</project>

通过 plugins 如何找到已有仓库的 gav

本文由作者按照 CC BY 4.0 进行授权