您可以在支持 Gradle 的 IDE 中打开此示例。

此示例展示了如何将构建逻辑组织成可重用的部分,并发布到仓库中,以便在多仓库设置的其他项目中重用。

还有一个新示例演示了如何在此场景中使用孵化中测试套件插件

用例

例如,假设一个组织生产两种类型的 Java 软件 - 服务和库。我们希望对这两种类型的项目应用一组代码质量检查规则,并配置一些特定于每种类型的方面。

组织构建逻辑

用例可以通过分层三个独立的插件来建模

构建逻辑布局
├── convention-plugins
│   ├── build.gradle.kts
│   ├── settings.gradle.kts
│   ├── src
│   │   ├── main
│   │   │   └── kotlin
│   │   │       ├── com.myorg.java-conventions.gradle.kts
│   │   │       ├── com.myorg.library-conventions.gradle.kts
│   │   │       └── com.myorg.service-conventions.gradle.kts
...
构建逻辑布局
├── convention-plugins
│   ├── build.gradle
│   ├── settings.gradle
│   ├── src
│   │   ├── main
│   │   │   └── groovy
│   │   │       ├── com.myorg.java-conventions.gradle
│   │   │       ├── com.myorg.library-conventions.gradle
│   │   │       └── com.myorg.service-conventions.gradle
...
  • com.myorg.java-conventions - 配置组织中任何 Java 项目通用的约定。这适用于先前确定的两种类型的软件,因此此插件将应用于后续的两个插件中。

  • com.myorg.library-conventions - 添加发布配置以发布到组织的仓库,并配置强制性文档检查。

  • com.myorg.service-conventions - 配置集成测试并检查 README 中的强制性内容。由于服务与库不同,因此在本例中配置了不同的文档要求。

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

编译约定插件

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

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

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

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

发布约定插件

在此示例中,我们的目标是多仓库设置。为了将上述插件应用于单独的项目,它们必须发布到公司的工件仓库。约定插件是常规的 Gradle 插件 - 因此它们可以像任何其他 Gradle 插件一样发布到外部仓库

在这里,我们配置项目以使用 maven-publish 插件发布插件。出于演示目的,我们发布到本地文件系统目录。您可以在 maven-publish 插件的仓库部分中找到有关如何发布到远程仓库的信息。

示例 2. 发布配置
convention-plugins/build.gradle.kts
plugins {
    `kotlin-dsl`
    `maven-publish`
}

group = "com.myorg.conventions"
version = "1.0"

publishing {
    repositories {
        maven {
            // change to point to your repo, e.g. http://my.org/repo
            url = uri(layout.buildDirectory.dir("repo"))
        }
    }
}

tasks.publish {
    dependsOn("check")
}
convention-plugins/build.gradle
plugins {
    id 'groovy-gradle-plugin'
    id 'maven-publish'
}

group = 'com.myorg.conventions'
version = '1.0'

publishing {
    repositories {
        maven {
            // change to point to your repo, e.g. http://my.org/repo
            url = layout.buildDirectory.dir('repo')
        }
    }
}

tasks.named('publish') {
    dependsOn('check')
}

可以使用以下命令发布插件

./gradlew publish

为了在另一个项目中消费它们,请在 settings 文件中配置插件仓库并应用插件

settings.gradle.kts
pluginManagement {
    repositories {
        gradlePluginPortal()
        maven {
            // replace the path with the actual path to the repository
            url = uri("<path-to>/convention-plugins/build/repo")
        }
    }
}
build.gradle.kts
plugins {
    id("com.myorg.service-conventions") version "1.0"
}
settings.gradle
pluginManagement {
    repositories {
        gradlePluginPortal()
        maven {
            // replace the path with the actual path to the repository
            url = uri('<path-to>/convention-plugins/build/repo')
        }
    }
}
build.gradle
plugins {
    id 'com.myorg.service-conventions' version '1.0'
}

注意事项

在约定插件中应用外部插件

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

SpotBugs 是一个外部插件 - 外部插件需要作为 implementation 依赖项添加,然后才能在约定插件中应用

convention-plugins/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")
}
convention-plugins/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'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

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

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

  • 插件版本从依赖版本确定。

添加依赖项后,可以通过 ID 在约定插件中应用外部插件

convention-plugins/src/main/kotlin/com.myorg.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")
}
convention-plugins/src/main/groovy/com.myorg.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'
}

应用其他约定插件

约定插件可以应用其他约定插件。

com.myorg.library-conventionscom.myorg.service-conventions 插件都应用了 com.myorg.java-conventions 插件

convention-plugins/src/main/kotlin/com.myorg.library-conventions.gradle.kts
plugins {
    `java-library`
    `maven-publish`
    id("com.myorg.java-conventions")
}
convention-plugins/src/main/kotlin/com.myorg.service-conventions.gradle.kts
plugins {
    id("com.myorg.java-conventions")
}
convention-plugins/src/main/groovy/com.myorg.library-conventions.gradle
plugins {
    id 'java-library'
    id 'maven-publish'
    id 'com.myorg.java-conventions'
}
convention-plugins/src/main/groovy/com.myorg.service-conventions.gradle
plugins {
    id 'com.myorg.java-conventions'
}

使用 main source set 中的类

约定插件可以使用在插件项目 main source set 中定义的类。

在此示例中,com.myorg.service-conventions 插件使用 src/main/java 中的自定义 task 类来配置服务 README 检查

convention-plugins/src/main/kotlin/com.myorg.service-conventions.gradle.kts
val readmeCheck by tasks.registering(com.example.ReadmeVerificationTask::class) {
    readme = layout.projectDirectory.file("README.md")
    readmePatterns = listOf("^## Service API$")
}
convention-plugins/src/main/groovy/com.myorg.service-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 = ['^## Service API$']
}

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