1.bazel介绍
Bazel是一个开源的构建和测试工具,类似于Make、Maven和Gradle。Bazel支持多种语言的项目,并为多种平台构建输出。Bazel支持跨多个存储库和大量用户的大型代码库。
2.bazel安装
bazel安装有两种方法,一种是通过源安装,另一种是通过下载安装包本地安装。官网安装教程。本人选取的是第一种安装方式。
第一步:添加Bazel分发URI作为包源
sudo apt install curl gnupg
curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
第二步:安装或更新bazel
sudo apt update && sudo apt install bazel
如果已经安装了bazel,需要升级到最新版本的bazel
sudo apt update && sudo apt full-upgrade
安装特定版本的bazel,例如需要安装bazel-1.0.0
sudo apt install bazel-1.0.0
3.bazel构建工程
bazel里面有构建C++工程的例子,可以通过命令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
下面对这个C++工程进行解读。
3.1 设置工作区
在构建项目将之前,需要设置项目工作区,这个工作区就是一个包含项目源文件的目录和bazel构建输出,bazel识别WORKSPACE
和BUILD
这两个文件。
- WORKSPACE文件将目录及其内容标识为Bazel工作区,位于项目目录结构的根目录中
- BUILD文件会有一个或者多个,分别构建项目的不同部分。
3.2 理解BUILD文件
BUILD
文件包含Bazel的几种不同类型的指令。最重要的类型是构建规则,它告诉Bazel如何构建所需的输出,比如可执行的二进制文件或库。构建文件中构建规则的每个实例称为目标,并指向一组特定的源文件和依赖项。一个目标也可以指向其他目标。
看一下cpp-tutorial/stage1/main
中BUILD文件:
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
)
在我们的示例中,hello-world目标实例化了Bazel的内置cc_binary规则。规则告诉bazel建立一个独立的可执行二进制hello world.cc源文件没有依赖性。
3.3 使用bazel编译项目
构建示例项目。切换到cpp-tutorial/stage1目录,运行以下命令:
bazel build //main:hello-world
注意目标标签——//main:部分是构建文件相对于工作区根的位置,hello-world是我们在构建文件中命名的目标。
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/main/hello-world
3.4 查看依赖图
成功的构建将在构建文件中显式地声明其所有依赖项。Bazel使用这些语句创建项目的依赖关系图,从而实现精确的增量构建。
将示例项目的依赖关系可视化。首先,生成依赖关系图的文本表示(在工作区根运行此命令):
bazel query --notool_deps --noimplicit_deps "deps(//main:hello-world)"
--output graph
上面的命令告诉Bazel寻找target //main:hello-world的所有依赖项(不包括主机和隐式依赖项),并将输出格式化为图形。
然后,将文本粘贴到GraphViz中。
然后你可以通过管道直接输出到xdot来生成和查看图形:
sudo apt update && sudo apt install graphviz xdot
然后你可以通过管道直接输出到xdot来生成和查看图形:
xdot <(bazel query --notool_deps --noimplicit_deps "deps(//main:hello-world)"
--output graph)
如你所见,这个示例项目的第一阶段有一个单一的目标,它构建一个没有附加依赖关系的单一源文件:
接下来,增加点难度
3.5 多个目标文件(target)编译
将示例项目构建分成两个目标。看一下cpp-tutorial/stage2/主目录中的构建文件:
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首先构建hello-greet库(使用Bazel内置的cc_library规则),然后构建hello-world二进制文件。hello-world目标中的deps属性告诉Bazel需要hello-greet库来构建hello-world二进制文件。
切换到cpp-tutorial/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
现在测试新构建的二进制文件:
bazel-bin/main/hello-world
如果修改hello-greet.cc和重建项目,Bazel将只重新编译该文件。
看看依赖关系图,你可以看到hello-world和以前一样依赖于相同的输入,但是构建的结构是不同的:
现在已经构建了带有两个目标的项目。hello-world目标构建一个源文件,并依赖于另一个目标(//main:hello-greet)。
3.6 多个项目包(packages)编译
现在将项目分离成多个包,看一下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目标依赖于lib包中的hello-time目标(因此是目标标签//lib:hello-time)—Bazel通过deps属性知道这一点。看看依赖关系图:
请注意,为了使构建成功,我们使用visibility属性使lib/ build中的//lib:hello-time目标对main/ build中的目标显式可见。这是因为在默认情况下,目标只对同一构建文件中的其他目标可见。(Bazel使用目标可见性来防止诸如库中包含的实现细节泄漏到公共api等问题。)
切换到cpp-tutorial/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
现在测试新构建的二进制文件:
bazel-bin/main/hello-world
到此具有三个目标的两个包的项目构建完成,并了解了它们之间的依赖关系。