简介
刚开始接触 Bazel?你来对地方了。请查看此“First Build”教程,简要了解如何使用 Bazel。本教程定义了在 Bazel 上下文中使用的关键术语,并介绍了 Bazel 工作流的基础知识。从您需要的工具开始,您将构建和运行三个越来越复杂的项目,并了解这些项目如何和为何变得更加复杂。
虽然 Bazel 是一个支持多语言构建的构建系统,但本教程以 C++ 项目为例,提供了适用于大多数语言的一般准则和流程。
预计所需时长:30 分钟。
前提条件
首先安装 Bazel(如果尚未安装)。本教程使用 Git 进行源代码控制,因此为了获得最佳效果,也请安装 Git。
接下来,在您选择的命令行工具中运行以下命令,从 Bazel 的 GitHub 代码库中检索示例项目:
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
有三组文件,每组文件代表本教程中的一个阶段。在第一阶段,您将构建位于单个软件包中的单个目标。在第二阶段,您将通过单个软件包构建二进制文件和库。在第三阶段(也是最后一个阶段),您将构建一个包含多个软件包的项目,并使用多个目标进行构建。
摘要:简介
通过安装 Bazel(和 Git)并克隆本教程的代码库,您已经为使用 Bazel 的第一次构建奠定了基础。请继续下一部分定义一些术语,并设置您的工作区。
开始使用
设置工作区
您需要先设置项目的工作区,然后才能构建项目。工作区是一个目录,其中保存有项目的源文件和 Bazel 的构建输出。它还包含以下重要文件:
,用于将目录及其内容标识为 Bazel 工作区,并位于项目目录结构的根目录中。WORKSPACE
file- 一个或多个
,用于告知 Bazel 如何构建项目的不同部分。工作区中包含BUILD
filesBUILD
文件的目录就是一个软件包。(本教程后面会详细介绍软件包。)
在今后的项目中,如需将某个目录指定为 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 阶段:单个目标、单个软件包
现在该构建项目的第一部分了。下面列出了项目第 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 阶段的依赖关系图:
摘要:第 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
二进制文件。hello-world
目标中的 deps
属性告知 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
依赖于一个名为 hello-greet
的额外输入:
摘要:第 2 阶段
现在,您已经使用两个目标构建了项目。hello-world
目标会构建一个源文件,并依赖于另一个目标 (//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",
],
)
main 软件包中的 hello-world
目标依赖于 lib
软件包中的 hello-time
目标(因此是目标标签 //lib:hello-time
)- Bazel 通过 deps
属性知道这一点。您可以看到这反映在依赖关系图中:
若要构建成功,请使用可见性属性让 lib/BUILD
中的 //lib:hello-time
目标对 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。
后续步骤
现在,您已经使用 Bazel 完成了您的第一个基本构建,但这仅仅是开始。下面列出了更多资源,以便您继续学习 Bazel:
- 如需继续专注于 C++,请参阅常见的 C++ 构建用例。
- 如需开始使用 Bazel 构建其他应用,请参阅 Java、Android 应用或 iOS 应用教程。
- 如需详细了解如何使用本地和远程代码库,请参阅外部依赖项。
- 如需详细了解 Bazel 的其他规则,请参阅此参考指南。
祝您使用愉快!