baeldung 2024-01-08
1. 概述
Bazel 是一个开源的源代码构建和测试工具,类似于 Maven 和 Gradle。它支持多种编程语言的项目,并能为多个平台生成构建产物。
在本教程中,我们将逐步介绍如何使用 Bazel 构建一个简单的 Java 应用程序。为了便于说明,我们将从一个多模块的 Maven 项目开始,然后使用 Bazel 来构建其源代码。
首先,我们需要安装 Bazel。
2. 项目结构
让我们创建一个多模块的 Maven 项目:
bazel(根目录)
pom.xml
WORKSPACE(Bazel 工作区文件)
|— bazelapp
pom.xml
BUILD(Bazel 构建文件)
|— src
|— main
|— java
|— test
|— java
|— bazelgreeting
pom.xml
BUILD(Bazel 构建文件)
|— src
|— main
|— java
|— test
|— java
WORKSPACE 文件用于设置 Bazel 的工作区。一个项目中可以有一个或多个 WORKSPACE 文件。在本例中,我们只在项目顶层目录保留一个 WORKSPACE 文件。
另一个重要的文件是 BUILD 文件,其中包含构建规则。每个规则都通过唯一的目标名称(target name)进行标识。
Bazel 允许我们根据需要配置任意数量的 BUILD 文件,并可精细控制到任意粒度。这意味着我们可以仅构建少量 Java 类,只要相应地配置 BUILD 规则即可。为简化起见,本示例将只使用最少的 BUILD 文件。
由于 Bazel 构建配置的输出通常是 JAR 文件,因此我们将每个包含 BUILD 文件的目录称为一个构建包(build package)。
3. 构建文件
3.1 规则配置
现在,我们来配置第一个构建规则以编译 Java 二进制文件。我们在 bazelapp 模块的 BUILD 文件中进行如下配置:
java_binary(
name = "BazelApp",
srcs = glob(["src/main/java/com/baeldung/*.java"]),
main_class = "com.baeldung.BazelApp"
)
让我们逐项解释这些配置参数:
java_binary:规则类型名称;用于构建可执行二进制文件,需提供额外属性。name:构建目标的名称。srcs:指定要构建的 Java 文件位置模式的数组。main_class:应用程序主类的全限定名(可选)。
3.2 执行构建
现在可以开始构建应用了。在包含 WORKSPACE 文件的目录下,执行以下命令:
$ bazel build //bazelapp:BazelApp
最后一个参数是 BUILD 文件中配置的目标名称,其格式为 //<构建路径>:<目标名称>。
//表示从当前工作区根目录开始。bazelapp是从工作区根目录到BUILD文件的相对路径。BazelApp是要构建的目标名称。
3.3 构建输出
执行上述命令后,应生成两个输出文件:
bazel-bin/bazelapp/BazelApp.jar
bazel-bin/bazelapp/BazelApp
BazelApp.jar包含所有编译后的类。BazelApp是一个用于执行该 JAR 文件的包装脚本。
3.4 可部署的 JAR
有时我们需要将 JAR 及其依赖项分发到其他位置进行部署。
上一节中的包装脚本会在启动 BazelApp.jar 时自动包含所有依赖项(JAR 文件)。
不过,我们也可以生成一个包含所有依赖项的 fat jar(即“可部署 JAR”):
$ bazel build //bazelapp:BazelApp_deploy.jar
在目标名称后添加 _deploy 后缀,Bazel 就会将所有依赖打包进 JAR 中,生成一个可直接部署的独立 JAR 文件。
4. 依赖管理
到目前为止,我们只使用了 bazelapp 模块中的文件进行构建。但大多数应用程序都有外部依赖。
本节将介绍如何将依赖项与 JAR 文件一起打包。
4.1 构建库
首先,我们需要一个可供 bazelapp 使用的依赖项。
我们创建另一个名为 bazelgreeting 的 Maven 模块,并在其 BUILD 文件中使用 java_library 规则进行配置。我们将此目标命名为 greeter:
java_library(
name = "greeter",
srcs = glob(["src/main/java/com/baeldung/*.java"])
)
这里使用了 java_library 规则来创建一个库。构建该目标后,将生成 libgreeter.jar 文件:
INFO: Found 1 target...
Target //bazelgreeting:greeter up-to-date:
bazel-bin/bazelgreeting/libgreeter.jar
注意:原文中目标名为
greetings,但BUILD文件中写的是greeter,此处统一修正为greeter。
4.2 配置依赖
为了让 bazelapp 能使用 greeter,我们需要进行额外配置。
首先,需让 greeter 包对 bazelapp 可见。这可以通过在 greeter 的 java_library 规则中添加 visibility 属性实现:
java_library(
name = "greeter",
srcs = glob(["src/main/java/com/baeldung/*.java"]),
visibility = ["//bazelapp:__pkg__"]
)
visibility 属性指定哪些包可以访问当前包。
接下来,在 bazelapp 的 BUILD 文件中,通过 deps 属性声明对 greeter 的依赖:
java_binary(
name = "BazelApp",
srcs = glob(["src/main/java/com/baeldung/*.java"]),
main_class = "com.baeldung.BazelApp",
deps = ["//bazelgreeting:greeter"]
)
deps 属性列出了当前包所依赖的其他包。
5. 外部依赖
在实际项目中,我们可能需要跨多个工作区协作,或从远程仓库导入库。这类外部依赖可分为两类:
- 本地依赖:在同一工作区内管理(如上一节所示),或跨越多个工作区。
- HTTP 归档:通过 HTTP 从远程位置导入库。
Bazel 提供了多种规则来管理外部依赖。接下来,我们将演示如何从远程位置导入 JAR 文件。
5.1 通过 HTTP URL 导入
以导入 Apache Commons Lang 为例。由于需要从 HTTP 地址获取该 JAR,我们将使用 http_jar 规则。
首先,在 WORKSPACE 文件中加载 Bazel 的 HTTP 构建定义,并配置 Apache Commons Lang 的下载地址:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_jar")
http_jar(
name = "apache-commons-lang",
url = "https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar"
)
然后,在 bazelapp 包的 BUILD 文件中添加依赖:
deps = ["//bazelgreeting:greeter", "@apache-commons-lang//jar"]
注意:此处使用的名称必须与 WORKSPACE 文件中 http_jar 规则的 name 属性一致。
5.2 Maven 依赖
逐个管理 JAR 文件非常繁琐。更高效的方式是使用 rules_jvm_external 规则,在 WORKSPACE 文件中配置 Maven 仓库,从而批量获取所需依赖。
首先,使用 http_archive 规则从远程位置导入 rules_jvm_external:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
RULES_JVM_EXTERNAL_TAG = "2.0.1"
RULES_JVM_EXTERNAL_SHA = "55e8d3951647ae3dffde22b4f7f8dee11b3f70f3f89424713debd7076197eaca"
http_archive(
name = "rules_jvm_external",
strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
sha256 = RULES_JVM_EXTERNAL_SHA,
url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
)
接着,使用 maven_install 规则配置 Maven 仓库地址和所需构件:
load("@rules_jvm_external//:defs.bzl", "maven_install")
maven_install(
artifacts = [
"org.apache.commons:commons-lang3:3.12.0"
],
repositories = [
"https://repo1.maven.org/maven2",
]
)
最后,在 BUILD 文件中添加依赖(注意构件名称中的点 . 被转换为下划线 _):
deps = ["//bazelgreeting:greeter", "@maven//:org_apache_commons_commons_lang3"]
6. 结论
在本教程中,我们学习了如何使用 Bazel 构建一个类似 Maven 风格的 Java 项目,包括:
- 设置基本项目结构
- 编写
BUILD和WORKSPACE文件 - 构建可执行 JAR 和 fat JAR
- 管理内部模块依赖
- 引入外部依赖(通过 HTTP 或 Maven)
这些基础知识足以帮助你将现有 Maven 项目迁移到 Bazel 构建系统。