从 Maven 迁移到 Bazel

报告问题 查看源代码

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

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

准备工作

  • 安装 Bazel(如果尚未安装)。
  • 如果您刚开始接触 Bazel,请先浏览 Bazel 简介:构建 Java 教程,然后再开始迁移。本教程介绍了 Bazel 的概念、结构和标签语法。

Maven 与 Bazel 之间的区别

  • Maven 使用顶级 pom.xml 文件。Bazel 支持多个构建文件,并且每个 BUILD 文件支持多个目标,从而允许执行比 Maven 更高的构建操作。
  • Maven 负责部署过程的步骤。Bazel 不会自动部署
  • Bazel 使您能够表达语言之间的依赖关系。
  • 在向项目添加新部分时,您可能需要使用 Bazel 添加新的 BUILD 文件。最佳实践是在每个新的 Java 软件包中添加一个 BUILD 文件。

从 Maven 迁移到 Bazel

以下步骤说明了如何将项目迁移到 Bazel:

  1. 创建 WORKSPACE 文件
  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. 创建 WORKSPACE 文件

在项目的根目录下创建一个名为 WORKSPACE 的文件。如果您的项目没有外部依赖项,则工作区文件可以为空。

如果您的项目依赖于不在项目目录中的文件或软件包,请在工作区文件中指定这些外部依赖项。如需自动列出工作区文件的外部依赖项,请使用 rules_jvm_external。如需了解如何使用此规则集,请参阅 README

Guava 项目示例:外部依赖项

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

将以下代码段添加到 WORKSPACE 文件中:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

RULES_JVM_EXTERNAL_TAG = "4.3"
RULES_JVM_EXTERNAL_SHA = "6274687f6fc5783b589f56a2f1ed60de3ce1f99bc4e8f9edef3de43bdf7c6e74"

http_archive(
    name = "rules_jvm_external",
    sha256 = RULES_JVM_EXTERNAL_SHA,
    strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
    url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
)

load("@rules_jvm_external//:defs.bzl", "maven_install")

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",
    ],
)

2. 创建一个 BUILD 文件

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

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

  1. WORKSPACE 文件所在的目录中,创建一个文本文件并将其命名为 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:为目标指定一个含义明确的名称。在上面的示例中,目标称为“全部”。
      • srcs:使用 globbing 列出项目中的所有 .java 文件。
      • resources:使用 globbing 列出项目中的所有资源。
      • deps:您需要确定项目需要哪些外部依赖项。例如,如果您使用工具 generate_workspace 生成了外部依赖项列表,则 java_library 的依赖项就是 generated_java_libraries 宏中列出的库。
    • 请查看迁移 Guava 项目过程中的此顶级 BUILD 文件的以下示例

  3. 现在项目的根目录下有了 BUILD 文件,接下来构建项目以确保它可以正常运行。在命令行中,从工作区目录使用 bazel build //:everything 通过 Bazel 构建项目。

    项目现已使用 Bazel 成功构建。您将需要添加更多 BUILD 文件,以允许对项目进行增量构建。

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

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

java_library(
    name = "everything",
    srcs = glob([
        "guava/src/**/*.java",
        "futures/failureaccess/src/**/*.java",
    ]),
    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 可维护性,以及
  • 控制软件包之间目标的可见性,这可以防止包含实现详情的库泄露到公共 API 等问题。

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

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

4. 使用 Bazel 构建

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

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