这是关于Bazel的第二篇blog,前一篇写了安装、配置相关的东西,这一篇则是4个逐步推动的例子,改编自官方demo;以及相应的概念、文档连接等。html
[TOC]python
前提
- Linux(Ubuntu, etc)或Mac OSX系统,会点儿命令行(包括brew/apt)
- 装好了zsh和oh-my-zsh(用于
bazel build
等命令的补全) - 装好了bazel;
- 学过C/C++;
- 用过make/cmake
- 最好会一点git
- bazel版本:目前我用0.21版本,最新版删过东西(https://docs.bazel.build/versions/0.21.0/be/workspace.html)
基本概念
WORKSPACE
: 空文件;标识了项目根目录;只有一个BUILD
:WORKSPACE下的子目录里,若是放了一个名为BUILD
的文件,则这个目录是一个package
;BUILD
里写一些构建规则 (rules)cc_binary
: C/C++package
的最经常使用的构建规则- bazel的C/C++在线文档:https://docs.bazel.build/versions/master/be/c-cpp.html
target
的概念:cmake中的target包括executable、library两种状况rule
的概念:相似于cmake中target
概念的推广,bazel构建C/C++时的规则有:- 即便是bazel build官方文档,也不明确区分target和rule字眼,能够认为是一个意思
- 大致上,
.bzl
至关于.cmake
文件,BUILD
至关于CMakeLists.txt
WORKSPACE
,BUILD
中用到了一些预约义的函数或变量,具体看这里:https://docs.bazel.build/versions/master/skylark/lib/skylark-overview.html
速查连接汇总
workspace规则 starlark预设全局变量 完整代码ios
stage1: 一个package, 一个target
这是最简单的bazel构建例子git
目录结构github
├── WORKSPACE └── main ├── BUILD └── hello.c
其中,main
为包名,由于它包含了BUILD
文件bash
hello.c
:网络
#include <stdio.h> int main(void){ printf("hello from C!\n"); return 0; }
BUILD
:框架
cc_binary( name = "hello", srcs = ["hello.c"], )
执行构建dom
bazel build main:all
- 语法是
bazel build 包名:任务名
- 输入完
bazel build
后按tab
键补全提示,比较方便 - 由于目前只有一个target,也能够输入
bazel build main:hello
运行函数
bazel run main:all
它其实除了输出bazel相关的信息,执行的是./bazel-bin/hello
目录下的可执行文件hello
等
执行清除
bazel clean
stage2: 一个package,多个target
典型场景:写一个库,而后调用它。这里写一个神经网络激活函数库,而后写一个测试程序。
目录结构
├── WORKSPACE └── main ├── BUILD ├── activations.c ├── activations.h └── testbed.c 1 directory, 5 files
BUILD
:
cc_library( name = "actv", srcs = ["activations.c"], hdrs = ["activations.h"], ) cc_binary( name = "actv-testbed", srcs = ["testbed.c"], deps = [ ":actv", ], )
activations.h
:
#ifndef __ACTIVATIONS_H__ #define __ACTIVATIONS_H__ float relu(float x); float sigmoid(float x); #endif
activations.c
:
#include "activations.h" #include <math.h> float relu(float x){ if (x>=0) return x; return 0; } float sigmoid(float x){ return 1.0f / (1.0f + exp(-x)); }
testbed.c
:
#include <stdio.h> #include <time.h> #include <stdlib.h> #include "activations.h" // return a random float in (s, t) float get_random(float s, float t){ float v = (float)(rand()) / RAND_MAX; v = v * (t-s) + s; return v; } int main(){ const int maxn = 5; srand(time(0)); for(int i=0; i<maxn; i++) { float x = get_random(-2.0f, 2.0f); float res_relu = relu(x); float res_sigmoid = sigmoid(x); printf("x=%f\n", x); printf("relu(x)=%f\n", res_relu); printf("sigmoid(x)=%f\n", res_sigmoid); printf("\n"); } return 0; }
构建库
bazel build main:actv
构建测试
bazel build main:actv-testbed
执行
bazel run main:actv-testbed
stage3: 多package,多target
主要是弄清楚,如何在不一样package的target之间设定依赖关系,好比一个库target在其余包中是否可用(visibility),好比头文件的包含路径。
目录结构:
├── lib │ ├── BUILD │ ├── random.c │ ├── random.h │ ├── timer.c │ └── timer.h ├── main │ ├── activations.c │ ├── activations.h │ ├── BUILD │ └── testbed.c └── WORKSPACE
其中,lib/BUILD
:
cc_library( name = "timer", srcs = ["timer.c"], hdrs = ["timer.h"], visibility = ["//main:__pkg__"] ) cc_library( name = "random", srcs = ["random.c"], hdrs = ["random.h"], visibility = ["//main:__pkg__"] )
而main/BUILD
:
cc_library( name = "actv", srcs = ["activations.c"], hdrs = ["activations.h"], ) cc_binary( name = "actv-testbed", srcs = ["testbed.c"], deps = [ ":actv", "//lib:random", "//lib:timer" ], )
各个源码文件其实都很简单,这里就不贴出来的,只须要注意在包含lib包里面的头文件时,main/testbed.c
是这样写的:
#include "activations.h" #include "lib/timer.h" #include "lib/random.h"
stage4: 使用外部依赖
这里说的外部依赖,包括:本地的其余目录,git仓库或http地址下载的。外部依赖能够是基于bazel构建的,也能够不是。
anyway,google家开源的abseil框架的hello-world例子多是最典型的demo了。可是它同时用了gtest、gmock和abseil,感受太麻烦了,干脆直接引入abseil外部库来写hello-world,以下:
目录结构
├── BUILD ├── hello.cc └── WORKSPACE
这里看到并无子目录,这也是能够的
WORKSPACE
# 非必须 workspace(name = "com_google_absl_hello_world") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # Abseil http_archive( name = "absl", strip_prefix = "abseil-cpp-master", urls = ["https://github.com/abseil/abseil-cpp/archive/master.zip"], )
BUILD
cc_binary( name = "hello", srcs = ["hello.cc"], deps = [ "@absl//absl/strings" ] )
说明:由于在WORKSPACE
中载入了absl,因此BUILD
中可使用@absl
。
hello.cc
#include <iostream> #include <string> #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" using std::string; using std::cout; using std::endl; string Greet(absl::string_view person) { return absl::StrCat("Hello ", person); } int main(){ cout << Greet("world") << endl; cout << Greet("ChrisZZ") << endl; return 0; }
执行构建
bazel build :hello
它根据WORKSPACE
中的设定(也就是http_archive
),从http下载。其余的方法还包括git_repository
的方式,不过最新版bazel中把这些都踢掉了,必须手动load一下bazel工具中的.bzl文件,才能用它们。。
参考: https://stackoverflow.com/questions/45814669/c-project-with-bazel-and-gtest?noredirect=1&lq=1
https://github.com/vincent-picaud/Bazel_with_GTest
https://docs.bazel.build/versions/master/external.html
最佳实践
https://docs.bazel.build/versions/master/external.html#best-practices