1. 概述

Pants 是一个支持多语言的构建工具。它速度快、可扩展性强、使用友好,适合各种规模的代码库,尤其适合包含多种编程语言的多语言项目(polyglot 项目)。

在本教程中,我们将学习如何配置 Pants、定义 BUILD 文件、添加依赖、测试等内容,全部以 Java 为例进行说明。

2. Pants 构建工具简介

构建工具是自动化软件开发流程的关键组件。在 Java 生态中,MavenGradle 是非常流行的构建工具。但在面对大型多语言代码库时,它们可能会显得力不从心。

Pants 提供了构建系统所需的基本功能,包括测试运行器、代码生成器、编译器、依赖解析器、打包工具等。

它非常适合单一代码库中包含多个项目的场景,尤其是这些项目使用不同编程语言编写的情况,也就是常说的 monorepo 架构。

目前,Pants 支持 Python、Go、Java、Scala 和 Shell,未来还会支持更多语言。

3. 初始配置

安装好 Pants 的二进制文件后,项目配置非常简单。它支持 Windows、Linux 和 macOS。

要创建一个 Java 项目,我们首先创建一个项目目录:

$ mkdir hello-pant

进入该目录,创建 pants.toml 文件,并添加 Java 后端支持:

[GLOBAL]
backend_packages = [
  "pants.backend.experimental.java",
]

这里我们告诉 Pants,这是一个 Java 项目。由于 Pants 擅长处理 monorepo 项目,我们可以配置其支持多种语言:

[GLOBAL]
backend_packages = [
  "pants.backend.experimental.java",
  "pants.backend.experimental.scala",
]

上面的配置启用了 Java 和 Scala 的支持。

接下来,我们需要为项目创建目录结构。在根目录下创建 src 目录存放源代码:

$ mkdir -p src/com/baeldung/hellopant

再创建一个 test 目录用于存放单元测试:

$ mkdir -p test/com/baeldung/hellopant

4. 编译代码

Pants 可以轻松编译代码并生成 JAR 文件。本节我们将介绍如何使用 Pants 编译代码、运行测试以及打包为 JAR。

4.1. 生成 BUILD 文件

前面我们创建了 Java 项目的目录结构。现在在 src 目录下添加一个 Hello.java 文件:

public class Hello {
    public static void main(String [] args) {
        System.out.println ("Hello World!");
    }
}

这段代码是最经典的 “Hello World!” 示例。

为了让 Pants 能够识别并编译代码,我们需要生成 BUILD 文件。执行以下命令:

$ pants tailor ::

该命令会在每个包含代码的目录中生成 java_sources target,在测试目录中生成 junit_tests target:

Created src/com/baeldung/hellopant/BUILD:
  - Add java_sources target hellopant
Created test/com/baeldung/hellopant/BUILD:
  - Add junit_tests target tests

⚠️ 没有 BUILD 文件,代码将无法编译。

测试目录下的 BUILD 文件内容如下:

junit_tests(
    name="tests",
)

源码目录下的 BUILD 文件内容如下:

java_sources(
)

此外,我们还需要生成锁文件以设置默认的 JVM 并管理第三方依赖:

$ pants generate-lockfiles

该命令会在项目根目录下生成一个 default.lock 文件和一个 jvm 子目录。

最后,我们执行代码:

pants run src/com/baeldung/hellopant/Hello.java

输出结果为:

Hello World!

4.2. 添加第三方依赖

添加第三方依赖需要修改 default.lock 文件并定义依赖信息。我们以添加 Guava 为例。

首先修改 default.lock 文件内容:

# {
#   "version": 1,
#   "generated_with_requirements": [
#     "com.google.guava:guava:18.0,url=not_provided,jar=not_provided",
#    ]
# }

然后在 jvm 目录下创建对应的目录结构:

$ mkdir -p com/google/guava

接着创建 BUILD 文件定义 Guava 的 artifact 信息:

jvm_artifact(
    group="com.google.guava",
    artifact="guava",
    version="32.1.1-jre",
)

注意目录结构应与 artifact 的 group 名称一致。其他以 com 开头的依赖也应放在 com 目录下。

例如,添加 Jackson Core

$ mkdir -p com/fasterxml/jackson/core

然后添加对应的 BUILD 文件即可。

最后执行:

$ pants generate-lockfiles

该命令会根据 default.lock 文件下载对应的依赖。

4.3. 运行测试

我们可以添加 JUnit 依赖来运行单元测试。更新 default.lock 文件:

# "generated_with_requirements": [
#     "junit:junit:4.13.2,url=not_provided,jar=not_provided",
# ]

创建目录结构:

$ mkdir -p junit/junit

创建 BUILD 文件:

jvm_artifact(
    group="junit",
    artifact="junit",
    version="4.13.2"
)

执行:

$ pants generate-lockfiles

接下来编写一个测试类:

class GuavaUnitTest {
    @Test
    void whenConvertListToStringAndSkipNull_thenConverted() {
        List<String> names = Lists.newArrayList("John", null, "Jane", "Adam", "Tom");
        final String result = Joiner.on(",")
          .skipNulls()
          .join(names);
        assertEquals(result, "John,Jane,Adam,Tom");
    }
}

运行测试:

$ pants test test/com/baeldung/hellopant/GuavaUnitTest.java

✅ 测试成功时,控制台会输出类似如下信息(截图略):

pants success unit test

4.4. 资源文件处理

我们可以将资源文件放在 resources 目录中。例如:

$ mkdir -p resource/com/baeldung/hellopant

创建一个 word.txt 文件,内容为:

from Us

创建 BUILD 文件:

resources(name = "word", sources=["word.txt"])

编写测试类读取资源文件:

@Test
public void whenGettingTextFromAResourceFile_thenJoined() throws IOException {
    String world = Resources.toString(Resources.getResource(GuavaUnitTest.class, "word.txt"), Charsets.UTF_8).strip();
    String result = Joiner.on(" ").join("Hello", world);
    assertEquals(result, "Hello from Us");
}

在测试的 BUILD 文件中添加依赖:

junit_tests(
    name="tests",
    dependencies=[
        "src/resources/com/baeldung/hellopant:word",
    ],
)

✅ 这样测试就能成功读取资源文件。

4.5. 打包为 JAR

打包为 JAR 文件非常简单。首先在 src 目录下的 BUILD 文件中定义目标:

java_sources()

deploy_jar(
    name="HelloPant",
    main="com.baeldung.hellopant",
    dependencies=[
        ":hellopant",
    ],
)

执行打包命令:

$ pants package ::

成功后会在项目根目录下生成 dist 文件夹,里面包含 JAR 文件:

pants folder output jar

5. 与 IDE 集成

Pants 可以通过 Build Server Protocol (BSP) 加载到 IntelliJ IDEA 中。默认情况下,IntelliJ 不支持 BSP,需要安装 Scala 插件。

创建 bsp-groups.toml 文件:

[groups.default]
addresses = [  
   "src/jvm::",  
   "tests/jvm::",  
]
resolve = "jvm:jvm-default"

更新 pants.toml 文件:

[experimental-bsp]
groups_config_files = ["bsp-groups.toml"]

最后导入项目到 IntelliJ,Pants 会启动 BSP 服务并与 IntelliJ 同步模块信息。

6. 其他功能

Pants 提供了丰富的功能,包括代码格式化、测试超时设置、指定 JDK 版本等。

6.1. 代码格式化与检查

Pants 支持 Google Java Format。启用方式如下:

[GLOBAL]
backend_packages = [
   "pants.backend.experimental.java",
   "pants.backend.experimental.java.lint.google_java_format"
]

格式化代码:

$ pants fmt test/com::

检查格式:

$ pants lint ::

6.2. 设置测试超时

可以在 BUILD 文件中设置测试超时时间:

junit_tests(
    name="tests",
    timeout=120,
)

也可以在 pants.toml 中设置默认值和最大值:

[test]
timeout_default = 60
timeout_maximum = 120

调试时可禁用超时:

$ pants test --no-timeouts

6.3. 指定 JDK 版本

pants.toml 中设置 JDK 版本:

[jvm]
jdk = 1.8

若未指定,Pants 会使用主机默认的 JDK。

7. 总结

在本文中,我们了解了 Pants 构建工具的基本功能,并通过 Java 示例展示了其使用方法。我们还介绍了如何与 IDE 集成,以及一些高级功能如代码格式化、测试超时设置和 JDK 版本指定。

Pants 是 monorepo 架构项目的理想选择,尤其适合多语言项目。

如需查看完整示例代码,请访问 GitHub 仓库


原始标题:Introduction to the Pants Build Tool