首页 > 解决方案 > 摇篮。块插件中的自定义函数{}

问题描述

我可以在我的自定义插件中编写一些函数,如 kotlin("jvm") 吗?

plugins {
    java
    kotlin("jvm") version "1.3.71"
}

我想在我的自定义插件中编写函数 myplugin("foo") 然后像使用它一样使用它

plugins {
    java
    kotlin("jvm") version "1.3.71"
    custom.plugin
    myplugin("foo")
}

我该怎么做?

标签: kotlingradlegradle-plugingradle-kotlin-dsl

解决方案


我认为该plugins块是某种宏表达式。它使用非常有限的上下文进行解析和预编译。可能,魔法发生在kotlin-dsl的某个地方。这可能是从插件中获取静态访问器和扩展函数以在 Kotlin 中工作的唯一方法。我从未在 Gradle 的文档中看到过这个过程,但让我解释一下我的想法。可能来自 Gradle 的一些聪明人会纠正我。

让我们看看一些第三方插件,比如Liquibase。它允许你在你的build.gradle.kts

liquibase {
    activities {
        register("name") {
            // Configure the activity here
        }
    }
}

想一想:在像 Kotlin 这样的静态编译语言中,为了使这种语法能够工作,应该有一个以类型命名的扩展名liquibaseProject因为它是thisevery 中的对象类型build.gradle.kts)在执行的 Gradle 的 VM 的类路径中可用构建脚本。

事实上,如果你点击它,你会看到类似的东西:

fun org.gradle.api.Project.`liquibase`(configure: org.liquibase.gradle.LiquibaseExtension.() -> Unit): Unit =
    (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("liquibase", configure)

但是看一下定义它的文件。就我而言,它是~/.gradle/caches/6.3/gradle-kotlin-dsl-accessors/cmljl3ridzazieb8fzn553oa8/cache/src/org/gradle/kotlin/dsl/Accessors39qcxru7gldpadn6lvh8lqs7b.kt。它绝对是一个自动生成的文件。文件树的上几层——~/.gradle/caches/6.3/gradle-kotlin-dsl-accessors/在我的例子中——有几十个类似的目录。我想,我曾经在 Gradle 6.3 中使用过的每个插件/版本都一个。这是Detekt插件的另一个:

fun org.gradle.api.Project.`detekt`(configure: io.gitlab.arturbosch.detekt.extensions.DetektExtension.() -> Unit): Unit =
    (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("detekt", configure)

因此,我们有一堆.kt文件定义了应用于项目的不同插件的所有扩展。这些文件显然是预缓存和预编译的,它们的内容在build.gradle.kts. 事实上,您可以classes在这些来源旁边找到目录。

源是根据应用插件的内容生成的。这可能是一项棘手的任务,其中包括一些魔术、反思和内省。有时这种魔法不起作用(由于 Groovy 的特性太复杂),然后你需要使用这个包中的一些糟糕的 DSL 。

它们是如何产生的?我认为没有其他办法,但

  1. build.script.kts使用嵌入式 Kotlin 编译器/词法分析器解析
  2. 提取所有plugins部分
  3. 编译它们,可能针对一些模拟(记住它Project还不可用:我们还没有执行它build.gradle.kts本身!)
  4. 解析Gradle Plugin 存储库中声明的插件(有一些细微差别来自settngs.gradle.kts
  5. 内省插件的工件
  6. 生成源
  7. 编译源码
  8. 将生成的类添加到脚本的类路径

plugins这里有个问题:编译块时可用的上下文(类路径、类、方法——随便叫什么)非常有限。实际上,尚未应用任何插件!因为,您知道,您正在解析应用插件的块。鸡、蛋,还有它们的问题,呵呵……</p>

因此,我们越来越接近您问题的答案,要在plugins块中提供自定义 DSL,您需要修改该类路径。这不是你的类路径build.gradle.kts,而是解析的 VM 的类路径build.gradle.kts。基本上,它是 Gradle 自己的类路径——捆绑在 Gradle 发行版中的所有类。

因此,可能在块中提供真正自定义 DSL 的唯一方法plugins是创建自定义 Gradle 发行版。

编辑:

确实,完全忘记测试buildSrc. 我在其中创建了一个文件PluginExtensions.kt,其中包含一个内容

inline val org.gradle.plugin.use.PluginDependenciesSpec.`jawa`: org.gradle.plugin.use.PluginDependencySpec
    get() = id("org.gradle.war") // Randomly picked

inline fun org.gradle.plugin.use.PluginDependenciesSpec.`jawa`(): org.gradle.plugin.use.PluginDependencySpec {
    return id("org.gradle.cunit") // Randomly picked
}

它似乎正在工作:

plugins {
    jawa
    jawa()
}

但是,这仅PluginExtensions.kt在默认包中时才有效。每当我将它放入子包中时,即使使用导入,扩展也无法识别:

在此处输入图像描述

魔法!


推荐阅读