Bazel 构建系统
bash brew install bazelisk
**Linux**
```bash
# 下载 Bazelisk 可执行文件
wget https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64
chmod +x bazelisk-linux-amd64
sudo mv bazelisk-linux-amd64 /usr/local/bin/bazel
Windows (Chocolatey)
choco install bazelisk
安装后,执行 bazel version 验证是否成功。
核心概念速览
Bazel 的世界由几个关键抽象构成,理解它们才能阅读和编写构建文件。
工作空间(Workspace)
工作空间是包含 WORKSPACE(或 WORKSPACE.bazel)文件的目录,是整个项目的根。该文件用于引入外部依赖。
包(Package)
包是包含 BUILD 或 BUILD.bazel 文件的目录。一个工作空间可以包含多个包,每个包定义了一组构建目标。
目标(Target)
目标是构建产物的最小单元,例如一个 Java 库、一个 C++ 可执行文件或一个容器镜像。目标由 标签(Label) 唯一标识,格式为 //package:target,若省略 target 部分则默认为 //package:package_name。
规则(Rule)
规则定义如何从输入生成输出。Bazel 内置大量规则,如 java_library、cc_binary、genrule 等。开发者也可自定义规则。
动手:第一个 Bazel 项目
我们创建一个简单的 Java 应用来感受构建流程。创建以下目录结构:
my-project/
├── WORKSPACE
├── BUILD
└── src/
└── main/
└── java/
└── com/
└── example/
├── BUILD
└── Greeting.java
设置工作空间
WORKSPACE 文件可以为空,它标记了工作空间根目录。外部依赖后续会在这里声明。
编写 Java 源码
src/main/java/com/example/Greeting.java:
package com.example;
public class Greeting {
public static void sayHi() {
System.out.println("Hello from Bazel!");
}
}
定义构建目标
在 src/main/java/com/example/BUILD 文件中声明一个 Java 库:
java_library(
name = "greeting",
srcs = ["Greeting.java"],
visibility = ["//:__pkg__"], # 对本工作空间的包可见
)
在项目根目录的 BUILD 文件中声明一个可执行目标,使其依赖上面的库:
java_binary(
name = "hello",
main_class = "com.example.Greeting",
runtime_deps = ["//src/main/java/com/example:greeting"],
)
构建与运行
在项目根目录执行:
bazel build //:hello
首次构建会下载相关规则和依赖,之后再次构建速度极快。运行输出:
bazel run //:hello
# 输出:Hello from Bazel!
若要生成可执行 JAR 包,可以执行:
bazel build //:hello_deploy.jar
java -jar bazel-bin/hello_deploy.jar
处理外部依赖
Bazel 建议将第三方依赖显式声明,实现可复现构建。以 Java 的 Guava 库为例。
编辑 WORKSPACE 文件,添加 Maven 仓库规则(需先加载 rules_jvm_external):
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
RULES_JVM_EXTERNAL_TAG = "4.5"
RULES_JVM_EXTERNAL_SHA ="..."
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,
)
load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")
rules_jvm_external_deps()
load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup")
rules_jvm_external_setup()
load("@rules_jvm_external//:defs.bzl", "maven_install")
maven_install(
artifacts = [
"com.google.guava:guava:31.1-jre",
],
repositories = [
"https://repo1.maven.org/maven2",
],
)
注意:SHA 值请从 rules_jvm_external 的发布页获取。添加后,在 BUILD 文件中引用 Guava:
java_library(
name = "greeting",
srcs = ["Greeting.java"],
deps = ["@maven//:com_google_guava_guava"],
visibility = ["//:__pkg__"],
)
重新运行构建,Bazel 会自动下载并缓存依赖。
多语言构建示例:C++
Bazel 对 C++ 的支持同样简洁。假设有一个 hello.cpp 文件,位于 cpp/ 包内:
cpp/hello.cpp:
#include <iostream>
int main() {
std::cout << "Hello from C++!" << std::endl;
return 0;
}
cpp/BUILD:
cc_binary(
name = "hello",
srcs = ["hello.cpp"],
)
运行:
bazel run //cpp:hello
如果依赖第三方 C++ 库,可以使用 http_archive 在 WORKSPACE 中引入,并用 cc_library 引用。
构建缓存与远程执行(进阶概念)
Bazel 的极速构建得益于其细粒度的缓存机制。本地缓存默认开启,所有构建输出会存储在输出基目录下。
远程缓存(Remote Cache):允许多个开发者或 CI 机器共享构建输出,大幅减少重复编译时间。只需在 .bazelrc 文件中配置:
build --remote_cache=grpc://cache.example.com:9090