您可以在支持 Gradle 的 IDE 中打开此示例。 |
为了让您的软件项目做好增长准备,您可以将 Gradle 项目组织成多个子项目,以模块化您正在构建的软件。在本指南中,您将通过 Kotlin 应用示例了解如何构建此类项目。不过,通用概念适用于您使用 Gradle 构建的任何软件。您可以按照指南逐步从零开始创建一个新项目,或使用上面的链接下载完整的示例项目。
您将构建什么
您将构建一个 Kotlin 应用,它包含一个 application 项目和多个 library 项目。
您需要什么
-
文本编辑器或 IDE - 例如 IntelliJ IDEA
-
Java Development Kit (JDK),版本 8 或更高 - 例如 AdoptOpenJDK
-
最新的 Gradle 分发版本
创建项目文件夹
Gradle 内置了一个名为 init
的任务,它可以在一个空文件夹中初始化一个新的 Gradle 项目。init
任务使用(同样内置的)wrapper
任务来创建一个 Gradle wrapper 脚本 gradlew
。
第一步是为新项目创建一个文件夹并进入该目录。
$ mkdir demo $ cd demo
运行 init 任务
在新项目目录中,在终端中运行以下命令来运行 init
任务:gradle init
。当出现提示时,选择项目类型 1: application
,实现语言选择 2: Kotlin
。然后选择项目类型 2: Application and library project
。接下来您可以选择用于编写构建脚本的 DSL - 1 : Kotlin
或 2: Groovy
。对于其他问题,按 Enter 键使用默认值。
输出将如下所示
$ 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] 2 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] Select test framework: 1: JUnit 4 2: TestNG 3: Spock 4: JUnit Jupiter Enter selection (default: JUnit Jupiter) [1..4] 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.kotlin-application-conventions.gradle.kts
│ ├── buildlogic.kotlin-common-conventions.gradle.kts
│ └── buildlogic.kotlin-library-conventions.gradle.kts
├── app
│ ├── build.gradle.kts (7)
│ └── src
│ ├── main (8)
│ │ └── java
│ │ └── demo
│ │ └── app
│ │ ├── App.java
│ │ └── MessageUtils.kt
│ └── test (9)
│ └── java
│ └── demo
│ └── app
│ └── MessageUtilsTest.kt
├── list
│ ├── build.gradle.kts (7)
│ └── src
│ ├── main (8)
│ │ └── java
│ │ └── demo
│ │ └── list
│ │ └── LinkedList.kt
│ └── test (9)
│ └── java
│ └── demo
│ └── list
│ └── LinkedListTest.kt
└── utilities
├── build.gradle.kts (7)
└── src
└── main (8)
└── java
└── demo
└── utilities
├── JoinUtils.kt
├── SplitUtils.kt
└── StringUtils.kt
├── 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.kotlin-application-conventions.gradle
│ ├── buildlogic.kotlin-common-conventions.gradle
│ └── buildlogic.kotlin-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 | 惯例插件 (convention plugins) 的源文件夹,使用 Groovy 或 Kotlin DSL 编写 |
7 | 三个子项目 app 、list 和 utilities 的构建脚本 |
8 | 每个子项目中的 Kotlin 源文件夹 |
9 | 子项目中的 Kotlin 测试源文件夹 |
您现在已经设置好了项目,可以构建一个模块化为多个子项目的 Kotlin 应用。
查看项目文件
settings.gradle(.kts)
文件中有两行值得关注
rootProject.name = "demo"
include("app", "list", "utilities")
rootProject.name = 'demo'
include('app', 'list', 'utilities')
-
rootProject.name
为构建指定一个名称,这会覆盖默认的按目录名称命名构建的行为。建议设置一个固定名称,因为如果项目被共享(例如作为 Git 仓库的根目录),文件夹可能会改变。 -
include("app", "list", "utilities")
定义了构建由相应文件夹中的三个子项目组成。可以通过扩展列表或添加更多include(…)
语句来添加更多子项目。
由于我们的构建包含多个子项目,我们希望在它们之间共享构建逻辑和配置。为此,我们使用了位于 buildSrc
文件夹中的所谓 惯例插件 (convention plugins)。buildSrc
中的惯例插件是利用 Gradle 插件系统编写可重用构建配置片段的简单方法。
在此示例中,我们可以找到三个相互基于的惯例插件
plugins {
id("org.jetbrains.kotlin.jvm") (1)
}
repositories {
mavenCentral() (2)
}
dependencies {
constraints {
implementation("org.apache.commons:commons-text:1.13.0") (3)
}
testImplementation("org.junit.jupiter:junit-jupiter:5.12.1") (4)
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
tasks.named<Test>("test") {
useJUnitPlatform() (5)
}
plugins {
id 'org.jetbrains.kotlin.jvm' (1)
}
repositories {
mavenCentral() (2)
}
dependencies {
constraints {
implementation 'org.apache.commons:commons-text:1.13.0' (3)
}
testImplementation 'org.junit.jupiter:junit-jupiter:5.12.1' (4)
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform() (5)
}
kotlin-common-conventions
定义了一些应由我们所有 Kotlin 项目共享的配置,无论它们代表库还是实际应用。首先,我们应用 Kotlin Gradle 插件 (1) 以便拥有构建 Kotlin 项目所需的所有功能。然后,我们声明一个仓库 mavenCentral()
作为外部依赖项的来源 (2),定义依赖约束 (3) 以及所有子项目共享的标准依赖项,并将 JUnit 5 设置为测试框架 (4…)。其他共享设置,例如编译器标志或 JVM 版本兼容性,也可以在这里设置。
plugins {
id("buildlogic.kotlin-common-conventions") (1)
`java-library` (2)
}
plugins {
id 'buildlogic.kotlin-common-conventions' (1)
id 'java-library' (2)
}
plugins {
id("buildlogic.kotlin-common-conventions") (1)
application (2)
}
plugins {
id 'buildlogic.kotlin-common-conventions' (1)
id 'application' (2)
}
kotlin-library-conventions
和 kotlin-application-conventions
都应用了 kotlin-common-conventions
插件 (1),以便在其中进行的配置可以被库项目和应用项目共享。接下来它们分别应用了 java-library
或 application
插件 (2),从而将我们的通用配置逻辑与库或应用的特定配置结合起来。虽然此示例中没有更细粒度的配置,但库或应用项目特定的构建配置可以放在这些惯例插件脚本中的一个。
让我们看一下子项目中的 build.gradle(.kts)
文件。
plugins {
id("buildlogic.kotlin-application-conventions")
}
dependencies {
implementation("org.apache.commons:commons-text")
implementation(project(":utilities"))
}
application {
mainClass = "demo.app.AppKt" (1)
}
plugins {
id 'buildlogic.kotlin-application-conventions'
}
dependencies {
implementation 'org.apache.commons:commons-text'
implementation project(':utilities')
}
application {
mainClass = 'demo.app.AppKt' (1)
}
plugins {
id("buildlogic.kotlin-library-conventions")
}
plugins {
id 'buildlogic.kotlin-library-conventions'
}
plugins {
id("buildlogic.kotlin-library-conventions")
}
dependencies {
api(project(":list"))
}
plugins {
id 'buildlogic.kotlin-library-conventions'
}
dependencies {
api project(':list')
}
查看构建脚本,我们可以看到它们最多包含三个块
-
每个构建脚本都应该有一个
plugins {}
块来应用插件。在一个结构良好的构建中,它可能只应用一个 惯例插件 (convention plugin),如本示例所示。惯例插件随后会负责应用和配置核心 Gradle 插件(如application
或java-library
)、其他惯例插件或来自插件门户的社区插件。 -
其次,如果项目有依赖项,应添加一个
dependencies {}
块。依赖项可以是外部的,例如我们在kotlin-common-conventions
中添加的 JUnit 依赖项,也可以指向其他本地子项目。为此,使用了project(…)
标记。在我们的示例中,utilities
库需要list
库。而app
使用了utilities
库。如果本地项目相互依赖,Gradle 会在需要时(且仅在需要时)负责构建依赖的项目。要了解更多信息,请查阅关于Gradle 中的依赖管理的文档。 -
第三,可能有一个或多个插件配置块。这些配置块只有在为特定项目配置特定内容时才应直接在构建脚本中使用。否则,此类配置也应放在惯例插件中。在此示例中,我们使用特定于
application
插件的application {}
块,将app
项目中的mainClass
设置为demo.app.App
(1)。
我们拥有的最后一个构建文件是 buildSrc
中的 build.gradle(.kts)
文件。
plugins {
`kotlin-dsl` (1)
}
repositories {
gradlePluginPortal() (2)
}
dependencies {
implementation(libs.kotlin.gradle.plugin)
}
plugins {
id 'groovy-gradle-plugin' (1)
}
repositories {
gradlePluginPortal() (2)
}
dependencies {
implementation libs.kotlin.gradle.plugin
}
此文件为构建惯例插件本身设置了基础。通过应用插件开发插件之一 —— groovy-gradle-plugin
或 kotlin-dsl
(1),我们可以在 buildSrc
中以构建文件形式编写惯例插件,这些正是我们上面已查看过的惯例插件。此外,我们将 Gradle 的插件门户添加为仓库 (2),这使我们可以访问社区插件。要使用插件,需要在 dependencies {}
块中将其声明为依赖项。
除了 Gradle 构建文件之外,您还可以在相应的文件夹中找到示例 Kotlin 源代码和测试源代码。请随意修改这些生成的源代码和测试,以探索 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 8 actionable tasks: 8 executed
如果您运行如上所示的完整构建,Gradle 将会为您生成两种格式的归档文件:app/build/distributions/app.tar
和 app/build/distributions/app.zip
。
发布构建扫描
了解您的构建幕后正在做什么的最佳方式是发布构建扫描。为此,只需使用 --scan
标志运行 Gradle。
$ ./gradlew build --scan BUILD SUCCESSFUL in 0s 8 actionable tasks: 8 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 配置和构建了一个 Kotlin 应用项目。您已经学会了如何
-
初始化一个生成 Kotlin 应用的项目
-
通过组合多个子项目创建一个模块化软件项目
-
使用
buildSrc
中的惯例插件在子项目之间共享构建配置逻辑 -
在所有子项目中运行同名任务
-
在特定子项目中运行任务
-
构建、打包并运行应用
下一步
当您的项目增长时,您可能对如何配置 JVM 项目、构建多项目构建以及依赖管理的更多细节感兴趣