news 2026/9/13 5:08:14

Bazel C++ 首次构建实战教程:从单一 Target 到多 Package 的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Bazel C++ 首次构建实战教程:从单一 Target 到多 Package 的完整指南

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/BUILD

cc_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-worldBUILD文件中的目标名。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/BUILD

cc_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,含srcshdrs)加hello-worldcc_binarydeps引用:hello-lib)的结构完全一致,hello-lib.h 与 hello-lib.cc 就是可对照阅读的真实实现。

阶段三:多个包

本阶段引入多包构建,也是教程中复杂度最高的一层。stage3目录下有两个子目录,各自包含BUILD文件,因此 Bazel 视角下工作区存在两个包:libmain

查看lib/BUILD

cc_library( name = "hello-time", srcs = ["hello-time.cc"], hdrs = ["hello-time.h"], visibility = ["//main:__pkg__"], )

再看main/BUILD

cc_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 world

bazel-bin/main/hello-world

至此你完成了"两个包、三个目标"的多包构建,理解了目标之间的依赖关系。关于标签与依赖的底层约定,可进一步阅读 标签概念文档 与 依赖概念文档。

仓库源码印证:从教程到真实工程

教程的三阶段演进并非纸上谈兵,本仓库 examples/cpp 就是其真实落地的进阶版:

  • 规则导入方式:教程中直接使用内置规则名,而真实工程通过load("@rules_cc//cc:cc_binary.bzl", "cc_binary")显式导入cc_binarycc_librarycc_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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 5:08:13

TIA博途V15 SCL积分库:LREAL精度与AT语法实现高可靠数值积分

简介:本资源是面向西门子TIA博途V15平台开发者的专用积分运算SCL算法库,适用于工业自动化领域中需实现PID控制、过程累计量计算或信号积分处理的工程师与PLC程序员。压缩包共24个文件,含10个XML格式的功能块定义与接口描述文件、9张PNG格式的…

作者头像 李华
网站建设 2026/9/13 5:07:13

Python对接金融行情API实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 5:04:10

IEC插座滤波器选型指南:从EMC原理到安装实战

1. 为什么一个IEC插座滤波器要卖到三百块?——从“能用”到“真可靠”的分水岭你拆开一台工业PLC控制柜,看到那个带IEC标准接口的黑色方块,标着“EMI滤波器”,价格标签写着298元;再点开某电商平台,同尺寸、…

作者头像 李华
网站建设 2026/9/13 5:04:06

六个完整项目拆解:掌握数据挖掘全流程实战

简介:包含六个完整Python数据挖掘学习项目,覆盖员工流失预警、电信客户流失、药物决策树挖掘、世界幸福报告分析、英雄联盟胜负预测、保险交叉销售等典型业务场景。所有项目均以Jupyter Notebook形式编排,代码、分析过程、运行结果与配套数据…

作者头像 李华
网站建设 2026/9/13 5:03:49

Django实现零信任策略引擎:设备指纹+动态策略+审计日志

简介:本资源是一套基于Django框架实现的零信任网络访问控制系统后端源码,面向计算机、信息安全、人工智能等专业的在校学生与教师,适用于课程设计、期末大作业及毕设选题场景,聚焦身份认证、动态授权与细粒度访问控制等核心安全能…

作者头像 李华