Bazel入门2:C++编译常见用例

1. 在一个target中包含多个文件

可以利用glob在单个target中包含多个文件,例如:

cc_library(
    name = "build-all-the-files",
    srcs = glob(["*.cc"])
    hdrs = glob(["*.h"]),
)

在这个target中,Bazel会编译BUILD文件所在目录下的所有.cc和.h文件(不包括子目录)。

2.includes可以进行传递

如果一个文件包括头文件,那么这个文件的规则也受到头文件所包含的库的影响咯?其实,我们只需要指定直接的依赖项为依赖项即可。比如说,假如sandwich.h包含bread.hbread.h又包含floor.h,那么sandwich.h是不用显式包含flour.h的,所以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"],
)

3. 添加include路径

有时不能(或不愿)让依赖文件的路径包含其相对于工作区根目录的路径。因为现有的库可能已经拥有了与工作区路径不匹配的目录。例如,假设有以下目录结构:

└── my-project
    ├── third_party
    │   └── some_lib
    │       ├── BUILD
    │       ├── include
    │       │   └── some_lib.h
    │       └── some_lib.cc
    └── WORKSPACE

Bazel 希望some_lib.h被include的时候是以third_party/some_lib/include/some_lib.h这一路径来include的,但是some_lib.cc包含它的时候用的路径却是include/some_lib.h。为了使包含路径有效, third_party/some_lib/BUILD需要指定some_lib是一个包含目录:

cc_library(
    name = "some_lib",
    srcs = ["some_lib.cc"],
    hdrs = ["some_lib.h"],
    copts = ["-Ithird_party/some_lib"],
)

这一点对于加入外部依赖项非常有用。

4. 包含外部库

假设你正在使用Google Test.你可以在WORKSPACE文件中使用一种new_ 的repo function来下载Google Test并且把它用于你的repo:

new_http_archive(
    name = "gtest",
    url = "https://github.com/google/googletest/archive/release-1.7.0.zip",
    sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0",
    build_file = "gtest.BUILD",
)

注意,如果目标位置已经包含了一个BUILD文件,则可以使用non-new_函数。

然后创建gtest.BUILD,一个用于编译Google Test的BUILD文件。Google Test有几个特殊要求使它的cc_library规则更加复杂:

  • googletest-release-1.7.0/src/gtest-all.cc 包含了googletest-release-1.7.0/src/中的所有其他文件,所以我们需要在编译时去掉它,否则会由于duplicate symbols而出现link errors。
  • 所使用的头文件都是相对于 googletest-release-1.7.0/include/目录的 ("gtest/gtest.h"),所以我们必须把这个文件加到include路径中。
  • 它还需要link pthread, 所以我们以 linkopt的方式加。

那么根据以上规则,BUILD文件可以写成这样:

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"
    ],
    linkopts = ["-pthread"],
    visibility = ["//visibility:public"],
)

这看起来有点乱:一切都加了个前缀googletest-1.7.0作为archive文件结构的副产品。我们可以通过添加strip_prefix属性使new_http_archive带上这个前缀:

new_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_rules依赖于@gtest/main

5. 编写并运行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",
        "//lib:hello-greet",
    ],
)

注意:为了使hello-greet对于hello-test可见,必须在./lib/BUILD添加"//test:__pkg__"可见属性。
现在可以用Bazel test运行测试了。

bazel test test:hello-test

这会得到如下的output:

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.

6.在预编译上添加依赖

如果你想使用一个库,但是你只有编译好的版本,包装在一个cc_library规则里,例如你只有头文件和一个.so文件

cc_library(
    name = "mylib",
    srcs = ["mylib.so"],
    hdrs = ["mylib.h"],
)

然后在工作区中其他的C ++程序,就可以使用这条规则了。

Reference

  1. Introduction to Bazel: Common C++ Build Use Cases

你可能感兴趣的:(c++编程)