Bazel教程

本文介绍了如何使用Bazel构建C++项目,包括创建源文件、添加BUILD文件、处理依赖关系等步骤,并演示了一个简单的helloworld示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Bazel入门

上一篇博文中讲解了以Java工程bazel的使用,接下来讲解在C++工程中bazel的使用。

C++教程
*建立工作区*
假设目录中已经有了一个项目,对应 ~/gitroot/my-project/ 目录,先创建一个空的 ~/gitroot/my-project/WORKSPACE 工作区配置文件,用于表示这是Bazel项目对应的根目录。 在这里以下面目录结构创建一个小的hello world工程:

└── my-project
    ├── lib
    │   ├── BUILD
    │   ├── hello-greet.cc
    │   └── hello-greet.h
    ├── main
    │   ├── BUILD
    │   ├── hello-time.cc
    │   ├── hello-time.h
    │   └── hello-world.cc
    └── WORKSPACE

*创建源文件*
使用下面的命令创建所需的源文件:

$ # If you're not already there, move to your workspace directory.
$ cd ~/gitroot/my-project
$ mkdir ./main
$ cat > main/hello-world.cc <<'EOF'
#include "lib/hello-greet.h"
#include "main/hello-time.h"
#include <iostream>
#include <string>

int main(int argc, char** argv) {
  std::string who = "world";
  if (argc > 1) {
    who = argv[1];
  }
  std::cout << get_greet(who) <<std::endl;
  print_localtime();
  return 0;
}
EOF
$ cat > main/hello-time.h <<'EOF'
#ifndef MAIN_HELLO_TIME_H_
#define MAIN_HELLO_TIME_H_

void print_localtime();

#endif
EOF
$ cat > main/hello-time.cc <<'EOF'
#include "main/hello-time.h"
#include <ctime>
#include <iostream>

void print_localtime() {
  std::time_t result = std::time(nullptr);
  std::cout << std::asctime(std::localtime(&result));
}
EOF
$ mkdir ./lib
$ cat > lib/hello-greet.h <<'EOF'
#ifndef LIB_HELLO_GREET_H_
#define LIB_HELLO_GREET_H_

#include <string>

std::string get_greet(const std::string &thing);

#endif
EOF
$ cat > lib/hello-greet.cc <<'EOF'
#include "lib/hello-greet.h"
#include <string>

std::string get_greet(const std::string& who) {
  return "Hello " + who;
}
EOF

*添加BUILD文件*
从上面的源代码可知,main/hello-world.cc要用到lib/hello-greet.h和main/hello-time.h。首先在lib目录下为hello-greet.cc创建BUILD:

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
    visibility = ["//main:__pkg__"],
)

注意 visibility = [“//main:pkg“] 表示hello-greet对于main/BUILD是可见的。接下来在main目录下创建BUILD文件:

cc_library(
    name = "hello-time",
    srcs = ["hello-time.cc"],
    hdrs = ["hello-time.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-time",
        "//lib:hello-greet",
    ],
)

注意当依赖的包在同一目录下,只需用 :hello-time,当依赖的包在不同的目录下,需要用全路径://lib:hello-greet。
现在可以建立hello world 的C++二进制程序了:

$ bazel build main:hello-world
INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 2.869s, Critical Path: 1.00s
$ ./bazel-bin/main/hello-world
Hello world
Thu Jun 23 18:51:46 2016
$ ./bazel-bin/main/hello-world Bazel
Hello Bazel
Thu Jun 23 18:52:10 2016

恭喜你刚刚成功构建了第一个Bazel项目了!

*传递依赖Transitive includes)*
如果一个文件包含一个头文件,那么这个文件的规则也应该依赖与头文件的库,相反的,只有直接依赖需要被指定为依赖。例如,假设sandwich.h包括bread.h,而且bread.h包括flour.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"],
)

在这里,sandwich库依赖于bread库,而bread库依赖于flour库。
*添加包含路径(Adding include paths)*
有时不能(或不愿)让依赖文件包含在工作区根目录的路径。现有的库可能已经拥有了包括与工作空间不匹配路径的目录。例如,假设有以下目录结构:

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

Bazel 希望 some_lib.h被包含在hird_party/some_lib/include/some_lib.h中,但假定some_lib.c要依赖于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文件中用一种新的库函数下载Google Test,使它在库中更适合:

new_http_archive(
    name = "gtest",
    url = "https://googletest.googlecode.com/files/gtest-1.7.0.zip",
    sha256 = "247ca18dd83f53deb1328be17e4b1be31514cedfc1e3424f672bf11fd7e0d60d",
    build_file = "gtest.BUILD",
)

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

 - gtest-1.7.0/src/gtest-all.cc #includes all of the other files in gtest-1.7.0/src/, so we need to exclude it from the compile or we'll get link errors for duplicate symbols.
 - It uses header files that relative to the gtest-1.7.0/include/ directory ("gtest/gtest.h"), so we must add that directory the include paths.
 - It needs to link in pthread, so we add that as a linkopt.

最终的规则像这样:

cc_library(
    name = "main",
    srcs = glob(
        ["gtest-1.7.0/src/*.cc"],
        exclude = ["gtest-1.7.0/src/gtest-all.cc"]
    ),
    hdrs = glob([
        "gtest-1.7.0/include/**/*.h",
        "gtest-1.7.0/src/*.h"
    ]),
    copts = [
        "-Iexternal/gtest/gtest-1.7.0/include"
    ],
    linkopts = ["-pthread"],
    visibility = ["//visibility:public"],
)

这有点凌乱:一切都以前缀GTEST-1.7.0作为体系结构的副产品。可以通过添加strip_prefix属性使new_http_archive带这个前缀:

new_http_archive(
    name = "gtest",
    url = "https://googletest.googlecode.com/files/gtest-1.7.0.zip",
    sha256 = "247ca18dd83f53deb1328be17e4b1be31514cedfc1e3424f672bf11fd7e0d60d",
    build_file = "gtest.BUILD",
    strip_prefix = "gtest-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依赖于//external:gtest/main。

编写并运行C++测试程序
例如,可以创建一个测试程序 ./test/hello-test.cc:

#include "gtest/gtest.h"
#include "lib/hello-greet.h"

TEST(FactorialTest, Negative) {
  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运行测试了。

$ 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.

在预编译上添加依赖
如果你想使用一个库,你只有(例如,头和一个.so)包装在一个cc_library规则的编译版本:

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

然后在工作区中其他的C ++程序,可以依赖这条规则。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值