您可以使用 IntelliJ 原生导入器Eclipse Buildship 在 IDE 中打开此示例。

此示例展示了如何在多项目构建中将构建逻辑组织成可重用的插件。

用例

例如,假设一个包含三个子项目的项目会生成两个公共 Java 库,这两个库使用第三个子项目作为内部共享库。这是项目结构

项目结构
├── internal-module
│   └── build.gradle.kts
├── library-a
│   ├── build.gradle.kts
│   └── README.md
├── library-b
│   ├── build.gradle.kts
│   └── README.md
└── settings.gradle.kts
项目结构
├── internal-module
│   └── build.gradle
├── library-a
│   ├── build.gradle
│   └── README.md
├── library-b
│   ├── build.gradle
│   └── README.md
└── settings.gradle

假设我们所有的项目都是 Java 项目。在这种情况下,我们希望对所有项目应用一组通用的规则,例如源目录布局、编译器标志、代码风格约定、代码质量检查等等。

三个项目中的两个不仅仅是 Java 项目 - 它们是库,我们可能希望将它们发布到外部存储库。发布配置,例如库的通用组名以及存储库坐标,可能是两个库都需要共享的横切关注点。对于此示例,我们还假设我们希望强制执行我们的库公开具有通用结构的一些文档。

组织构建逻辑

从上面的用例中,我们确定了两种类型的项目:通用 Java 项目和公共库。我们可以通过分层两个独立的插件来模拟这个用例,每个插件定义了应用它们的项目类型。

构建逻辑布局
├── buildSrc
│   ├── build.gradle.kts
│   ├── settings.gradle.kts
│   ├── src
│   │   ├── main
│   │   │   └── kotlin
│   │   │       ├── myproject.java-conventions.gradle.kts
│   │   │       └── myproject.library-conventions.gradle.kts
...
构建逻辑布局
├── buildSrc
│   ├── build.gradle
│   ├── settings.gradle
│   ├── src
│   │   ├── main
│   │   │   └── groovy
│   │   │       ├── myproject.java-conventions.gradle
│   │   │       └── myproject.library-conventions.gradle
...
  • myproject.java-conventions - 配置适用于组织中任何 Java 项目的通用约定。它应用核心 javacheckstyle 插件以及外部 com.github.spotbugs 插件,配置常见的编译器选项以及代码质量检查。

  • myproject.library-conventions - 添加发布配置以发布到组织的存储库,并检查 README 中的强制性内容。它应用 java-librarymaven-publish 插件以及 myproject.java-conventions 插件。

内部库子项目应用 myproject.java-conventions 插件

internal-module/build.gradle.kts
plugins {
    id("myproject.java-conventions")
}

dependencies {
    // internal module dependencies
}
internal-module/build.gradle
plugins {
    id 'myproject.java-conventions'
}

dependencies {
    // internal module dependencies
}

两个公共库子项目应用 myproject.library-conventions 插件。

library-a/build.gradle.kts
plugins {
    id("myproject.library-conventions")
}

dependencies {
    implementation(project(":internal-module"))
}
library-b/build.gradle.kts
plugins {
    id("myproject.library-conventions")
}

dependencies {
    implementation(project(":internal-module"))
}
library-a/build.gradle
plugins {
    id 'myproject.library-conventions'
}

dependencies {
    implementation project(':internal-module')
}
library-b/build.gradle
plugins {
    id 'myproject.library-conventions'
}

dependencies {
    implementation project(':internal-module')
}

请注意,将约定插件应用于子项目实际上声明了它的类型。通过应用 myproject.java-conventions 插件,我们声明:这是一个“Java”项目。通过应用 myproject.library-conventions 插件,我们声明:这是一个“库”项目。

本示例中创建的所有插件都包含使用 TestKit 验证其行为的功能测试。

本示例没有任何项目源代码,只列出了一个假设的项目结构,其中两个库子项目依赖于一个共享的内部子项目。

编译约定插件

在本示例中,约定插件被实现为 预编译脚本插件 - 这是最简单的开始方式,因为您可以直接使用 Gradle 的 DSL 之一来实现构建逻辑,就像插件是常规构建脚本一样。

为了使预编译脚本插件被发现,buildSrc 项目需要在其 build.gradle.kts 文件中应用 kotlin-dsl 插件

为了使预编译脚本插件被发现,buildSrc 项目需要在其 build.gradle 文件中应用 groovy-gradle-plugin 插件

buildSrc/build.gradle.kts
plugins {
    `kotlin-dsl`
}
buildSrc/build.gradle
plugins {
    id 'groovy-gradle-plugin'
}

注意事项

在预编译脚本插件中应用外部插件

myproject.java-conventions 插件使用 SpotBugs 插件执行静态代码分析。

SpotBugs 是一个外部插件 - 外部插件 需要作为实现依赖项添加,然后才能在预编译脚本插件中应用它们。

buildSrc/build.gradle.kts
repositories {
    gradlePluginPortal() // so that external plugins can be resolved in dependencies section
}

dependencies {
    implementation("com.github.spotbugs.snom:spotbugs-gradle-plugin:5.2.1")
    testImplementation("junit:junit:4.13")
}
buildSrc/build.gradle
repositories {
    gradlePluginPortal() // so that external plugins can be resolved in dependencies section
}

dependencies {
    implementation 'com.github.spotbugs.snom:spotbugs-gradle-plugin:5.2.1'
    testImplementation platform("org.spockframework:spock-bom:2.2-groovy-3.0")
    testImplementation 'org.spockframework:spock-core'
}

tasks.named('test', Test) {
    useJUnitPlatform()
}
  • 插件的依赖项工件坐标 (GAV) 可能与插件 ID 不同。

  • Gradle 插件门户 (gradlePluginPortal()) 被添加为插件依赖项的仓库。

  • 插件版本由依赖项版本确定。

添加依赖项后,可以在预编译脚本插件中通过 ID 应用外部插件

buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
plugins {
    java
    checkstyle

    // NOTE: external plugin version is specified in implementation dependency artifact of the project's build file
    id("com.github.spotbugs")
}
buildSrc/src/main/groovy/myproject.java-conventions.gradle
plugins {
    id 'java'
    id 'checkstyle'

    // NOTE: external plugin version is specified in implementation dependency artifact of the project's build file
    id 'com.github.spotbugs'
}

应用其他预编译脚本插件

预编译脚本插件可以应用其他预编译脚本插件。

myproject.library-conventions 插件应用了 myproject.java-conventions 插件

buildSrc/src/main/kotlin/myproject.library-conventions.gradle.kts
plugins {
    `java-library`
    `maven-publish`
    id("myproject.java-conventions")
}
buildSrc/src/main/groovy/myproject.library-conventions.gradle
plugins {
    id 'java-library'
    id 'maven-publish'
    id 'myproject.java-conventions'
}

使用主源集中的类

预编译脚本插件可以使用插件项目主源集中定义的类。

在本示例中,myproject.library-conventions 插件使用来自 buildSrc/src/main/java 的自定义任务类来配置库 README 检查

buildSrc/src/main/kotlin/myproject.library-conventions.gradle.kts
val readmeCheck by tasks.registering(com.example.ReadmeVerificationTask::class) {
    readme = layout.projectDirectory.file("README.md")
    readmePatterns = listOf("^## API$", "^## Changelog$")
}
buildSrc/src/main/groovy/myproject.library-conventions.gradle
def readmeCheck = tasks.register('readmeCheck', com.example.ReadmeVerificationTask) {
    // Expect the README in the project directory
    readme = layout.projectDirectory.file("README.md")
    // README must contain a Service API header
    readmePatterns = ['^## API$', '^## Changelog$']
}

有关编写自定义 Gradle 插件的更多详细信息,请参阅 用户手册