简介
第一次使用 Bazel?您来对地方了。请按照这个“首次构建”教程 简要介绍如何使用 Bazel本教程定义了一些关键术语 将在 Bazel 的上下文中使用,并将为您讲解 Bazel 的基础知识 工作流。从您需要的工具开始,您将构建并运行三个 并了解它们如何及为什么变得更加复杂。
虽然 Bazel 是一个构建系统, 支持多语言构建,本教程以 C++ 项目为例 并提供了适用于大多数语言的一般准则和流程。
预计所需时长:30 分钟。
前提条件
首先安装 Bazel(如果尚未安装) 。本教程使用 Git 控制源代码,以获得最佳结果 安装 Git 。
接下来,运行 运行以下代码:
git clone https://github.com/bazelbuild/examples
本教程的示例项目位于 examples/cpp-tutorial
目录中。
下面我们来看一下它的结构:
examples
└── cpp-tutorial
├──stage1
│ ├── main
│ │ ├── BUILD
│ │ └── hello-world.cc
│ └── WORKSPACE
├──stage2
│ ├── main
│ │ ├── BUILD
│ │ ├── hello-world.cc
│ │ ├── hello-greet.cc
│ │ └── hello-greet.h
│ └── WORKSPACE
└──stage3
├── main
│ ├── BUILD
│ ├── hello-world.cc
│ ├── hello-greet.cc
│ └── hello-greet.h
├── lib
│ ├── BUILD
│ ├── hello-time.cc
│ └── hello-time.h
└── WORKSPACE
有三组文件,每组文件代表本教程中的一个阶段。 在第一阶段,您将构建位于单个 package 中的单个目标。在第二阶段,您需要 您将通过单个软件包同时构建二进制文件和库。在 在第三阶段(也是最后一个阶段),您将构建一个包含多个软件包的项目, 并使用多个目标来构建应用。
摘要:简介
通过安装 Bazel(和 Git)并克隆本教程的代码库 已经为您使用 Bazel 完成首次构建奠定了基础。继续前往下一条 部分定义一些术语并设置工作区。
使用入门
设置工作区
您需要先设置项目工作区,然后才能构建项目。工作区是 一个目录,用于保存项目的源文件和 Bazel 的构建输出。它 还包含以下重要文件:
,用于将目录及其内容标识为 Bazel 工作区, 位于项目目录结构的根目录下。WORKSPACE
file- 一个或多个
,告知 Bazel 如何构建项目的不同部分。答 包含BUILD
filesBUILD
文件的目录是 package 中的可用属性。(有关软件包的详细信息 )。
在以后的项目中,要将目录指定为 Bazel 工作区,
空文件(名为 WORKSPACE
)。在本教程中
每个阶段中都已经存在一个 WORKSPACE
文件。
注意:在 Bazel 构建项目时,所有输入都必须位于 同一个工作区。位于不同工作区的文件独立于 才能相互关联。有关工作区规则的更多详细信息可以 本指南。
了解 BUILD 文件
BUILD
文件包含几种不同类型的 Bazel 说明。每个
BUILD
文件至少需要一条规则
一组指令,告知 Bazel 如何构建所需的输出,
例如可执行的二进制文件或库在
BUILD
文件称为目标
并指向一组特定的源文件和依赖项。
一个目标也可以指向其他目标。
看一下 cpp-tutorial/stage1/main
目录中的 BUILD
文件:
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
)
在我们的示例中,hello-world
目标会实例化 Bazel 的内置
cc_binary rule
。
该规则告诉 Bazel,从
hello-world.cc
个源文件,无依赖项。
摘要:使用入门
现在,您已经熟悉了一些关键术语,以及这些术语在 常规项目和 Bazel在下一部分中,您将构建和测试 项目的第 1 阶段。
第 1 阶段:单个目标,单个软件包
现在该构建项目的第一部分了。作为直观的参考, 项目“第一阶段”部分的结构是:
examples
└── cpp-tutorial
└──stage1
├── main
│ ├── BUILD
│ └── hello-world.cc
└── WORKSPACE
运行以下命令以移至 cpp-tutorial/stage1
目录:
cd cpp-tutorial/stage1
然后运行:
bazel build //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 目标。Bazel 会将构建输出
bazel-bin
目录下的
工作区。
现在,测试新构建的二进制文件,即:
bazel-bin/main/hello-world
此操作会输出“Hello world
”消息。
以下是第一阶段的依赖关系图:
摘要:第 1 阶段
现在,您已经完成了第一个构建,对构建 是结构化的。在下一阶段,您需要添加 目标。
第 2 阶段:多个构建目标
虽然单个目标对小型项目来说已经足够,但您可能希望进行拆分 多个目标和软件包。这样,您便可快速 增量构建(也就是说,Bazel 只重新构建已更改的内容) 通过一次性构建项目的多个部分来构建构建。此阶段的 下一个教程会添加一个目标,下一个教程中会添加一个软件包。
以下是您在第 2 阶段使用的目录:
├──stage2
│ ├── main
│ │ ├── BUILD
│ │ ├── hello-world.cc
│ │ ├── hello-greet.cc
│ │ └── hello-greet.h
│ └── WORKSPACE
下面我们来看一下 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",
],
)
借助此 BUILD
文件,Bazel 首先构建了 hello-greet
库
(使用 Bazel 的内置 cc_library rule
),
然后是 hello-world
二进制文件。deps
属性
hello-world
目标告知 Bazel,hello-greet
库构建 hello-world
二进制文件。
在构建这个新版本的项目之前,您需要
目录中,运行以下命令切换到 cpp-tutorial/stage2
目录:
cd ../stage2
现在,您可以使用下面熟悉的命令构建新的二进制文件:
bazel build //main:hello-world
Bazel 再次生成了如下内容:
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 2.399s, Critical Path: 0.30s
现在,您可以测试新构建的二进制文件,该文件会返回另一个“Hello world
”:
bazel-bin/main/hello-world
如果您现在修改 hello-greet.cc
并重新构建项目,Bazel 只会重新编译
该文件。
通过查看依赖关系图,您可以看到 hello-world 依赖于 输入与之前相同,但 build 的结构有所不同:
摘要:第 2 阶段
现在,您已经构建了具有两个目标的项目。hello-world
目标 build
一个源文件并依赖于另一个目标 (//main:hello-greet
),该目标
构建两个额外的源文件。在下一部分中,您可以更进一步
然后添加另一个软件包
第 3 阶段:多个软件包
接下来的这一阶段会添加另一层复杂功能,并使用
多个软件包让我们来看看以下网址的结构和内容:
cpp-tutorial/stage3
目录:
└──stage3
├── main
│ ├── BUILD
│ ├── hello-world.cc
│ ├── hello-greet.cc
│ └── hello-greet.h
├── lib
│ ├── BUILD
│ ├── hello-time.cc
│ └── hello-time.h
└── WORKSPACE
您可以看到,现在有两个子目录,每个子目录都包含一个 BUILD
文件。因此,对于 Bazel,现在工作区包含两个软件包:lib
和
main
。
查看 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",
],
)
主软件包中的 hello-world
目标依赖于 hello-time
目标
在 lib
软件包中(因此是目标标签 //lib:hello-time
)- Bazel 知道
通过 deps
属性实现这一目的。您可以看到
图表:
为确保构建成功,请在 lib/BUILD
中创建 //lib:hello-time
目标
使用 visibility 属性对 main/BUILD
中的目标明确可见。
这是因为,在默认情况下,只有同一个
BUILD
文件。Bazel 使用目标可见性来防止库等问题
包含泄露到公共 API 的实现详情。
现在,构建项目的最终版本。切换到cpp-tutorial/stage3
目录中的内容:
cd ../stage3
再次运行以下命令:
bazel build //main:hello-world
Bazel 会生成类似于以下内容的内容:
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 0.167s, Critical Path: 0.00s
现在,测试本教程的最后一个二进制文件以获取最终的 Hello world
消息:
bazel-bin/main/hello-world
摘要:第 3 阶段
现在,您已经将项目构建为两个软件包,其中包含三个目标,并且了解了 它们之间的依赖关系,让您大胆创新,打造美好未来 项目和项目。在下一节中,您将了解如何 Bazel 的历程
后续步骤
现在,您已经使用 Bazel 完成了第一个基本构建,但这只是 start 的值。下面提供了更多资源,可帮助您继续学习 Bazel:
- 要继续专注于 C++,请阅读常见的 C++ build 用例。
- 要开始使用 Bazel 构建其他应用,请参阅相关教程 Java、Android 应用、 或 iOS 应用。
- 如需详细了解如何使用本地和远程仓库,请参阅 外部依赖项。
- 如需详细了解 Bazel 的其他规则,请参阅此参考指南。
祝您构建顺利!