# 多语言项目编译规则散落各处，Bazel 能把一次构建跑通吗？

**URL:** <https://www.shiqidu.com/t/topic/2069>\
**Category:** 开发调优\
**Tags:** Linux, 编程, DevOps, 问与答, 开源软件\
**Created:** [2026 年11 月 13 日 13:23 UTC](https://www.shiqidu.com/t/topic/2069 "2026-11-13T13:23:00Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

作者： ![shiqidu\_editor](https://www.shiqidu.com/user_avatar/www.shiqidu.com/shiqidu_editor/32/528_2.png) [@shiqidu\_editor](https://www.shiqidu.com/u/shiqidu_editor)\
发布日期： [2026 年11 月 13 日 13:23 UTC](https://www.shiqidu.com/t/topic/2069/1 "2026-11-13T13:23:00Z")

</div>

编译规则还散在各语言自己的脚本里。想把一次构建收成同一条入口，就会去看 Bazel。它管的是怎么把代码构建出来，不是用来声明云上的基础设施，和站内那篇 [OpenTofu](https://www.shiqidu.com/t/topic/2033) 不是同一类问题。这里只看构建系统如何把一份工程从安装接到第一次成功的 `bazel build`。

[安装页](https://bazel.build/install) 写明，在 Ubuntu Linux、macOS 和 Windows 上，推荐用 Bazelisk 来安装或更新。页面同时列出了社区维护的包（Alpine、Arch、Debian、Fedora、Homebrew、Nixpkgs 等），并注明 Bazel 团队并不官方支持这些包，问题要找包的维护者。其他平台可以尝试从源码编译。安装页本身没有写死某一个发行号，只指向官方的 release 页面。装好之后，[用 Bazel 构建程序](https://bazel.build/run/build) 把日常入口写成：走到工作区根目录或其子目录，运行 `bazel`。不带参数时可以运行 `bazel help`，常用的子命令包括 `build`（构建指定目标）、`clean`（清理输出，并可选择停止服务器）以及查询类命令。第一次跑通一次构建，用到的就是 `bazel build`。

打开 [入门入口](https://bazel.build/start) 时，页面会落到 [C++ 教程](https://bazel.build/start/cpp)。安装页的导航里还能看到 Java、Go、Android、iOS 的入门，`/start` 目前指向的是 C++ 这一份，本文只跟这条路径，不把各语言的规则摊开。教程开头就说明：Bazel 是一套支持多语言构建的构建系统，这份入门用 C++ 项目做例子，并给出对多数语言都适用的工作流程。它不是一份把所有语言规则写全的手册，第一次跟着走完即可。教程要求先完成安装，建议同时装好 Git，然后克隆官方示例仓库 `https://github.com/bazelbuild/examples`，示例在 `examples/cpp-tutorial` 目录，分三个阶段。

工作区是存放源码和构建输出的目录。根目录要有 `MODULE.bazel`，用来标识这是一个 Bazel 工作区，并声明外部依赖。含有 `BUILD` 文件的目录则是一个包。以后若要把某个目录标成工作区，教程写的办法是在该目录里放一个空的 `MODULE.bazel`。这份入门的各个阶段里已经放好了。`BUILD` 里至少要有一条规则，用来说明如何得到可执行文件或库。每一条规则的实例叫做目标，指向一组源文件和依赖，也可以再指向别的目标。教程第一阶段的 `BUILD` 里用内置的 `cc_binary` 规则定义了名为 `hello-world` 的目标，源文件是 `hello-world.cc`，没有额外依赖。进入 `cpp-tutorial/stage1` 后执行：

```auto
bazel build //main:hello-world

```

`//main:` 是 `BUILD` 文件相对工作区根目录的位置，`hello-world` 是目标名。构建产物放在工作区根下的 `bazel-bin` 目录，随后可以直接运行 `bazel-bin/main/hello-world`，示例会打印 `Hello world`。教程第二阶段会在同一个包里先用 `cc_library` 做出库，再用 `cc_binary` 的 `deps` 指向它，从而演示如何把较大的工程拆成多个目标：Bazel 只重新构建有改动的部分，也可以同时构建工程的不同部分。第三阶段再拆成多个包。第一次只想看通一次构建，第一阶段已经够用，后面两个阶段可以等第一次跑通再跟。

构建结束时，文档说它会打印这次请求了哪些目标、成功了没有，以及产物在哪里，方便脚本解析结果。同一条 `bazel build` 再跑一遍且没有改动时，会很快结束，文档称之为空构建（null build），因为没有包要重新加载，也没有构建动作要执行。若源码或依赖有变化，它会只重做必要的动作，也就是增量构建。目标可以用 `//foo/bar:wiz` 这样的标签，也可以用 `//foo/bar:all`、`//foo/...`、`//...` 这类模式一次圈出一组目标。以 `//` 开头的模式相对当前工作区解析。这些语法在第一次跑通单个目标之后再查即可，不必一开始就记全。

你们仓库里是打算把某一个语言的目标写进 `BUILD` 跑通，还是已经准备把散落的脚本一起迁过去？
