Bazel C++ 首次构建实战教程:从单一 Target 到多 Package 的完整指南 📅 发布时间:2026/9/13 5:08:21 👁 浏览次数: Bazel C 首次构建实战教程从单一 Target 到多 Package 的完整指南【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel本教程是 Bazel 官方 First Build 系列的核心入门路径以 C 项目为载体带你零基础跑通 Bazel 的核心工作流理解工作区workspace、BUILD文件、包package与目标target的关系并分三个阶段单目标 → 多目标 → 多包亲手完成三次构建。读完本教程你将掌握bazel build的基本用法、cc_binary/cc_library规则的配置方式、deps依赖声明与visibility可见性控制的实战技巧并能在本仓库的 examples/cpp 示例中直接复现验证。全程约需 30 分钟。准备工作安装 Bazel 并获取示例工程如果你还没有安装 Bazel请先参照 安装指南 完成安装。由于本教程使用 Git 管理示例代码建议同时安装 Git。然后克隆官方示例仓库获取教程所需的样例工程git clone https://github.com/bazelbuild/examples示例工程位于examples/cpp-tutorial目录其目录结构如下examples └── cpp-tutorial ├──stage1 │ ├── main │ │ ├── BUILD │ │ └── hello-world.cc │ └── MODULE.bazel ├──stage2 │ ├── main │ │ ├── BUILD │ │ ├── hello-world.cc │ │ ├── hello-greet.cc │ │ └── hello-greet.h │ └── MODULE.bazel └──stage3 ├── main │ ├── BUILD │ ├── hello-world.cc │ ├── hello-greet.cc │ └── hello-greet.h ├── lib │ ├── BUILD │ ├── hello-time.cc │ └── hello-time.h └── MODULE.bazel三组文件分别对应教程的三个阶段第一阶段在单一包package中构建单一目标target第二阶段在单一包中同时构建一个二进制目标和一个库目标第三阶段构建包含多个包、多个目标的项目。三个阶段中MODULE.bazel均已就绪无需手动创建。核心概念Workspace、MODULE.bazel 与 BUILD 文件在动手构建之前先建立三个贯穿全程的关键概念。工作区workspace是存放项目源代码和 Bazel 构建产物的目录。它包含两类关键文件MODULE.bazel位于项目根目录用于标识该目录是一个 Bazel 工作区同时也是声明外部依赖的地方Bazel 7 起默认启用 Bzlmod 模块系统由该文件驱动。在后续项目中只需在目录中创建一个空的MODULE.bazel文件即可将其标记为工作区。BUILD文件告诉 Bazel 如何构建项目的不同部分。工作区内包含BUILD文件的目录即为一个包package。以本仓库为例根目录的 MODULE.bazel 中通过bazel_dep(name rules_cc, version 0.2.19)声明了 C 构建规则依赖说明当前 Bazel 的 C 规则由rules_cc模块提供examples/cpp/BUILD 则通过load(rules_cc//cc:cc_binary.bzl, cc_binary)等语句导入规则后使用。理解 BUILD 文件BUILD文件包含多种指令其中最关键的是规则rule——一组告诉 Bazel 如何构建你想要的输出如可执行二进制、库的指令。规则在BUILD文件中的每一次实例化被称为目标target它指向一组特定的源文件与依赖dependency也可以指向其他目标。查看cpp-tutorial/stage1/main/BUILDcc_binary( name hello-world, srcs [hello-world.cc], )hello-world目标实例化了 Bazel 内置的cc_binary规则告诉 Bazel 从hello-world.cc这一个无依赖的源文件构建一个自包含的可执行二进制。从源码结构看这一模式与本仓库 examples/cpp/BUILD 中cc_binary(name hello-world, srcs [hello-world.cc], deps [:hello-lib])的写法一脉相承只是后者额外声明了对hello-lib库的依赖。阶段一单一目标、单一包进入cpp-tutorial/stage1目录并执行首次构建cd cpp-tutorial/stage1 bazel build //main:hello-world这里的目标标签target label//main:hello-world中//main:表示BUILD文件相对于工作区根目录的位置hello-world是BUILD文件中的目标名。Bazel 执行后会输出类似如下结果INFO: Found 1 target... Target //main:hello-world up-to-date: bazel-bin/main/hello-world INFO: Elapsed time: 2.267s, Critical Path: 0.25s构建产物默认输出到工作区根目录下的bazel-bin目录。运行刚构建好的二进制bazel-bin/main/hello-world终端将打印Hello world。阶段一的依赖图只有一个目标、一个源文件cpp-tutorial-stage1.png 展示了 hello-world 的依赖图单一目标对应单一源文件至此你已完成第一次 Bazel 构建掌握了基本构建流程。参照本仓库的 examples/cpp/hello-world.cc可以看到真实项目中二进制入口的写法main函数接收命令行参数调用库对象的greet方法打印问候语并通过退出码始终为 0向 Bazel 汇报测试通过。阶段二多个构建目标对于稍大的项目单一目标往往不够用。将大项目拆分为多个目标与包可以带来两个关键收益增量构建——Bazel 只重新构建发生变化的部分并行加速——一次构建多个部分。本阶段新增一个目标下一阶段再新增一个包。查看cpp-tutorial/stage2/main/BUILDcc_library( name hello-greet, srcs [hello-greet.cc], hdrs [hello-greet.h], ) cc_binary( name hello-world, srcs [hello-world.cc], deps [ :hello-greet, ], )Bazel 会先用内置的cc_library规则构建hello-greet库再构建hello-world二进制。hello-world目标中的deps属性告诉 Bazel构建hello-world需要hello-greet库。注意deps中:hello-greet的写法——前导冒号表示引用同一 BUILD 文件内的目标。切换到 stage2 目录并构建cd ../stage2 bazel build //main:hello-world输出同样指向bazel-bin/main/hello-world运行后依旧打印Hello world。此时若修改hello-greet.cc再重新构建Bazel 只会重新编译该文件这正是增量构建的直观体现。从依赖图可以看到hello-world比阶段一多了一个名为hello-greet的输入cpp-tutorial-stage2.png 展示了 hello-world 的依赖图二进制目标依赖 hello-greet 库目标本阶段你已用两个目标完成构建hello-world编译一个源文件并依赖//main:hello-greet后者编译两个额外源文件。这一库 二进制的组织方式与本仓库 examples/cpp/BUILD 中hello-libcc_library含srcs与hdrs加hello-worldcc_binarydeps引用:hello-lib的结构完全一致hello-lib.h 与 hello-lib.cc 就是可对照阅读的真实实现。阶段三多个包本阶段引入多包构建也是教程中复杂度最高的一层。stage3目录下有两个子目录各自包含BUILD文件因此 Bazel 视角下工作区存在两个包lib和main。查看lib/BUILDcc_library( name hello-time, srcs [hello-time.cc], hdrs [hello-time.h], visibility [//main:__pkg__], )再看main/BUILDcc_library( name hello-greet, srcs [hello-greet.cc], hdrs [hello-greet.h], ) cc_binary( name hello-world, srcs [hello-world.cc], deps [ :hello-greet, //lib:hello-time, ], )main包中的hello-world目标通过deps依赖lib包中的hello-time目标标签写作//lib:hello-time——不带冒号前缀的//lib:用于引用其他包中的目标。这一依赖关系同样体现在依赖图中cpp-tutorial-stage3.png 展示了 hello-world 的依赖图main 包中的目标依赖 lib 包中的目标这里出现了一个新概念可见性visibility。默认情况下目标只对同一BUILD文件内的其他目标可见。因此为了让lib/BUILD中的//lib:hello-time能被main/BUILD中的目标使用必须用visibility [//main:__pkg__]属性显式放行——//main:__pkg__表示对main包内的所有目标可见。Bazel 通过目标可见性机制防止库的内部实现细节泄露进公共 API避免跨包误用。关于可见性的更多规则与取值如//visibility:public、//visibility:private可参阅 可见性概念文档。切换到 stage3 目录完成最终构建cd ../stage3 bazel build //main:hello-world运行构建产物得到最后一次Hello worldbazel-bin/main/hello-world至此你完成了两个包、三个目标的多包构建理解了目标之间的依赖关系。关于标签与依赖的底层约定可进一步阅读 标签概念文档 与 依赖概念文档。仓库源码印证从教程到真实工程教程的三阶段演进并非纸上谈兵本仓库 examples/cpp 就是其真实落地的进阶版规则导入方式教程中直接使用内置规则名而真实工程通过load(rules_cc//cc:cc_binary.bzl, cc_binary)显式导入cc_binary、cc_library、cc_test因为自 Bazel 7 起 C 规则已外置到rules_cc模块见根目录 MODULE.bazel 中的bazel_dep(name rules_cc, version 0.2.19)。测试目标examples/cpp/BUILD中还有cc_test目标如hello-success_test直接用hello-world.cc当测试源靠退出码判定通过与故意失败的hello-fail_test打了tags [manual]标记演示了 Bazel 测试与二进制的同构关系——这与教程单一目标思想一脉相承。数据文件与运行文件runfile目标展示了data属性与rules_cc//cc/runfiles的用法runfile.cc 中通过Runfiles::Rlocation在运行时定位数据文件属于教程之外的自然延伸。下一步学习路径至此你已经完成 Bazel 的第一次完整构建。要继续深入推荐以下路径继续聚焦 C阅读 C 常见构建用例尝试其他语言的入门教程Java、Android 应用、iOS 应用了解本地与远程仓库的使用外部依赖深入学习 Bazel 的其他规则体系规则参考。Happy building!【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考