本页介绍了如何从 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:
以下示例来自将 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
文件来描述项目应如何构建。与 Maven 使用单个 pom.xml
文件不同,Bazel 可以使用多个 BUILD
文件构建项目。这些文件指定了多个构建目标,这让 Bazel 能够生成增量 build。
分阶段添加 BUILD
文件。首先,在项目根目录中添加一个 BUILD
文件,并使用该文件使用 Bazel 进行初始构建。然后,您可以添加更多包含更精细目标的 BUILD
文件,以优化 build。
在
MODULE.bazel
文件所在的目录中,创建一个文本文件并将其命名为BUILD
。在此
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
:使用全局通配来列出项目中的所有 .java 文件。resources
:使用全局通配来列出项目中的所有资源。deps
:您需要确定项目需要哪些外部依赖项。
请参阅 Guava 项目迁移的此顶级 BUILD 文件示例。
现在,您已在项目的根目录中创建了
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",
]),
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 的并行执行效率,
- 为未来用户提高 build 的可维护性,
- 控制软件包之间目标的可见性,这可以防止包含实现细节的库泄露到公共 API 等问题。
有关添加更多 BUILD
文件的提示:
- 首先,您可以向每个 Java 软件包添加
BUILD
文件。从依赖项最少的 Java 软件包开始,逐步过渡到依赖项最多的软件包。 - 添加
BUILD
文件并指定目标后,请将这些新目标添加到依赖于它们的目标的deps
部分。请注意,glob()
函数不会跨软件包边界,因此随着软件包数量的增加,glob()
匹配的文件数量将会减少。 - 每当您向
main
目录添加BUILD
文件时,请务必向相应的test
目录添加BUILD
文件。 - 请务必妥善限制软件包之间的可见性。
- 为简化
BUILD
文件设置中的错误问题排查,请确保在您添加每个 build 文件时,项目继续使用 Bazel 进行构建。运行bazel build //...
以确保所有目标仍可构建。
4. 使用 Bazel 构建
您在添加 BUILD
文件以验证 build 的设置时,一直在使用 Bazel 进行构建。
当您拥有所需精细度的 BUILD
文件后,就可以使用 Bazel 生成所有 build。