Google 自家的构建工具,相比 CMake 的优势,主要是多语言构建和相比 Make 语法有更好的可读性。最著名的使用 Bazel 的项目大概是 Tensorflow 吧。目前生态上还是不太好和 CMake 相提并论,毕竟下面的命令已经像魔法一样刻在 Unix 的世界里。
./configure
make
make install
Mac 用户当然是使用 brew 解决了:
brew install bazel
安装参考官网吧~
要使用 Bazel,首先在项目顶层目录创建一个名为 WORKSPACE
的文件。值得一提的是 Bazel 推崇的是 Monorepo ,建议的项目结构是把代码、测试代码放到一起。在 C/C++ 中,意味着更加推荐头文件、源码文件、测试文件都放到同一个目录下。如果按 include
、src
、lib
、test
拆分,在 Bazel 的设计下,反而会增加难度。
因此我们就创建一个 src
文件夹,并在该文件夹下建立 BUILD
文件。WORKSPACE
顾名思义是工作区,BUILD
就是具体的构建目标。
.
├── WORKSPACE
└── src
├── BUILD
在 src
下创建一个最简单的 C++ 文件:
#include
using namespace std;
int main(int argc, char* argv[])
{
cout << "hello, bazel" << endl;
return 0;
}
这个代码会编译成可执行文件,而不是库文件,因此在 BUILD
中写入第一个构建目标:
cc_binary(
name = "main",
srcs = ["main.cpp"]
)
接下来,就可以使用 Bazel 进行构建了:
bazel build src:main
如果终端的工作目录在 src
,可以省略 src
,bazel build :main
:
INFO: Analyzed target //src:main (14 packages loaded, 105 targets configured).
INFO: Found 1 target...
Target //src:main up-to-date:
bazel-bin/src/main
INFO: Elapsed time: 4.106s, Critical Path: 0.86s
INFO: 2 processes: 2 darwin-sandbox.
INFO: Build completed successfully, 5 total actions
之后就使用 bazel run src:main
运行构建好的程序:
INFO: Analyzed target //src:main (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //src:main up-to-date:
bazel-bin/src/main
INFO: Elapsed time: 0.132s, Critical Path: 0.01s
INFO: 0 processes.
INFO: Build completed successfully, 1 total action
INFO: Build completed successfully, 1 total action
hello, bazel
Bazel 会生成几个目录,可以在 bazel-bin 中找到构建好的程序。
Google Test 是 Google 开发的一套 C++ 测试框架,原生支持了 Bazel 构建。为了自动导入所需的依赖(一般只有支持的 Bazel 的项目才可以),我们在 WORKSPACE 中写入针对 google test 的依赖条目。
workspace(name = "com_googletest_driven")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Google Test
http_archive(
name = "com_google_googletest",
urls = ["https://github.com/google/googletest/archive/release-1.10.0.zip"],
strip_prefix = "googletest-release-1.10.0",
sha256 = "94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91",
)
workspace
那一行可以简单的理解为项目名称。load
用来发现本地路径和加载对应的符号规则,这里引入的 http_archive
用来引用和检索打包好的 Bazel 库。一旦检索到压缩的仓库,就会自动解压,里面包含的规则、构建目标都可以直接在项目中使用。
strip_prefix
就是解压后的文件夹名称,用来指定抽取的文件目录。sha256
对下载的内容进行校验,可以极大的增加依赖的可入性。在 Linux 中,可以使用 sha256sum
获得该项内容;Mac OSX 上使用的 shasum -a 256
。
可以将 curl
和 shasum
组合起来获得所需要的 sha256 校验码:
curl -L https://github.com/google/googletest/archive/release-1.10.0.zip | shasum -a 256
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91
由于 Google Test 原生支持了 Bazel,这样就已经集成了 Google Test。
Bazel 原生支持 test
目标的构建,编写基于 Google Test 的测试非常简单,甚至都不需要添加测试启动的入口。
我们试着构建一个最简单的测试用例,新建一个库的构建目标,在 src 中的 BUILD 中新增下面内容:
cc_library(
name = "add",
hdrs = ["add.hpp"],
srcs = ["add.cpp"]
)
hdrs
显然就是公开的头文件。
示例代码的内容:
// add.hpp
#pragma once
int add(int a, int b);
// add.cpp
#include "add.hpp"
int add(int a, int b) {
return a + b;
}
再创建一个 add_test.cpp 文件,引入 google test:
#include "gtest/gtest.h"
#include "add.hpp"
TEST(testAdd, test0)
{
EXPECT_EQ(15, add(5, 10));
}
为了构建测试,在 BUILD 中新增测试目标:
cc_test(
name = "test_add",
srcs = ["hello_test.cpp"],
deps = [
":add",
"@com_google_googletest//:gtest_main",
],
)
deps
指明了依赖的代码和 google test 库。导入外部依赖库的规则是这样的:
至此,就完成了一个测试环境的搭建,由于 Bazel 原生的支持,不需要写类似下面的测试入口方法来运行测试:
#include "gtest/gtest.h"
int main(int argc, char* argv[])
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
接下来,直接使用 bazel run src:test_add
,还可以使用 bazel test src:test_add
直接获取测试结果。
bazel run :test_add
INFO: Analyzed target //src:test_add (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //src:test_add up-to-date:
bazel-bin/src/test_add
INFO: Elapsed time: 0.151s, Critical Path: 0.00s
INFO: 0 processes.
INFO: Build completed successfully, 1 total action
INFO: Build completed successfully, 1 total action
exec ${PAGER:-/usr/bin/less} "$0" || exit 1
Executing tests from //src:test_add
-----------------------------------------------------------------------------
Running main() from gmock_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from testAdd
[ RUN ] testAdd.test0
[ OK ] testAdd.test0 (0 ms)
[----------] 1 test from testAdd (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (1 ms total)
[ PASSED ] 1 test.
Bazel 最大的优势还是多语言构建,当然 Bazel 的功能不止这些,还有类似包可见性等概念并没有涉及。不过,还是可以看到 Bazel 的使用还是比较简单的(如果原生支持)。