ooy*_*478 12 gradle kotlin gradle-kotlin-dsl gradle-multi-project-build kotlin-gradle-plugin
我正在为 JVM 设置一个基于 Kotlin 的多模块 Gradle 项目。由于根项目不包含任何代码,因此 Kotlin 插件只能应用于子项目。
build.gradle.kts(根项目)
plugins {
kotlin("jvm") version "1.6.20" apply false
}
subprojects {
apply(plugin = "kotlin")
group = "com.example"
repositories {
mavenCentral()
}
dependencies {}
kotlin {
jvmToolchain {
check(this is JavaToolchainSpec)
languageVersion.set(JavaLanguageVersion.of(11))
}
}
}
Run Code Online (Sandbox Code Playgroud)
尝试设置工具链会导致构建在kotlin {...}扩展处失败:
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public fun DependencyHandler.kotlin(module: String, version: String? = ...): Any defined in org.gradle.kotlin.dsl
public fun PluginDependenciesSpec.kotlin(module: String): PluginDependencySpec defined in org.gradle.kotlin.dsl
Run Code Online (Sandbox Code Playgroud)
如果我将扩展定义复制到每个子项目构建脚本中,效果很好,但为什么它在主脚本中不可用?
aSe*_*emy 28
这是我最喜欢在 Gradle 中修复的事情之一,并且真正展示了可能的灵活性(以及演示为什么 Gradle 可能很复杂!)
首先,我将提供一些有关subprojects {}DSL 的背景信息,然后我将展示如何修复脚本,最后我将展示与 buildSrc 约定插件共享构建逻辑的最佳方法。(尽管它是最后的,我真的推荐使用 buildSrc!)
使用allprojects {}andsubprojects {}确实很常见,我见过很多。它更类似于 Maven 的工作方式,其中所有配置都在“父”构建文件中定义。然而 Gradle 不推荐这样做。
[A],令人沮丧的是,在子项目之间共享构建逻辑的方法是通过
subprojects {}和allprojects {}DSL 构造进行跨项目配置。
(这可能很常见,因为它很容易理解 - 它使 Gradle 的工作方式更像 Maven,因此每个项目都从一个父项目继承。但是 Gradle 是为组合而设计的。进一步阅读:组合优于继承:Gradle 与 Maven)
您看到的错误基本上是因为您尚未应用 Kotlin 插件。
plugins {
kotlin("jvm") version "1.6.20" apply false // <- Kotlin DSL won't be loaded
}
Run Code Online (Sandbox Code Playgroud)
配置kotlin { }块是一个非常有用的扩展函数,在应用 Kotlin 插件时加载。它看起来是这样的:
/**
* Configures the [kotlin][org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension] extension.
*/
fun org.gradle.api.Project.`kotlin`(configure: Action<org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension>): Unit =
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("kotlin", configure)
// (note: this is generated code)
Run Code Online (Sandbox Code Playgroud)
所以如果我们没有扩展函数,我们可以configure直接调用,从而配置Kotlin扩展。
subprojects {
// this is the traditional Gradle way of configuring extensions,
// and what the `kotlin { }` helper function will call.
configure<org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension> {
jvmToolchain {
check(this is JavaToolchainSpec)
languageVersion.set(JavaLanguageVersion.of(11))
}
}
// without the Kotlin Gradle plugin, this helper function isn't available
// kotlin {
// jvmToolchain {
// check(this is JavaToolchainSpec)
// languageVersion.set(JavaLanguageVersion.of(11))
// }
// }
}
Run Code Online (Sandbox Code Playgroud)
然而,即使这有效,使用起来也subprojects {}有问题。有一个更好的方法...
buildSrc基本上,它是一个独立的 Gradle 项目,我们可以在主项目的构建脚本中使用它的输出。因此,我们可以编写自己的自定义 Gradle 插件,定义约定,我们可以有选择地将其应用于“主”构建中的任何子项目。
(这是 Gradle 和 Maven 之间的主要区别。在 Gradle 中,子项目可以由任意数量的插件配置。在 Maven 中,只有一个父项目。组合与继承!)
Gradle 文档有关于设置约定插件的完整指南,因此我将在这里简要总结一下解决方案。
./buildSrc创建一个buildSrc在项目根目录中命名的目录。
因为buildSrc是一个独立项目,所以像通常的项目一样创建一个./buildSrc/build.gradle.kts和文件。./buildSrc/settings.gradle.kts
在./buildSrc/build.gradle.kts,
kotlin-dsl插件// ./buildSrc/build.gradle.kts
plugins {
`kotlin-dsl` // this will create our Gradle convention plugins
// don't add the Kotlin JVM plugin
// kotlin("jvm") version embeddedKotlinVersion
// Why? It's a long story, but Gradle uses an embedded version of Kotlin,
// (which is provided by the `kotlin-dsl` plugin)
// which means importing an external version _might_ cause issues
// It's annoying but not important. The Kotlin plugin version below,
// in dependencies { }, will be used for building our 'main' project.
// https://github.com/gradle/gradle/issues/16345
}
val kotlinVersion = "1.9.21"
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
}
Run Code Online (Sandbox Code Playgroud)
请注意,我使用了Kotlin Gradle 插件的Maven 工件存储库坐标,而不是插件 ID!
如果您愿意,您还可以添加其他依赖项./buildSrc/build.gradle.kts。如果您想在构建脚本中解析 JSON,请添加对 JSON 解析器的依赖项,例如kotlinx-serialization.
创建可应用于任何 Kotlin JVM 子项目的 Kotlin JVM 约定。
// ./buildSrc/build.gradle.kts
plugins {
`kotlin-dsl` // this will create our Gradle convention plugins
// don't add the Kotlin JVM plugin
// kotlin("jvm") version embeddedKotlinVersion
// Why? It's a long story, but Gradle uses an embedded version of Kotlin,
// (which is provided by the `kotlin-dsl` plugin)
// which means importing an external version _might_ cause issues
// It's annoying but not important. The Kotlin plugin version below,
// in dependencies { }, will be used for building our 'main' project.
// https://github.com/gradle/gradle/issues/16345
}
val kotlinVersion = "1.9.21"
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
}
Run Code Online (Sandbox Code Playgroud)
不要忘记添加package声明!我有几次忘记了它,并且它导致了难以找出的错误。
就像 Gradle 插件有 ID 一样,我们的约定插件也有 ID。它是包名+前面的位.gradle.kts。所以在我们的例子中 ID 是my.project.convention.kotlin-jvm
我们可以像常规 Gradle 插件一样应用它......
// ./buildSrc/src/main/kotlin/my/project/convention/kotlin-jvm.gradle.kts
package my.project.convention
plugins {
kotlin("jvm") // don't include a version - that's provided by ./buildSrc/build.gradle.kts dependencies
}
dependencies {
// you can define default dependencies, if desired
// testImplementation(kotlin("test"))
}
// you can set defaults for subprojects that apply this plugin
// (which they can override if necessary)
kotlin {
jvmToolchain(11)
}
Run Code Online (Sandbox Code Playgroud)
(约定插件还可以导入其他约定插件,使用id("..."))
另外,由于我们使用 Kotlin,所以还有更好的方法。您知道如何包含 Gradle 插件,例如java和java-library。我们可以用同样的方式导入我们的约定插件!
// ./subprojects/my-project/build.gradle.kts
plugins {
id("my.project.convention.kotlin-jvm")
}
Run Code Online (Sandbox Code Playgroud)
请注意插件 ID 周围的反引号 - 由于连字符,因此需要它们。
(警告:这种非id("...")方法在内部不起作用buildSrc,仅在主项目中起作用)
现在根./build.gradle.kts可以保持真正干净整洁 - 它只需要定义项目的组和版本。
因为我们使用的是约定插件而不是毯子subprojects,所以每个子项目都可以专门化,并且只导入它需要的约定插件,而无需重复。
buildSrc主项目之间共享存储库buildSrc通常您希望在主项目之间共享存储库。因为 Gradle 插件不是专门针对项目的,所以我们可以为任何东西编写插件,包括settings.gradle.kts!
我所做的就是创建一个包含我想要使用的所有存储库的文件......
// ./subprojects/my-project/build.gradle.kts
plugins {
// id("my.project.convention.kotlin-jvm")
my.project.convention.`kotlin-jvm` // this works just like id("...") does
}
Run Code Online (Sandbox Code Playgroud)
(名称repositories.settings.gradle.kts并不重要 - 但命名它*.settings.gradle.kts应该意味着 IntelliJ 提供建议,但是目前存在错误。)
然后我可以将其作为插件导入到其他settings.gradle.kts文件中,就像您将 Kotlin JVM 插件应用到子项目一样。
// ./buildSrc/repositories.settings.gradle.kts
@Suppress("UnstableApiUsage") // centralised repository definitions are incubating
dependencyResolutionManagement {
repositories {
mavenCentral()
jitpack()
gradlePluginPortal()
}
pluginManagement {
repositories {
jitpack()
gradlePluginPortal()
mavenCentral()
}
}
}
fun RepositoryHandler.jitpack() {
maven("https://jitpack.io")
}
Run Code Online (Sandbox Code Playgroud)
// ./buildSrc/settings.gradle.kts
apply(from = "./repositories.settings.gradle.kts")
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8446 次 |
| 最近记录: |