在这里,您将找到一些使用Bazel构建C++项目的最常用用例。如果尚未执行此操作,请完成教程Bazel简介:构建C++项目,开始使用Bazel构建C++项目。
1 在target中包含多个文件
您可以在具有glob的单个target中包含多个文件。例如:
cc_library(
name = "build-all-the-files",
srcs = glob(["*.cc"]),
hdrs = glob(["*.h"]),
)
使用该target,Bazel将在与包含该target的BUILD
文件(子目录除外)相同的target中构建找到的所有.cc
和.h
文件。
2 使用transitive includes
如果文件包含header,则文件的规则应取决于该header的库。相反,仅需要将直接依赖项指定为依赖项。例如,假设sandwich.h
包括bread.h
,而bread.h
包括flour.h
。 sandwich.h
不包含flour.h
(谁想要sandwich中的flour?),因此BUILD
文件如下所示:
cc_library(
name = "sandwich",
srcs = ["sandwich.cc"],
hdrs = ["sandwich.h"],
deps = [":bread"],
)
cc_library(
name = "bread",
srcs = ["bread.cc"],
hdrs = ["bread.h"],
deps = [":flour"],
)
cc_library(
name = "flour",
srcs = ["flour.cc"],
hdrs = ["flour.h"],
)
这里,sandwich
库取决于bread
库,而bread
库取决于flour
库。
3 添加include路径
有时,您不能(或不想)在工作空间的根目录中包含路径。现有库可能已经包含一个include目录,该include目录与其在工作空间中的路径不匹配。例如,假设您具有以下目录结构:
└── my-project
├── legacy
│ └── some_lib
│ ├── BUILD
│ ├── include
│ │ └── some_lib.h
│ └── some_lib.cc
└── WORKSPACE
Bazel希望将some_lib.h
作为legacy/some_lib/include/some_lib.h
包含在内,但假设some_lib.cc
包含some_lib.h
。为了使include路径有效,legacy/some_lib/BUILD
将需要指定some_lib/include
目录为include目录:
cc_library(
name = "some_lib",
srcs = ["some_lib.cc"],
hdrs = ["include/some_lib.h"],
copts = ["-Ilegacy/some_lib/include"],
)
这对于外部依赖项特别有用,因为否则它们的头文件必须包含前缀/
。
3 包括外部库
假设您正在使用Google Test。您可以使用WORKSPACE
文件中的存储库功能之一来下载Google Test并将其在您的存储库中使用:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "gtest",
url = "https://github.com/google/googletest/archive/release-1.7.0.zip",
sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0",
build_file = "@//:gtest.BUILD",
)
注意:如果目标位置已包含BUILD
文件,则可以省略build_file
属性。
然后创建gtest.BUILD
,这是一个用于编译Google Test的BUILD
文件。 Google Test有几个“特殊”要求,这使其cc_library
规则更加复杂:
googletest-release-1.7.0/src/gtest-all.cc
#include
googletest-release-1.7.0/src/
中的所有其他文件,因此我们需要从编译中排除它,否则我们将收到重复符号的链接错误。- 它使用的头文件相对于
googletest-release-1.7.0/include/
目录(gtest/gtest.h
),因此我们必须将该目录添加到include路径中。 - 它需要在
pthread
中进行链接,因此我们将其添加为linkopt
。
因此,最终规则如下所示:
cc_library(
name = "main",
srcs = glob(
["googletest-release-1.7.0/src/*.cc"],
exclude = ["googletest-release-1.7.0/src/gtest-all.cc"]
),
hdrs = glob([
"googletest-release-1.7.0/include/**/*.h",
"googletest-release-1.7.0/src/*.h"
]),
copts = [
"-Iexternal/gtest/googletest-release-1.7.0/include",
"-Iexternal/gtest/googletest-release-1.7.0"
],
linkopts = ["-pthread"],
visibility = ["//visibility:public"],
)
这有点混乱:作为archive结构的副产品,所有内容都以googletest-release-1.7.0
为前缀。您可以通过添加strip_prefix
属性使http_archive
剥离此前缀:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "gtest",
url = "https://github.com/google/googletest/archive/release-1.7.0.zip",
sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0",
build_file = "@//:gtest.BUILD",
strip_prefix = "googletest-release-1.7.0",
)
然后gtest.BUILD
看起来像这样:
cc_library(
name = "main",
srcs = glob(
["src/*.cc"],
exclude = ["src/gtest-all.cc"]
),
hdrs = glob([
"include/**/*.h",
"src/*.h"
]),
copts = ["-Iexternal/gtest/include"],
linkopts = ["-pthread"],
visibility = ["//visibility:public"],
)
现在cc_
规则可以依赖于@gtest//:main
。
4 编写和运行C++测试
例如,我们可以创建一个测试./test/hello-test.cc
,例如:
#include "gtest/gtest.h"
#include "lib/hello-greet.h"
TEST(HelloTest, GetGreet) {
EXPECT_EQ(get_greet("Bazel"), "Hello Bazel");
}
然后为您的测试创建./test/BUILD
文件:
cc_test(
name = "hello-test",
srcs = ["hello-test.cc"],
copts = ["-Iexternal/gtest/include"],
deps = [
"@gtest//:main",
"//main:hello-greet",
],
)
请注意,为了使hello-greet
对hello-test
可见,我们必须在./main/BUILD
中的visibility
属性中添加//test:__pkg__
。
现在,您可以使用bazel test来运行测试。
bazel test test:hello-test
这将产生以下输出:
INFO: Found 1 test target...
Target //test:hello-test up-to-date:
bazel-bin/test/hello-test
INFO: Elapsed time: 4.497s, Critical Path: 2.53s
//test:hello-test PASSED in 0.3s
Executed 1 out of 1 tests: 1 test passes.
5 添加对预编译库的依赖
如果要使用仅具有已编译版本(例如, headers和.so
文件)的库,请将其包装在cc_library
规则中:
cc_library(
name = "mylib",
srcs = ["mylib.so"],
hdrs = ["mylib.h"],
)
这样,工作空间中的其他C++ targets可以依赖此规则。