【Bazel】C++編譯常見用例

在一個target中包含多個文件

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

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

在這個target中,Bazel會編譯BUILD文件所在目錄下的所有.cc和.h文件(不包括子目錄)。

includes可以進行傳遞

如果一個文件包括頭文件,那麼這個文件的規則也受到頭文件所包含的庫的影響咯?其實,我們只需要指定直接的依賴項爲依賴項即可。比如說,假如sandwich.h包含bread.h,bread.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"],
)
添加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"],
)

這一點對於加入外部依賴項非常有用。

包含外部庫

假設你正在使用Google Test.你可以在WORKSPACE文件中使用一種repo function來下載Google Test並且把它用於你的repo:

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屬性使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_rules依賴於@gtest/main。

編寫並運行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運行測試了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章