1 Bazel 简介
1.1 Bazel 是什么
Bazel 是 Google 开源的构建 + 测试一体化工具,前身是谷歌内部的构建系统 Blaze,2015 年对外开源。有意思的是,Bazel 这个名字其实就是 Blaze 的字母重组词。

定位上,Bazel 与 Make / CMake / Gradle 属于同一类工具,但它的设计目标很明确:专门面向巨型单体仓库(monorepo)、多语言、跨平台、可复现构建场景。
1.2 核心特点
声明式构建,Starlark 语言
- 使用
BUILD 文件(语法是 Starlark,Python 的子集)来描述构建目标,例如 cc_binary、cc_library、java_library、py_test 等。
- 文件中描述的是目标(target)和依赖关系,而不是直接写编译器命令。Bazel 会自动推导编译链路,这与 Makefile 那种直接写 shell 命令的思路完全不同。
- 另外还有
WORKSPACE 文件,用来声明外部依赖,比如远程 Git 仓库、Maven 包等。
内容哈希缓存 + 增量构建 + 远程缓存
- 只重新编译被改动的目标,而且判断依据不是文件修改时间,而是文件内容哈希。
- 支持远程缓存和远程执行:多开发者、CI 可以共享构建产物,大仓库的构建速度提升非常明显,Google 内部的缓存命中率据说是极高的。
- 自动并行执行互不依赖的构建 / 测试任务。
多语言、跨平台
- 原生支持 C/C++、Java、Go、Python、Rust、TypeScript、Objective-C。
- 同一套
BUILD 文件,可以编译 Linux / macOS / Windows 服务端,以及 Android、iOS 移动端程序,真正的一套定义多平台输出。
1.3 适合什么项目
- 大型单体仓库 monorepo,多语言混编(如 C++ + Java + Python 共存)
- 跨平台构建(同时编译服务器 + 移动端)
- 大量自动化测试,CI 构建压力较大的团队
- 要求构建结果可复现、多开发者协作的环境
2 在 Ubuntu 中安装 Bazel 工具
curl -LO https://github.com/bazelbuild/bazel/releases/download/7.4.0/bazel-7.4.0-linux-x86_64
chmod +x bazel-7.4.0-linux-x86_64
sudo mv bazel-7.4.0-linux-x86_64 /usr/local/bin/bazel
bazel --version
在 Ubuntu 中直接下载比较慢,实测在 Windows 中下载速度会快不少,可以先在 Windows 下载再复制到 Ubuntu 中继续操作。

执行 bazel --version 后可以看到版本号为 7.4.0,说明安装成功。
3 Bazel 构建测试
参考 Bazel 官方示例代码: https://github.com/bazelbuild/examples/tree/main/cpp-tutorial/stage1
3.1 测试代码
首先看 MODULE.bazel 文件:
bazel_dep(name = "rules_cc", version = "0.0.17")
main/BUILD 文件:
load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
cc_binary(
name = "hello",
srcs = ["hello.cpp"],
)
main/hello.cpp 文件:
#include <ctime>
#include <string>
#include <iostream>
std::string get_greet(const std::string& who){
return "Hello " + who;
}
void print_localtime(){
std::time_t result = std::time(nullptr);
std::cout << std::asctime(std::localtime(&result));
}
int main(int argc, char** argv){
std::string who = "world";
if (argc > 1) {
who = argv[1];
}
std::cout << get_greet(who) << std::endl;
print_localtime();
return 0;
}
项目结构如下:
xxpcb@xxpcb-ubuntu20:~/myTest/Bazel/test_demo$ tree
.
└── test1
├── main
│ ├── BUILD
│ └── hello.cpp
└── MODULE.bazel
2 directories, 3 files
xxpcb@xxpcb-ubuntu20:~/myTest/Bazel/test_demo$
3.2 开始构建
执行构建后,Bazel 会尝试从 GitHub 的 bazelbuild 仓库下载文件,但在国内网络环境下很容易报错。
尝试在 ~/.bazelrc 中配置了一个国内镜像地址:
common --registry=https://mirror.ghproxy.com/https://bcr.bazel.build
但再次构建仍然失败:

注意到项目中自动生成了一个 MODULE.bazel.lock 文件:

这个文件是 Bzlmod 从 BCR(Bazel Central Registry)下载 rules_cc 时生成的。虽然刚才配置了 ghproxy 的 registry 镜像,但 ghproxy 的访问稳定性并不好,经常直接超时。
3.3 构建失败问题解决
面对这个问题,有两种思路:
- 方案 A:出于入门学习目的,直接关掉 Bzlmod,不使用
MODULE.bazel,回到经典的 WORKSPACE 模式,这样就完全不需要联网下载 rules_cc 了。
- 方案 B:继续使用 Bzlmod +
MODULE.bazel,但需要配置 --module_mirrors,配置比较复杂,可以后续再深入研究。
本文先采用方案 A。
3.3.1 代码修改
# 删除 MODULE.bazel,Bzlmod 就不会自动拉取 rules_cc 了
rm MODULE.bazel
# 创建 WORKSPACE 文件(空文件即可,作为经典老工作区的标记)
touch WORKSPACE
main/BUILD 文件去掉开头的 load 那一行:
cc_binary(
name = "hello",
srcs = ["hello.cpp"],
)
3.3.2 关闭 Bzlmod
命令行参数方式(单次生效):
bazel build --noenable_bzlmod //main:hello
写入 .bazelrc 永久关闭(推荐,一劳永逸):
echo "common --noenable_bzlmod" >> ~/.bazelrc
3.3.3 再次执行
bazel build //main:hello
这里简单解释一下 //main:hello 这个目标表达式的含义:
// 双斜杠:表示工作区根目录起点
// 就代表项目根(本例中是 test1/,里面有 WORKSPACE 文件)
//main 就是根目录下的 main 文件夹
: 冒号:分隔 package 路径和 target 目标名
- 冒号前面是 package 路径(
//main)
- 冒号后面是 target 名称(在
BUILD 文件中定义的 hello)
这次构建成功了,同时也生成了多个 bazel 相关的目录:

3.4 测试可执行程序的运行
可以直接找到 bazel-bin 下的产物运行,也可以使用 bazel run 命令:

3.5 构建产物分析
构建完成后,工作目录下会多出几个 bazel 开头的目录:
bazel-bin:编译输出的二进制、库、头文件,最终产物都在这里
bazel-out:构建的核心工作目录,存放编译中间产物(体量最大)
bazel-test1:Bazel 的执行根目录(execution root)
bazel-testlogs:单元测试的日志目录
3.5.1 bazel 目录软链接
这些生成的 bazel 目录实际上都是软链接,真实指向 ~/.cache 目录下的位置:
xxpcb@xxpcb-ubuntu20:~/myTest/Bazel/test_demo/test1$ ls -l
total 20
lrwxrwxrwx 1 xxpcb xxpcb 115 Oct 18 16:29 bazel-bin -> /home/xxpcb/.cache/bazel/_bazel_xxpcb/139b038fc59e32075309080b94868442/execroot/__main__/bazel-out/k8-fastbuild/bin
lrwxrwxrwx 1 xxpcb xxpcb 98 Oct 18 16:29 bazel-out -> /home/xxpcb/.cache/bazel/_bazel_xxpcb/139b038fc59e32075309080b94868442/execroot/__main__/bazel-out
lrwxrwxrwx 1 xxpcb xxpcb 88 Oct 18 16:29 bazel-test1 -> /home/xxpcb/.cache/bazel/_bazel_xxpcb/139b038fc59e32075309080b94868442/execroot/__main__
lrwxrwxrwx 1 xxpcb xxpcb 120 Oct 18 16:29 bazel-testlogs -> /home/xxpcb/.cache/bazel/_bazel_xxpcb/139b038fc59e32075309080b94868442/execroot/__main__/bazel-out/k8-fastbuild/testlogs
drwxrwxr-x 2 xxpcb xxpcb 4096 Oct 18 16:28 main
-rw-rw-r-- 1 xxpcb xxpcb 0 Oct 18 16:27 WORKSPACE
xxpcb@xxpcb-ubuntu20:~/myTest/Bazel/test_demo/test1$
进入 .cache 目录查看:
xxpcb@xxpcb-ubuntu20:~/myTest/Bazel/test_demo/test1/bazel-out$ cd ~/.cache/
xxpcb@xxpcb-ubuntu20:~/.cache$ ls
bazel evolution ibus mesa_shader_cache mozilla thunderbird update-manager-core
bazelisk fontconfig ibus-table Microsoft rhythmbox tracker yelp
event-sound-cache.tdb.9fe5b643f31a452cad31d0c22abb2f43.x86_64-pc-linux-gnu gstreamer-1.0 libgweather motd.legal-displayed thumbnails ubuntu-report
xxpcb@xxpcb-ubuntu20:~/.cache$ cd bazel
xxpcb@xxpcb-ubuntu20:~/.cache/bazel$ ls
_bazel_xxpcb
xxpcb@xxpcb-ubuntu20:~/.cache/bazel$ cd _bazel_xxpcb/
xxpcb@xxpcb-ubuntu20:~/.cache/bazel/_bazel_xxpcb$ ls
139b038fc59e32075309080b94868442 cache install
进入 bazel/_bazel_xxpcb 后可以看到 3 个子目录。路径 ~/.cache/bazel/_bazel_xxpcb/ 这一层被称作 outputUserRoot,当前用户 xxpcb 的全部 Bazel 数据都存放在这里。
139b038fc59e32075309080b94868442:output_base,输出基目录,以 MD5 哈希命名
cache:全局共享仓库缓存(repository cache)
install:Bazel 二进制首次运行时,会把内嵌的 Java 服务 jar、linux-sandbox、辅助工具脚本等解压到这里
3.5.2 三个子目录详解
MD5 哈希目录
- 名字是项目工作区路径做 MD5 哈希生成的字符串,一个项目对应一个哈希文件夹
- 存放 Bazel Server 进程信息、编译沙盒、
external/ 外部仓库、编译 action 缓存、编译中间产物
- 还保存 Bazel 服务的 PID、日志、JVM 日志、沙盒执行目录 execroot
- 执行
bazel clean --expunge 会清空这个目录

如何确认项目对应的哈希值?可以使用这条指令:
xxpcb@xxpcb-ubuntu20:~/myTest/Bazel/test_demo/test1$ bazel info output_base
/home/xxpcb/.cache/bazel/_bazel_xxpcb/139b038fc59e32075309080b94868442
xxpcb@xxpcb-ubuntu20:~/myTest/Bazel/test_demo/test1$
cache 目录
- 跨项目共享!所有 Bazel 工程共用这个缓存
- 通过
http_archive / Bzlmod 下载的外部压缩包(如 rules_cc、protobuf 等第三方依赖),按 SHA256 哈希存储
- 同一个第三方包,A 项目下载过,B 项目再使用时可以直接复用,无需重复联网下载

install 目录
- 不同 Bazel 版本会按哈希分开存放
- 包含
A-server.jar:Bazel 后台 Server 的核心程序
- 沙盒工具、process-wrapper 等底层执行工具
- 只要不升级 Bazel 版本,这个目录基本不会变动

3.5.3 构建产物清除
bazel clean:清除工作区中 bazel 软链接指向的那些生成文件
bazel clean --expunge:清除 cache 中对应的哈希目录

4 总结
本篇介绍了 Bazel 构建工具的基础知识,并在 Ubuntu 中完成了环境配置,用一个最基础的 hello world 程序进行了编译和运行测试。过程中遇到了 Bzlmod 模式下国内网络下载 rules_cc 超时的问题,通过切换到 WORKSPACE 模式绕开了网络依赖。此外,还分析了构建产物目录的结构,包括 output_base 的哈希目录、全局共享缓存和 install 安装目录,对 Bazel 的存储布局有了一个初步认识。如果你对构建系统的底层机制感兴趣,后续可以继续深入研究 Bzlmod 的镜像配置以及远程缓存的使用。