這是關於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