Bazel简介:构建C ++项目

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bazel简介:构建C ++项目相关的知识,希望对你有一定的参考价值。

参考技术A 在本教程中,您将学习使用Bazel构建C ++应用程序的基础知识。您将设置工作区并构建一个简单的C ++项目,该项目说明了Bazel的关键概念,例如目标和 BUILD 文件。完成本教程后,请查看 Common C ++ Build Use Cases, 以获取有关更高级概念(如编写和运行C ++测试)的信息。

预计完成时间:30分钟。

在本教程中,您将学习如何:

要开始本教程,请先 安装Bazel( 如果尚未安装)。然后,从Bazel的GitHub存储库中检索示例项目:

本教程的示例项目在 examples/cpp-tutorial 目录中,其结构如下:

如您所见,共有三组文件,每组文件代表本教程中的一个阶段。在第一阶段,您将构建一个驻留在单个程序包中的单个目标。在第二阶段,您将把您的项目分成多个目标,但将其保存在一个程序包中。在第三阶段(也是最后一个阶段)中,您将把您的项目分成多个包,并使用多个目标进行构建。

在构建项目之前,您需要设置其工作区。工作区是一个目录,其中包含项目的源文件和Bazel的构建输出。它还包含Bazel认为特殊的文件:

要将目录指定为Bazel工作区,请 WORKSPACE 在该目录中创建一个空文件 。

Bazel构建项目时,所有输入和依赖项必须位于同一工作空间中。除非链接,否则位于不同工作空间中的文件彼此独立,这超出了本教程的范围。

一个 BUILD 文件包含几种不同类型的用于Bazel指令。最重要的类型是 构建规则(build rule) ,该 规则(rule) 告诉Bazel如何构建所需的输出,例如可执行二进制文件或库。 BUILD 文件中构建规则的每个实例都称为 目标(target) 并指向一组特定的源文件和依赖项。一个目标也可以指向其他目标。

看一下目录 BUILD 中的 cpp-tutorial/stage1/main 文件:

在我们的示例中, hello-world 目标实例化了Bazel内置的 cc_binary rule 。该规则告诉Bazel从 hello-world.cc 源文件构建一个自包含的可执行二进制文件,而没有任何依赖关系。

目标中的属性明确声明其依赖项和选项。虽然该 name 属性是强制性的,但许多是可选的。例如,在 hello-world 目标中 name 是不言自明的, 其 srcs 属性指定Bazel从中构建目标的源文件。

让我们构建您的示例项目。切换到 cpp-tutorial/stage1 目录并运行以下命令:

注意目标标签-该 //main: 部分是 BUILD 文件相对于工作空间根目录的位置, hello-world 也是我们在 BUILD 文件中命名该目标的名称。(您将在本教程的最后详细了解目标标签。)

Bazel产生类似于以下内容的输出:

恭喜,您刚刚建立了第一个Bazel目标!Bazel将构建输出放置 bazel-bin 在工作空间根目录中的目录中。浏览其内容以了解Bazel的输出结构。

现在测试您新构建的二进制文件:

成功的构建具有在 BUILD 文件中明确声明的所有依赖项。Bazel使用这些语句来创建项目的依赖关系图,从而实现准确的增量构建。

让我们可视化示例项目的依赖关系。首先,生成依赖关系图的文本表示(在工作区根目录运行命令):

上面的命令告诉Bazel查找目标的所有依赖关系 //main:hello-world (不包括主机和隐式依赖关系),并将输出格式化为图形。

然后,将文本粘贴到 GraphViz中 。

在Ubuntu上,可以通过安装GraphViz和xdot Dot Viewer在本地查看图形:

然后,您可以通过将上面的文本输出直接传递到xdot来生成和查看图形:

如您所见,示例项目的第一阶段有一个目标,该目标构建一个没有附加依赖项的源文件:

既然您已经设置了工作区,构建了项目并检查了其依赖性,那么让我们增加一些复杂性。

尽管单个目标足以满足小型项目的需要,但您可能希望将较大的项目拆分为多个目标和程序包,以实现快速增量构建(即,仅重建更改的内容)并通过同时构建项目的多个部分来加快构建速度。

让我们将示例项目构建分为两个目标。看一下 目录 BUILD 中的 cpp-tutorial/stage2/main 文件:

BUILD Bazel 使用此文件首先构建 hello-greet 库(使用Bazel的内置 cc_library 规则 ),然后构建 hello-world 二进制文件。目标中的 deps 属性 hello-world 告诉Bazel,该 hello-greet 库是构建 hello-world 二进制文件所必需的。

让我们构建这个项目的新版本。切换到 cpp-tutorial/stage2 目录并运行以下命令:

Bazel产生类似于以下内容的输出:

现在测试您新构建的二进制文件:

如果现在修改 hello-greet.cc 并重建项目,Bazel将仅重新编译该文件。

查看依赖关系图,您可以看到它 hello-world 依赖与以前相同的输入,但是构建的结构不同:

您现在已经用两个目标构建了该项目。的 hello-world 目标建立一个源文件,并依赖于目标( //main:hello-greet ),它建立两个附加的源文件。

现在让我们将项目分成多个包。看一下 cpp-tutorial/stage3 目录的内容:

注意,我们现在有两个子目录,每个子目录都包含一个 BUILD 文件。因此,对于Bazel,工作空间现在包含两个包, lib 和 main 。

看一下 lib/BUILD 文件:

并在 main/BUILD 文件:

如上所示, hello-world 在目标 main 包依赖于 hello-time 目标 lib 包(因此目标标签 //lib:hello-time ) -Bazel通过知道这个 deps 属性。看一下依赖图:

注意,为使构建成功,我们使用 属性使 //lib:hello-time 目标对于目标 lib/BUILD 明确可见。这是因为默认情况下,目标仅对同一文件中的其他目标可见。(Bazel使用目标可见性来防止诸如包含实现详细信息的库之类的问题泄漏到公共API中。) main/BUILD``visibility``BUILD

让我们构建项目的最终版本。切换到 cpp-tutorial/stage3 目录并运行以下命令:

Bazel产生类似于以下内容的输出:

现在测试新构建的二进制文件:

现在,您已经将项目构建为带有三个目标的两个程序包,并了解了它们之间的依赖性。

在 BUILD 文件中和命令行中,Bazel使用 标签 来引用目标(例如 //main:hello-world 或) //lib:hello-time 。它们的语法是:

如果目标是规则目标,则 path/to/package 是包含 BUILD 文件的目录的路径,并且 target-name 是您在 BUILD 文件中命名目标( name 属性)的名称。如果目标是文件目标,则 path/to/package 是包根目录的路径,并且 target-name 是目标文件的名称,包括其完整路径。

在存储库根目录引用目标时,包路径为空,只需使用即可 //:target-name 。在同一 BUILD 文件中引用目标时,您甚至可以跳过 // 工作空间的根标识符,而只需使用 :target-name 。

恭喜你!您现在知道了使用Bazel构建C ++项目的基础知识。接下来,阅读最常见的 C ++构建用例 。然后,检查以下内容:

构建愉快!

以上是关于Bazel简介:构建C ++项目的主要内容,如果未能解决你的问题,请参考以下文章

一文学会使用Bazel构建C++项目

一文学会使用Bazel构建C++项目

一文学会使用Bazel构建C++项目

简单的 C/C++ 项目自动化构建--Xmake推荐

maven 之maven简介及安装

cmake与make的项目构建快速上手