从 Maven 迁移到 Bazel

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

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

准备工作

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

Maven 和 Bazel 之间的区别

  • Maven 使用顶级 pom.xml 文件。Bazel 支持每个 BUILD 文件包含多个构建 文件和多个目标,因此构建的增量性比 Maven's 更高。
  • 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。如需了解如何使用此规则集,请参阅 自述文件

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 文件来描述应如何构建项目。与 Maven 的一个 pom.xml 文件不同,Bazel 可以使用多个 BUILD 文件来构建项目。这些文件指定了多个构建目标, 使 Bazel 能够生成增量构建。

分阶段添加 BUILD 文件。首先,在项目的根目录下添加一个 BUILD 文件 ,并使用它通过 Bazel 执行初始构建。 然后,您可以通过添加更多具有更精细目标的 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:为目标指定一个有意义的名称。在上面的示例中, 目标名为“everything”。
      • 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 文件将提高构建的精细度 ,从而实现以下功能:

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

添加更多 BUILD 文件的提示:

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

4. 使用 Bazel 构建

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

BUILD 文件达到所需的精细度时,您可以使用 Bazel 生成所有构建。