从 Maven 迁移到 Bazel

报告问题 查看来源 Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

本页面介绍了如何从 Maven 迁移到 Bazel,包括前提条件和安装步骤。本文介绍了 Maven 和 Bazel 之间的区别,并提供了一个使用 Guava 项目的迁移示例。

从任何构建工具迁移到 Bazel 时,最好让这两个构建工具并行运行,直到您完全迁移了开发团队、CI 系统和任何其他相关系统。您可以在同一代码库中运行 Maven 和 Bazel。

准备工作

  • 安装 Bazel(如果尚未安装)。
  • 如果您不熟悉 Bazel,请在开始迁移之前先完成Bazel 简介:构建 Java 教程。本教程介绍了 Bazel 的概念、结构和标签语法。

Maven 和 Bazel 之间的区别

  • Maven 使用顶级 pom.xml 文件。Bazel 支持多个 build 文件以及每个 BUILD 文件中的多个目标,从而实现比 Maven 更增量的 build。
  • Maven 负责部署流程的各个步骤。Bazel 不会自动部署。
  • 借助 Bazel,您可以表达语言之间的依赖关系。
  • 随着您向项目添加新部分,您可能需要使用 Bazel 添加新的 BUILD 文件。最佳做法是向每个新的 Java 软件包添加 BUILD 文件。

从 Maven 迁移到 Bazel

以下步骤介绍了如何将项目迁移到 Bazel:

  1. 创建 MODULE.bazel 文件
  2. 创建一个 BUILD 文件
  3. 创建更多 BUILD 文件
  4. 使用 Bazel 构建

以下示例来自 Guava 项目从 Maven 到 Bazel 的迁移。使用的 Guava 项目是版本 v31.1。使用 Guava 的示例不会详细介绍迁移的每个步骤,但会显示为迁移生成或手动添加的文件和内容。

$ git clone https://github.com/google/guava.git && cd guava
$ git checkout v31.1

1. 创建 MODULE.bazel 文件

在项目根目录下创建一个名为 MODULE.bazel 的文件。如果您的项目没有外部依赖项,此文件可以为空。

如果您的项目依赖于不在项目目录中的文件或软件包,请在 MODULE.bazel 文件中指定这些外部依赖项。您可以使用 rules_jvm_external 管理来自 Maven 的依赖项。如需了解如何使用此规则集,请参阅自述文件

Guava 项目示例:外部依赖项

您可以使用 rules_jvm_external 规则集列出 Guava 项目的外部依赖项。

将以下代码段添加到 MODULE.bazel 文件中:

bazel_dep(name = "rules_jvm_external", version = "6.2")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
maven.install(
    artifacts = [
        "com.google.code.findbugs:jsr305:3.0.2",
        "com.google.errorprone:error_prone_annotations:2.11.0",
        "com.google.j2objc:j2objc-annotations:1.3",
        "org.codehaus.mojo:animal-sniffer-annotations:1.20",
        "org.checkerframework:checker-qual:3.12.0",
    ],
    repositories = [
        "https://repo1.maven.org/maven2",
    ],
)
use_repo(maven, "maven")

2. 创建一个 BUILD 文件

现在,您已定义工作区并列出外部依赖项(如果适用),接下来需要创建 BUILD 文件来描述项目的构建方式。与只有一个 pom.xml 文件的 Maven 不同,Bazel 可以使用多个 BUILD 文件来构建项目。这些文件指定了多个 build 目标,从而使 Bazel 可以生成增量 build。

分阶段添加 BUILD 文件。首先,在项目根目录中添加一个 BUILD 文件,并使用该文件通过 Bazel 执行初始 build。然后,您可以通过添加具有更精细目标的其他 BUILD 文件来优化 build。

  1. MODULE.bazel 文件所在的同一目录中,创建一个名为 BUILD 的文本文件。

  2. 在此 BUILD 文件中,使用适当的规则创建一个用于构建项目的目标。请参考以下提示:

    • 使用适当的规则:

      • 如需构建具有单个 Maven 模块的项目,请按如下方式使用 java_library 规则:

        java_library(
           name = "everything",
           srcs = glob(["src/main/java/**/*.java"]),
           resources = glob(["src/main/resources/**"]),
           deps = ["//:all-external-targets"],
        )
        
      • 如需构建具有多个 Maven 模块的项目,请按如下方式使用 java_library 规则:

        java_library(
           name = "everything",
           srcs = glob([
                 "Module1/src/main/java/**/*.java",
                 "Module2/src/main/java/**/*.java",
                 ...
           ]),
           resources = glob([
                 "Module1/src/main/resources/**",
                 "Module2/src/main/resources/**",
                 ...
           ]),
           deps = ["//:all-external-targets"],
        )
        
      • 如需构建二进制文件,请使用 java_binary 规则:

        java_binary(
           name = "everything",
           srcs = glob(["src/main/java/**/*.java"]),
           resources = glob(["src/main/resources/**"]),
           deps = ["//:all-external-targets"],
           main_class = "com.example.Main"
        )
        
      • 指定属性:

        • name:为目标指定一个有意义的名称。在上面的示例中,目标称为“everything”。
        • srcs:使用 globbing 列出项目中的所有 .java 文件。
        • resources:使用全局化列出项目中的所有资源。
        • deps:您需要确定项目所需的外部依赖项。
      • 不妨看看 Guava 项目迁移中的这个顶级 BUILD 文件示例

  3. 现在,您已在项目根目录中创建了 BUILD 文件,请构建项目以确保其正常运行。在命令行中,从工作区目录中使用 bazel build //:everything 通过 Bazel 构建项目。

    现在,项目已使用 Bazel 成功构建。您需要添加更多 BUILD 文件,才能对项目进行增量 build。

Guava 项目示例:从一个 BUILD 文件开始

将 Guava 项目迁移到 Bazel 时,最初使用一个 BUILD 文件来构建整个项目。以下是工作区目录中此初始 BUILD 文件的内容:

java_library(
    name = "everything",
    srcs = glob([
        "guava/src/**/*.java",
        "futures/failureaccess/src/**/*.java",
    ]),
    javacopts = ["-XepDisableAllChecks"],
    deps = [
        "@maven//:com_google_code_findbugs_jsr305",
        "@maven//:com_google_errorprone_error_prone_annotations",
        "@maven//:com_google_j2objc_j2objc_annotations",
        "@maven//:org_checkerframework_checker_qual",
        "@maven//:org_codehaus_mojo_animal_sniffer_annotations",
    ],
)

3. 创建更多 BUILD 文件(可选)

正如您在完成首次构建后所见,Bazel 仅使用一个 BUILD file 即可正常运行。您仍应考虑通过添加更多具有精细目标的 BUILD 文件,将 build 分成更小的块。

包含多个目标的多个 BUILD 文件将提高 build 的精细度,从而实现以下功能:

  • 增加了项目的增量 build,
  • 增加了构建的并行执行,
  • 更好地维护面向未来用户的 build,以及
  • 控制软件包之间目标的可见性,这可以防止包含实现细节的库泄露到公共 API 等问题。

有关添加更多 BUILD 文件的提示:

  • 您可以先向每个 Java 软件包添加一个 BUILD 文件。从依赖项最少的 Java 软件包开始,逐步处理依赖项最多的软件包。
  • 在添加 BUILD 文件并指定目标时,请将这些新目标添加到依赖于它们的目标的 deps 部分。请注意,glob() 函数不会跨越软件包边界,因此随着软件包数量的增加,glob() 匹配的文件会减少。
  • 每次向 main 目录添加 BUILD 文件时,请确保向相应的 test 目录添加 BUILD 文件。
  • 请注意正确限制软件包之间的可见性。
  • 为了简化 BUILD 文件设置中的错误排查,请确保在添加每个 build 文件时,项目能够继续使用 Bazel 进行 build。运行 bazel build //... 以确保所有目标仍能构建。

4. 使用 Bazel 构建

您一直在使用 Bazel 进行构建,同时添加 BUILD 文件来验证 build 的设置。

当您拥有所需粒度的 BUILD 文件时,可以使用 Bazel 生成所有 build。