为什么我不能在应用文件中使用 Gradle DSL

Why can't I use Gradle DSL in apply'ed files

在我们的团队中,我们有很多项目是用 Gradle 构建的。 Gradle 文件中的某些部分完全相同。例如,我们在所有项目中都使用 Java 11。所以我的想法是我可以将我的 build.gradle 文件拆分成一个公共部分,然后从中央存储库同步到每个 Gradle 项目,而项目特定部分保留在 build.gradle 中。

build.gradle:

plugins {
    id 'java'
    //...
}

apply from: "common.gradle.kts"

dependencies {
  // ...
}

common.gradle.kts

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(11)
    }
}

test {
    useJUnitPlatform()
}

现在我收到 Gradle

的错误消息
* Where:
Script '/Users/.../common.gradle.kts' line: 4

* What went wrong:
Script compilation errors:

  Line 04:     java {
               ^ Expression 'java' cannot be invoked as a function. The function 'invoke()' is not found

  Line 04:     java {
               ^ Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
                   public val PluginDependenciesSpec.java: PluginDependencySpec defined in org.gradle.kotlin.dsl

  Line 05:         toolchain {
                   ^ Unresolved reference: toolchain

  Line 06:             languageVersion = JavaLanguageVersion.of(11)
                       ^ Unresolved reference: languageVersion

  Line 09:     test {
               ^ Unresolved reference: test

  Line 10:         useJUnitPlatform()
                   ^ Unresolved reference: useJUnitPlatform

6 errors

对于某些配置,我找到了一个使用更通用的 API 的替代方案,虽然要找到相应的替代方案需要付出很多努力,但最终没有人能保证它们做的完全一样东西:

tasks.withType<JavaCompile> {
    options.release.set(11)
}

所以问题仍然存在:为什么我不能在我的外化 common.gradle.kts 中使用 DSL 函数 javatest

似乎它必须使用 Kotlin 脚本来做一些事情,至少如果我对我的外部化脚本也使用 Groovy,它就可以工作。

在您的 common.gradle.kts 中,java { } 生成了辅助 Kotlin DSL 函数。 Gradle 不知道 Kotlin DSL 助手,除非

  1. 它是构建的一部分(不使用 apply(from = "...")
  2. 已应用 java 插件

Understanding when type-safe model accessors are available

Only the main project build scripts and precompiled project script plugins have type-safe model accessors. Initialization scripts, settings scripts, script plugins do not. These limitations will be removed in a future Gradle release.

对插件的反应

https://docs.gradle.org/current/userguide/implementing_gradle_plugins.html#reacting_to_plugins

仍然可以使用您的 common.gradle.kts - 但它需要在没有 Kotlin DSL 的情况下配置 Java 插件

// common.gradle.kts
plugins.withType(JavaBasePlugin::class).configureEach {
    // the project has the Java plugin
    project.extensions.getByType<JavaPluginExtension>().apply {
        toolchain {
            languageVersion.set(JavaLanguageVersion.of(11))
        }
    }
    tasks.withType<Test>().configureEach {
        useJUnitPlatform()
    }
}

这有点笨拙,因为 Kotlin DSL 助手不可用。

buildSrc 约定插件

如果您想为单个项目创建约定,那么标准方法是创建 buildSrc 约定插件。

https://docs.gradle.org/current/userguide/organizing_gradle_projects.html#sec:build_sources

这最适合有很多子项目的项目。

// $projectRoot/buildSrc/src/main/kotlin/java-convention.gradle.kts
plugins {
    java
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(11)
    }
}

test {
    useJUnitPlatform()
}

查看此处的答案了解更多详情:

在项目之间共享插件

https://docs.gradle.org/current/userguide/implementing_gradle_plugins.html

可以在项目之间共享约定插件,只要您有一个 Maven 存储库来部署您的插件。

甚至可以创建自己的 Gradle 发行版,因此插件与 Gradle 包装器一起包含在内! https://docs.gradle.org/current/userguide/organizing_gradle_projects.html#sec:custom_gradle_distribution

但是我建议不要使用这些方法。一般来说,创建共享插件所花费的时间永远不会比仅仅复制和粘贴 buildSrc 约定插件更快。更重要的是,最好保持项目独立。虽然共享构建约定似乎是个好主意,但它引入了依赖关系,这使得跟踪问题变得困难,并使更新共享插件变得困难,因为你不确定后果可能是什么。本文解释更多https://phauer.com/2016/dont-share-libraries-among-microservices/