bazel C++语法入门

bazel的所有代码都在当前工程,每个工程都是一个 WORKSPACE 。
每个WORKSPACE下有多个BUILD文件。
BUILD内是多个targets,这些targets都是以starlark语言声明。

Starlark语言

  • 和python很像。
  • 线程安全
  • 数据类型有 None, bool, dict, function, int, list, string, depset, struct
  • 可变数据结构有 lists 和 dicts

命令行

规则

bazel [<startup options>] <command> [<args>]

bazel [<startup options>] <command> [<args>] -- [<target patterns>]

命令行参数文档

工作原理

  • 加载与target有关的BUILD文件
  • 分析inputs和dependencies,生成 action graph
  • 执行graph,产出outputs

action graph: bazel依赖这个图来追踪文件变化,以及是否需要重新编译,并且还可以为用户提供代码之间的依赖关系图。

依赖声明

  • 同一个文件BUILD之内
cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
    ],
)
  • 不同BUILD文件之间
cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

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

:hello-time 定义在 lib 目录
不同的目录BUILD在bazel中被称为 不同的package

  • 可见性
    同一个package内的targets默认互相可见
    不同package之间targets的可见性需要手动定义
cc_library(
    name = "hello-time",
    srcs = ["hello-time.cc"],
    hdrs = ["hello-time.h"],
    visibility = ["//main:__pkg__"],
)

可以在每个package的BUILD文件顶部声明其中的targets对其他包的默认可见性

package(
    default_visibility = [
        "//tensorflow_serving:internal",
    ],
    features = ["-layering_check"],
)

对所有包可见声明如下

cc_proto_library(
    name = "cc_echo_c++_proto",
    deps = [
        ":echo_c++_proto",
    ],
    visibility = [
        "//visibility:public",
    ],
)

target

有2种

  • rule target,比如 cc_library
  • file target

C++ 最佳实践

BUILD文件

  • 每个BUILD文件包含一个 cc_library 规则目标
  • 尽可能细粒度C++库,以提高并行速度,并减少增量编译
  • 如果srcs中只有一个文件,那么 cc_library的名字和这个文件名相同,比如:
   cc_library(
       name = "mylib",
       srcs = ["mylib.cc"],
       hdrs = ["mylib.h"],
       deps = [":lower-level-lib"]
   )
  • 每个library都有个单独的test target,并且以_test结尾命名这个target和测试文件名,比如
   cc_test(
       name = "mylib_test",
       srcs = ["mylib_test.cc"],
       deps = [":mylib"]
   )

include路径

  • 所有include路径都相对于WORKSPACE目录
  • 非系统目录用 #include "foo/bar/baz.h", 系统目录用 #include <foo/bar/baz.h>
  • 不要使用 ...
  • 对第三方库可以使用 include_prefixstrip_include_prefix

有时候依赖第三方库的时候,这些库里的文件已有的include path如果放到当前目录,会不符合从workspace root引用文件的规则,就需要添加目录,比如下面的目录

└── 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现在是"include/some_lib.h"这样包含的,要使这个include path有效,需要按如下方式指定路径(待验证):

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

包含多个文件

使用glob

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

依赖处理

  • bazel中的依赖不传递解析

如果包含了其他头文件,就要把这个头文件的target包含进来。这个头文件内部的include则不用管。比如

sandwich依赖bread,bread依赖flour,但sandwich不依赖flour。

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"],
)
  • 单个头文件,如果没有实现,也需要定义target,比如
cc_library(
    name = "source_adapter",
    hdrs = ["source_adapter.h"],
    visibility = [
        "//visibility:public",
    ],
    deps = [
        ":loader",
        ":servable_data",
        ":source",
        ":storage_path",
        ":target",
        "//tensorflow_serving/util:class_registration",
        "@org_tensorflow//tensorflow/core:lib",
    ],
)

包含外部库

假设我们要使用Google Test,可以在WORKSPACE中这样指定:

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,由于google test比较特殊的需求,所以它的编译规则会更复杂,特殊性在于:

  • googletest-release-1.7.0/src/gtest-all.cc #includegoogletest-release-1.7.0/src/下的所有文件,所以需要把这个文件排除掉
  • 它的头文件都是相对于这个目录的googletest-release-1.7.0/include/,比如"gtest/gtest.h",所以需要把这个目录加到copts的-I选项中
  • 需要链接pthread

所以,最终编译规则如下:

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"],
)

这个看起来有点乱,因为里面包含了那个版本目录名,这个名字可以在new_http_archive中使用strip_prefix去掉:

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

更详细的cc rule说明参考 cc rules

编写测试用例

创建文件 ./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-greethello-test可见,需要在 ./lib/BUILD文件中添加属性visibility,值为 //test:__pkg__

运行测试用例:

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.

该部分来自于 bazel C++ use case

包含预编译的库

  • 动态库
cc_library(
    name = "mylib",
    srcs = ["mylib.so"],
    hdrs = ["mylib.h"],
)

处理外部依赖

Working with external dependencies

依赖bazel工程

依赖非bazel工程

需要编写BUILD文件

依赖隐藏(Shadowing)

  • 依赖同一个package的不同版本

其他

getting started bazel C++

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 简介 Bazel是一个类似于Make,Maven和Gradle的开源构建和测试工具。Bazel支持多种语言混编的项...
    小村医阅读 34,593评论 0 10
  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,665评论 0 3
  • ANT build.xml文件详解(一) Ant的概念 可能有些读者并不连接什么是Ant以及入可使用它,但只要使用...
    SkTj阅读 3,930评论 0 2
  • i7@pc:~$ sudo apt-get install libgtest-dev [sudo] i7 的密码:...
    Ubuntu_2017阅读 3,132评论 0 0
  • 原文:http://www.bazel.io/docs/cpp.html 译者:chai2010 使用绝对路径 包...
    chai2010阅读 11,327评论 0 4