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

为了让你的软件项目易于增长,你可以将一个 Gradle 项目组织成多个子项目,以便将你正在构建的软件模块化。在本指南中,你将学习如何以一个 Groovy 应用程序为例来构建这样的项目。但是,这些一般概念适用于你使用 Gradle 构建的任何软件。你可以按照本指南一步步从头开始创建一个新项目,或者使用上面的链接下载完整的示例项目。

你将构建什么

你将构建一个 Groovy 应用程序,该应用程序由一个应用程序项目和多个项目组成。

你将需要什么

创建项目文件夹

Gradle 带有一个名为 init 的内置任务,用于在空文件夹中初始化新的 Gradle 项目。init 任务使用(同样内置的)wrapper 任务创建 Gradle Wrapper 脚本,即 gradlew

第一步是为新项目创建一个文件夹并进入该文件夹。

$ mkdir demo
$ cd demo

运行 init 任务

在新的项目目录中,在终端中使用以下命令运行 init 任务:gradle init。出现提示时,选择 1: application 作为项目类型,选择 3: Groovy 作为实现语言。然后,选择 2: Application and library project。接下来你可以选择用于编写构建脚本的 DSL - 1 : Kotlin2: Groovy。对于其他问题,按回车键使用默认值。

输出将如下所示

$ gradle init

Select type of build to generate:
  1: Application
  2: Library
  3: Gradle plugin
  4: Basic (build structure only)
Enter selection (default: Application) [1..4] 1

Select implementation language:
  1: Java
  2: Kotlin
  3: Groovy
  4: Scala
  5: C++
  6: Swift
Enter selection (default: Java) [1..6]  3

Project name (default: demo):

Enter target Java version (min: 7, default: 21):

Select application structure:
  1: Single application project
  2: Application and library project
Enter selection (default: Single application project) [1..2] 2

Select build script DSL:
  1: Kotlin
  2: Groovy
Enter selection (default: Kotlin) [1..2]

Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]

BUILD SUCCESSFUL
1 actionable task: 1 executed

init 任务生成的新项目结构如下

├── gradle (1)
│   ├── libs.versions.toml (2)
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew (3)
├── gradlew.bat (3)
├── settings.gradle.kts (4)
├── buildSrc
│   ├── build.gradle.kts (5)
│   ├── settings.gradle.kts (5)
│   └── src
│       └── main
│           └── kotlin (6)
│               ├── buildlogic.groovy-application-conventions.gradle.kts
│               ├── buildlogic.groovy-common-conventions.gradle.kts
│               └── buildlogic.groovy-library-conventions.gradle.kts
├── app
│   ├── build.gradle.kts (7)
│   └── src
│       ├── main (8)
│       │   └── java
│       │       └── demo
│       │           └── app
│       │               ├── App.java
│       │               └── MessageUtils.groovy
│       └── test (9)
│           └── java
│               └── demo
│                   └── app
│                       └── MessageUtilsTest.groovy
├── list
│   ├── build.gradle.kts (7)
│   └── src
│       ├── main (8)
│       │   └── java
│       │       └── demo
│       │           └── list
│       │               └── LinkedList.groovy
│       └── test (9)
│           └── java
│               └── demo
│                   └── list
│                       └── LinkedListTest.groovy
└── utilities
    ├── build.gradle.kts (7)
    └── src
        └── main (8)
            └── java
                └── demo
                    └── utilities
                        ├── JoinUtils.groovy
                        ├── SplitUtils.groovy
                        └── StringUtils.groovy
├── gradle (1)
│   ├── libs.versions.toml (2)
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew (3)
├── gradlew.bat (3)
├── settings.gradle (4)
├── buildSrc
│   ├── build.gradle (5)
│   ├── settings.gradle (5)
│   └── src
│       └── main
│           └── groovy (6)
│               ├── buildlogic.groovy-application-conventions.gradle
│               ├── buildlogic.groovy-common-conventions.gradle
│               └── buildlogic.groovy-library-conventions.gradle
├── app
│   ├── build.gradle (7)
│   └── src
│       ├── main (8)
│       │   └── java
│       │       └── demo
│       │           └── app
│       │               ├── App.java
│       │               └── MessageUtils.java
│       └── test (9)
│           └── java
│               └── demo
│                   └── app
│                       └── MessageUtilsTest.java
├── list
│   ├── build.gradle (7)
│   └── src
│       ├── main (8)
│       │   └── java
│       │       └── demo
│       │           └── list
│       │               └── LinkedList.java
│       └── test (9)
│           └── java
│               └── demo
│                   └── list
│                       └── LinkedListTest.java
└── utilities
    ├── build.gradle (7)
    └── src
        └── main (8)
            └── java
                └── demo
                    └── utilities
                        ├── JoinUtils.java
                        ├── SplitUtils.java
                        └── StringUtils.java
1 生成的 Wrapper 文件文件夹
2 生成的版本目录
3 Gradle Wrapper 启动脚本
4 Settings 文件,用于定义构建名称和子项目
5 buildSrc 的构建脚本,用于配置构建逻辑的依赖
6 用于编写 Groovy 或 Kotlin DSL 约定插件的源文件夹
7 三个子项目 applistutilities 的构建脚本
8 每个子项目中的 Groovy 源文件夹
9 子项目中的 Groovy 测试源文件夹

现在你已经完成了项目设置,可以构建一个模块化为多个子项目的 Groovy 应用程序。

检查项目文件

settings.gradle(.kts) 文件有两行值得关注的内容

settings.gradle.kts
rootProject.name = "demo"
include("app", "list", "utilities")
settings.gradle
rootProject.name = 'demo'
include('app', 'list', 'utilities')
  • rootProject.name 为构建指定了一个名称,这会覆盖默认的根据所在目录命名构建的行为。建议设置一个固定名称,因为如果项目被共享(例如作为 Git 仓库的根目录),文件夹可能会改变。

  • include("app", "list", "utilities") 定义了构建由对应文件夹中的三个子项目组成。可以通过扩展列表或添加更多 include(…​) 语句来添加更多子项目。

由于我们的构建由多个子项目组成,我们希望在它们之间共享构建逻辑和配置。为此,我们使用了位于 buildSrc 文件夹中的所谓约定插件buildSrc 中的约定插件是利用 Gradle 插件系统编写可重用构建配置片段的简单方法。

在此示例中,我们可以找到三个相互依赖的约定插件

buildSrc/src/main/kotlin/buildlogic.groovy-common-conventions.gradle.kts
plugins {
    groovy (1)
}

repositories {
    mavenCentral() (2)
}

dependencies {
    constraints {
        implementation("org.apache.commons:commons-text:1.13.0") (3)

        implementation("org.codehaus.groovy:groovy-all:3.0.24")
    }

    implementation("org.codehaus.groovy:groovy-all") (4)

    testImplementation("org.junit.jupiter:junit-jupiter:5.12.1") (5)

    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.named<Test>("test") {
    useJUnitPlatform() (6)
}
buildSrc/src/main/groovy/buildlogic.groovy-common-conventions.gradle
plugins {
    id 'groovy' (1)
}

repositories {
    mavenCentral() (2)
}

dependencies {
    constraints {
        implementation 'org.apache.commons:commons-text:1.13.0' (3)

        implementation 'org.codehaus.groovy:groovy-all:3.0.24'
    }

    implementation 'org.codehaus.groovy:groovy-all' (4)

    testImplementation 'org.junit.jupiter:junit-jupiter:5.12.1' (5)

    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
    useJUnitPlatform() (6)
}

groovy-common-conventions 定义了一些应由我们所有 Groovy 项目共享的配置,无论它们代表库还是实际应用程序。首先,我们应用Groovy 插件 (1),以使构建 Groovy 项目的所有功能可用。然后,我们声明一个仓库 — mavenCentral() — 作为外部依赖的来源 (2),定义依赖约束 (3) 以及所有子项目共享的标准依赖,并设置 JUnit 5 作为测试框架 (4…​)。其他共享设置,如编译器标志或 JVM 版本兼容性,也可以在此处设置。

buildSrc/src/main/kotlin/buildlogic.groovy-library-conventions.gradle.kts
plugins {
    id("buildlogic.groovy-common-conventions") (1)
    `java-library` (2)
}
buildSrc/src/main/groovy/buildlogic.groovy-library-conventions.gradle
plugins {
    id 'buildlogic.groovy-common-conventions' (1)
    id 'java-library' (2)
}
buildSrc/src/main/kotlin/buildlogic.groovy-application-conventions.gradle.kts
plugins {
    id("buildlogic.groovy-common-conventions") (1)
    application (2)
}
buildSrc/src/main/groovy/buildlogic.groovy-application-conventions.gradle
plugins {
    id 'buildlogic.groovy-common-conventions' (1)
    id 'application' (2)
}

groovy-library-conventionsgroovy-application-conventions 都应用了 groovy-common-conventions 插件 (1),以便库项目和应用程序项目都能共享在那里执行的配置。接下来,它们分别应用 java-libraryapplication 插件 (2),从而将我们的通用配置逻辑与库或应用程序的特定配置相结合。虽然在此示例中没有更细粒度的配置,但特定于库或应用程序项目的构建配置可以放在这些约定插件脚本之一中。

让我们看一下子项目中的 build.gradle(.kts) 文件。

app/build.gradle.kts
plugins {
    id("buildlogic.groovy-application-conventions")
}

dependencies {
    implementation("org.apache.commons:commons-text")
    implementation(project(":utilities"))
}

application {
    mainClass = "demo.app.App" (1)
}
app/build.gradle
plugins {
    id 'buildlogic.groovy-application-conventions'
}

dependencies {
    implementation 'org.apache.commons:commons-text'
    implementation project(':utilities')
}

application {
    mainClass = 'demo.app.App' (1)
}
list/build.gradle.kts
plugins {
    id("buildlogic.groovy-library-conventions")
}
list/build.gradle
plugins {
    id 'buildlogic.groovy-library-conventions'
}
utilities/build.gradle.kts
plugins {
    id("buildlogic.groovy-library-conventions")
}

dependencies {
    api(project(":list"))
}
utilities/build.gradle
plugins {
    id 'buildlogic.groovy-library-conventions'
}

dependencies {
    api project(':list')
}

查看构建脚本,我们可以看到它们包含最多三个块

  • 每个构建脚本都应该有一个 plugins {} 块来应用插件。在一个结构良好的构建中,它可能只应用一个约定插件,如本示例所示。约定插件将负责应用和配置核心 Gradle 插件(如 applicationjava-library)、其他约定插件或来自插件门户的社区插件。

  • 其次,如果项目有依赖,则应该添加一个 dependencies {} 块。依赖可以是外部的,例如我们在 groovy-common-conventions 中添加的 JUnit 依赖,也可以指向其他本地子项目。为此,使用了 project(…​) 表示法。在我们的示例中,utilities 库需要 list 库。而 app 使用了 utilities 库。如果本地项目相互依赖,Gradle 会在需要时(且仅在需要时)负责构建依赖的项目。要了解更多信息,请参阅关于Gradle 中的依赖管理的文档。

  • 第三,可能有一个或多个插件的配置块。这些配置块只有在配置特定于某个项目的某些内容时,才应该直接在构建脚本中使用。否则,此类配置也应属于约定插件。在此示例中,我们使用 application {} 块(特定于 application 插件)将 app 项目中的 mainClass 设置为 demo.app.App (1)

我们拥有的最后一个构建文件是 buildSrc 中的 build.gradle(.kts) 文件。

buildSrc/build.gradle.kts
plugins {
    `kotlin-dsl` (1)
}

repositories {
    gradlePluginPortal() (2)
}
buildSrc/build.gradle
plugins {
    id 'groovy-gradle-plugin' (1)
}

repositories {
    gradlePluginPortal() (2)
}

此文件为构建约定插件本身奠定了基础。通过应用插件开发插件之一(groovy-gradle-pluginkotlin-dsl(1),我们可以在 buildSrc 中以构建文件形式编写约定插件。这些就是我们上面已经检查过的约定插件。此外,我们添加了 Gradle 的插件门户作为仓库 (2),这使我们可以访问社区插件。要使用插件,需要在 dependencies {} 块中将其声明为依赖。

除了 Gradle 构建文件,你还可以在相应文件夹中找到 Groovy 示例源代码和测试源代码。你可以随意修改这些生成的源代码和测试,以探索在按照下一步所述运行构建时 Gradle 如何响应更改。

运行测试

你可以使用 ./gradlew check 来执行所有子项目中的所有测试。当你使用像 check 这样的纯任务名称调用 Gradle 时,该任务将在提供该任务的所有子项目中执行。要仅针对特定子项目,你可以使用任务的完整路径。例如,:app:check 将只执行 app 项目的测试。然而,在此示例中,其他子项目仍然会被编译,因为 app 声明了对它们的依赖。

$ ./gradlew check

BUILD SUCCESSFUL
9 actionable tasks: 9 executed

如果所有测试都成功通过,Gradle 不会在控制台打印更多输出。你可以在 <subproject>/build/reports 文件夹中找到测试报告。你可以随意更改一些示例代码或测试,然后重新运行 check,看看测试失败时会发生什么。

运行应用程序

多亏了 application 插件,你可以直接从命令行运行应用程序。run 任务告诉 Gradle 执行分配给 mainClass 属性的类中的 main 方法。

$ ./gradlew run

> Task :app:run
Hello world!

BUILD SUCCESSFUL
2 actionable tasks: 2 executed
第一次运行 wrapper 脚本 gradlew 时,可能会有一段延迟,因为该版本的 gradle 会被下载并本地存储在你的 ~/.gradle/wrapper/dists 文件夹中。

打包应用程序

application 插件还会为你打包应用程序及其所有依赖。生成的归档文件还将包含一个脚本,用于通过一个命令启动应用程序。

$ ./gradlew build

BUILD SUCCESSFUL in 0s
7 actionable tasks: 7 executed

如果你如上所示运行完整的构建,Gradle 将会为你生成两种格式的归档文件:app/build/distributions/app.tarapp/build/distributions/app.zip

发布构建扫描

了解构建幕后工作原理的最佳方法是发布构建扫描。为此,只需使用 --scan 标志运行 Gradle。

$ ./gradlew build --scan

BUILD SUCCESSFUL in 0s
7 actionable tasks: 7 executed

Publishing a build scan to scans.gradle.com requires accepting the Gradle Terms of Service defined at https://gradle.com/terms-of-service.
Do you accept these terms? [yes, no] yes

Gradle Terms of Service accepted.

Publishing build scan...
https://gradle.com/s/5u4w3gxeurtd2

点击链接,探索哪些任务被执行了,哪些依赖被下载了,以及更多详细信息!

总结

就这样!你现在已经成功地使用 Gradle 配置并构建了一个 Groovy 应用程序项目。你已经学会了如何

  • 初始化一个生成 Groovy 应用程序的项目

  • 通过组合多个子项目来创建一个模块化的软件项目

  • 使用 buildSrc 中的约定插件在子项目之间共享构建配置逻辑

  • 在所有子项目中运行同名任务

  • 在特定子项目中运行任务

  • 构建、打包并运行应用程序

下一步

当你的项目增长时,你可能对如何配置 JVM 项目、组织多项目构建以及依赖管理有更多兴趣。