简介
第一次使用 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
│ └── 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 中的单个目标。在第二阶段,您需要 通过单个软件包同时构建二进制文件和库。在第三个区域,也是最后一个 阶段时,您将构建一个包含多个软件包的项目,并使用 多个目标。
摘要:简介
通过安装 Bazel(和 Git)并克隆本教程的代码库 已经为您使用 Bazel 完成首次构建奠定了基础。继续前往下一条 部分定义一些术语 工作区。
使用入门
您需要先设置项目工作区,然后才能构建项目。工作区 是一个目录,用于保存项目的源文件和 Bazel 的构建输出。 它还包含以下重要文件:
MODULE.bazel
文件,用于将目录及其内容标识为 Bazel 工作区,位于项目目录的根目录 结构。您还可以在此目录中指定外部依赖项。- 一个或多个
BUILD
文件,用于将这些文件告诉 Bazel 如何构建项目的不同部分。此存储分区中的 包含BUILD
文件的工作区是 package 中的可用属性。(有关软件包的详细信息 )。
在以后的项目中,要将目录指定为 Bazel 工作区,
空文件(名为 MODULE.bazel
)。在此
那么每个阶段都已经有一个 MODULE.bazel
文件。
了解 BUILD 文件
BUILD
文件包含几种不同类型的 Bazel 说明。每个
BUILD
个文件至少需要一个
规则作为一组指令,
告知 Bazel 如何构建所需的输出,例如可执行二进制文件
或库。BUILD
文件中的构建规则的每个实例都称为
target 并指向特定的
一组源文件
依赖项。目标可以
也会指向其他目标
看一下 cpp-tutorial/stage1/main
目录中的 BUILD
文件:
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
)
在我们的示例中,hello-world
目标会实例化 Bazel 的内置
cc_binary
规则。规则
会告诉 Bazel,使用
hello-world.cc
>无依赖项的源文件
摘要:使用入门
现在,您已经熟悉了一些关键术语,以及这些术语在 常规项目和 Bazel在下一部分中,您将构建和测试 项目的第 1 阶段。
第 1 阶段:单个目标,单个软件包
现在该构建项目的第一部分了。作为直观的参考, 项目“第一阶段”部分的结构是:
examples
└── cpp-tutorial
└──stage1
├── main
│ ├── BUILD
│ └── hello-world.cc
└── MODULE.bazel
运行以下命令以移至 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
│ └── MODULE.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",
],
)
借助此 BUILD
文件,Bazel 首先构建了 hello-greet
库(使用
Bazel 的内置 cc_library
规则),则
hello-world
二进制文件。hello-world
目标中的 deps
属性用于告知
构建 hello-world
所需的 hello-greet
库所需的 Bazel
二进制文件
在构建这个新版本的项目之前,您需要
目录中,运行以下命令切换到 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
目标 build
一个源文件并依赖于另一个目标 (//main:hello-greet
),该目标
构建两个额外的源文件。在下一部分中,您可以更进一步
然后添加另一个软件包
第 3 阶段:多个软件包
接下来的这一阶段会添加另一层复杂功能,并使用
多个软件包先看一下 v3 组件的结构和内容,
cpp-tutorial/stage3
目录:
└──stage3
├── main
│ ├── BUILD
│ ├── hello-world.cc
│ ├── hello-greet.cc
│ └── hello-greet.h
├── lib
│ ├── BUILD
│ ├── hello-time.cc
│ └── hello-time.h
└── MODULE.bazel
您可以看到,现在有两个子目录,每个子目录都包含一个 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 的其他规则,请参阅此参考文档 指南。
祝您构建顺利!