-
簡介
bazel
是Google開源的一套類似於Make的編譯構建工具。- 運作原理
- 運行構建或測試時,Bazel執行以下操作
- 加載
BUILD
與目標相關的文件。 - 分析輸入及其依賴關係,應用指定的構建規則。並生產
action
圖 - 對輸入執行構建操作,直到生成最終構建輸出。
- 加載
- action圖表示各個構建輸入和他們之間的關係,以及Bazel將執行的構建操作。
- 運行構建或測試時,Bazel執行以下操作
- 運作原理
-
優點
- 構建快。支持增量編譯, 對依賴關係進行了優化,從而支持併發執行。
- 可構建多種語言。bazel可用來構建Java C++ Android iOS等很多語言和框架,並支持mac windows linux等不同平臺。
- 可伸縮。可處理任意大小的代碼庫,可處理多個庫,也可以處理單個庫
- 可擴展。使用bazel擴展語言可支持新語言和新平臺。
-
安裝方法
入門用法
項目結構
根目錄下爲工作區workspace
,workspace下包含多個package
,每個package又包含多個編譯目標target
。
wrokspace
- Bazel的編譯是基於工作區(workspace)的概念。工作區是一個存放了所有源代碼和Bazel編譯輸出文件的目錄,也就是整個項目的根目錄。同時它也包含一些Bazel認識的文件:
WORKSPACE
文件,用於指定當前文件夾就是一個Bazel的工作區。所以**WORKSPACE
文件總是存在於項目的根目錄下**。- 一個或多個
BUILD
文件,用於告訴Bazel怎麼構建項目的不同部分。(如果工作區中的一個目錄包含BUILD文件,那麼它就是一個package。)
- 要指定一個目錄爲Bazel的工作區,就只要在該目錄下創建一個空的
WORKSPACE
文件即可。 - 當Bazel編譯項目時,所有的輸入和依賴項都必須在同一個工作區。屬於不同工作區的文件,除非linked否則彼此獨立。
WORKSPACE
採用類似Python的語法
package
- 如果工作區中的一個目錄包含BUILD文件,那麼它就是一個
package
。
target
- 一個
BUILD
文件包含了幾種不同類型的指令。其中最重要的是編譯指令,它告訴Bazel如何編譯想要的輸出,比如可執行二進制文件或庫。 BUILD
文件中的每一條編譯指令被稱爲一個target
,它指向一系列的源文件和依賴,一個target也可以指向別的target。
編譯示例
示例使用官方的c++編譯示例
-
編譯示例目錄結構
examples
└── cpp-tutorial
├──stage1
│ ├── main
│ │ ├── BUILD
│ │ └── hello-world.cc
│ └── WORKSPACE
│
├──stage2
│ ├── main
│ │ ├── BUILD
│ │ ├── hello-world.cc
│ │ ├── hello-greet.cc
│ │ └── hello-greet.h
│ └── WORKSPACE
│
└──stage3
├── main
│ ├── BUILD
│ ├── hello-world.cc
│ ├── hello-greet.cc
│ └── hello-greet.h
├── lib
│ ├── BUILD
│ ├── hello-time.cc
│ └── hello-time.h
└── WORKSPACE
如何編譯
1 2 3 4 |
# 在WORKSPACE文件所在根目錄下執行 # //main:是BUILD文件相對於WORKSPACE文件的位置 # hello-world則是我們在BUILD文件中命名好的target的名字 bazel build //main:hello-world |
Stage1 如何構建單個package中的單個target
1 2 3 4 5 |
# BUILD 文件內容 cc_binary( name = "hello-world", srcs = ["hello-world.cc"], ) |
Stage2 如何構建單個package的多個target
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 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", ], ) |
Stage3 如何構建多個package的多個target
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# lib包 ## 默認情況, targets只對同一BUILD文件裏的其他targets可見 ## 若要對其他包可見, 需聲明爲顯示可見 ## 防止像共有API中庫的實現細節泄露等情況 cc_library( name = "hello-time", srcs = ["hello-time.cc"], hdrs = ["hello-time.h"], visibility = ["//main:__pkg__"], ) # main包 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", ], ) |
如何查看依賴圖
-
生成依賴圖
1 2 3 4
# 查找target爲 //main:hello-world 的所有依賴 # --nohost_deps 表示不包括host依賴 # --noimplicit_deps 表示不包括隱式依賴 e.g: @bazel_tools//tools/cpp:stl bazel query --nohost_deps --noimplicit_deps 'deps(//main:hello-world)' --output graph
-
將生成的輸出圖文字描述, 粘貼到 GraphViz, 生成的依賴圖如下
進階用法
-
bazel 可選命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
用法:bazel <command> <options> ... 可用命令: build 構建指定的目標。 clean 刪除輸出文件,並可選擇停止服務器。 run 運行指定的目標。 test 構建並運行指定的測試目標。 help 打印命令或索引的幫助。 info 顯示有關bazel服務器的運行時信息。 version 打印Bazel的版本信息。 analyze-profile 分析構建配置文件數據。 aquery 對分析後的操作圖執行查詢。 cquery 執行分析後依賴圖查詢。 query 執行依賴關係圖查詢。 canonicalize-flags Canonicalize Bazel flags。 dump 轉儲Bazel服務器進程的內部狀態。 fetch 獲取目標的所有外部依賴項。 mobile-install 在移動設備上安裝應用程序。 shutdown 停止Bazel服務器。
構建
- 用法
1 |
bazel build <options> <targets> |
指定包路徑
--package_path
指定搜索BUILD文件的目錄集合
1 2 3 4 5 6 7 |
mkdir -p build && cd build touch WORKSPACE ## 絕對路徑 bazel build --package_path /some/other/path //main:hello-world ## 相對路徑 bazel build --package_path %workspace%/relative/to/other/path //main:hello-world |
編譯警告檢查
--output_filter regex
顯示那些符合正則表達式的構建和編譯的警告,如果目標不匹配則標準輸出和標準錯誤將會被丟棄。用於幫助找到某些特定的警告。
1 2 3 4 5 6 7 8 9 10 11 |
## 顯示所有警告 bazel build --output_filter= --package_path /some/other/path ## 只顯示指定的某幾個包(project)的警告 --output_filter='^//(first/project|second/project):' ## 不顯示指定的某幾個包的警告 --output_filter='^//((?!(first/bad_project|second/bad_project):).)*$' ## 所有警告均不顯示 --output_filter=DONT_MATCH_ANYTHING |
編譯器的編譯選項
編譯C的Flag
--copt gcc-option
指定給 gcc 傳遞什麼參數, 等價於 cmake 的 CMAKE_C_FLAGS
1 2 |
## 參數項一次只能指定一個 bazel build --copt="-O3" --copt="-fpic" --copt="-std=c++11" //main:hello-world |
只適用編譯C的Flag
--conlyopt gcc-option
與 —copt 相似, 不同的是只能指定 c 的編譯選項
1 2 |
## 參數項一次只能指定一個 bazel build --conlyopt="-Wno-pointer-sign" //main:hello-world |
gcc中 char 和 unsigned char 有時候傳遞參數類型不匹配會有報警,增加該編譯選項可以關閉該報警。
編譯C++的Flag
--cxxopt gcc-option
指定編 c++ 的參數項, 等價於 cmake 的 CMAKE_CXX_FLAGS
1 2 |
## 參數項一次只能指定一個 bazel build --cxxopt="-fno-implicit-templates" //main:hello-world |
用**-fno-implicit-templates**編譯代碼,會令隱式的模板實例化失效,他會顯示的初始化所需模板。雖然這種方法需要精確瞭解正在使用的是哪種模板實例,但這種方法確實令源代碼更加清楚。
編譯器鏈接Flag
--linkopt linker-option
指定給 gcc 的鏈接庫選項
1 |
bazel build --linkopt="-pthread" //main:hello-world |
調試與符號信息的去除
--strip (always|never|sometimes)
指定是否從二進制文件和共享庫文件中去除調試信息
1 2 |
# 默認是 sometimes, 當語義項 --compilation_mode=fastbuild 時 bazel build --strip=always //main:hello-world |
注:
-
即使使用了
--strip=never
也不會保留 debug 信息, 如果想要得到完整符號庫, 需指定使用-c dbg
或者--copt -g
方可1
bazel build -c dbg --strip=never //main:hello-world
-
--strip
項默認是 –strip-debug, 如果不只是想去除 debug 信息, 而是去除所有符號信息, 則需指定鏈接項--strip-all
1
bazel build --strip=always --linkopt=-Wl,--strip-all //main:hello-world
完整的編譯選項文檔: Commands and Options
語義選項
影響構建的命令和輸出的內容。
指定編譯模式
--compilation_mode (fastbuild|opt|dbg) (-c)
fastbuild
快速編譯, 會產生最小的調試信息(-gmlt -Wl,-S), 同時將會設置 -DNDEBUG, 默認該模式opt
最優化, 不會產生任何調試信息, 除非手動指定了 –copt -g, 同時設置 -O2 -DNDEBUGdbg
調試, 產生調試信息(-g)
1 2 3 |
bazel build --compilation_mode "dbg" //main:hello-world ## 或者使用縮寫 bazel build -c "dbg" //main:hello-world |
動態鏈接模式
--dynamic_mode (auto|default|fully|off)
auto
根據平臺選擇: linux 上是 default, cygwin 上是 offdefault
由 bazel 去選擇是否進行動態鏈接fully
動態鏈接所有目標, 將會加速鏈接時間和減小輸出庫大小off
靜態鏈接所有目標, 當 linkopts 指定設置 -static, 將自動轉成該模式
輸出構建過程的執行語句
--explain logfile
寫到一個日誌文件當中, 配合 —-verbose_explanations
, 效果等同 cmake 開啓 CMAKE_VERBOSE_MAKEFILE
1 |
bazel build --explain "log.txt" -—verbose_explanations //main:hello-world |
輸出構建過程的性能
--profile file
會把構建的分析數據寫到一個二進制文件當中, 可使用 bazel analyze-profile
命令進行解析
1 2 3 4 5 |
## 生成 profile bazel build --profile "profile.bin" //main:hello-world ## 分析構建性能 bazel analyze-profile profile.bin |
**更多構建命令選項可參考: ** bazel help build
查看構建的信息內容
-
命令用法
1
bazel info <options> [key]
-
--show_make_env
顯示構建環境, 默認 false
**更多信息查詢命令選項可參考: ** bazel help info
分析
- 命令用法
1 |
bazel query <options> <query-expression> |
是否分析host依賴相關
--host_deps
指定分析其依賴關係, 默認開啓
--nohost_deps
指定不分析其依賴關係
是否分析其隱式依賴關係
--implicit_deps
指定分析其隱式依賴關係, 例如 @bazel_tools//tools/cpp:malloc, 默認開啓
--noimplicit_deps
指定不分析其隱式依賴關係
指定輸出格式
--output (build|graph|label|label_kind|location|maxrank|minrank|package|proto|xml)
build
輸出指定分析目標的 BUILD 文件內容
1 2 3 4 5 |
# /Users/wangzhuxing/Desktop/Project/repository/wzx3/bazel-examples/cpp-tutorial/stage1/main/BUILD:1:1 cc_binary( name = "hello-world", srcs = ["//main:hello-world.cc"], ) |
-
graph
輸出可進行圖可視化的格式文本1 2 3 4 5 6
digraph mygraph { node [shape=box]; "//main:hello-world" "//main:hello-world" -> "//main:hello-world.cc" "//main:hello-world.cc" }
-
label
輸出指定分析目標的依賴文件, 默認配置1 2
//main:hello-world //main:hello-world.cc
-
label_kind
分類輸出指定分析目標的依賴1 2
cc_binary rule //main:hello-world source file //main:hello-world.cc
-
location
輸出指定分析目標的代碼行位置1 2
/Users/.../main/BUILD:1:1: cc_binary rule //main:hello-world /Users/.../main/BUILD:3:12: source file //main:hello-world.cc
-
maxrank|minrank
按等級排列指定分析目標的依賴1 2
0 //main:hello-world 1 //main:hello-world.cc
-
package
輸出指定分析目標的包名1
main
實用流程
- 生成可視化圖結構的信息文本
1 2 3 4 |
bazel query --nohost_deps \ --noimplicit_deps \ 'deps(//main:hello-world)' \ --output graph > output-graph.gv |
- 通過 python 的
graphviz
模塊 進行圖解析
1 |
python -m bazel_graphviz --input_file="output-graph.gv" |
附上本人的解析代碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# encoding: utf-8 import argparse, os from graphviz import Source def _render(args): if args.input_file: output_file_name, output_file_extension = os.path.splitext(str(args.input_file)) src = Source.from_file(str(args.input_file)) src.format = 'png' src.render(filename=output_file_name, view=True, cleanup=True) return True return False def _get_parser(): parser = argparse.ArgumentParser() parser.add_argument( '--input_file', default=None, help='Path to the model weights file of the external tool (e.g caffe/onnx weights proto binary)') return parser def _main(): parser = _get_parser() args, unknown_args = parser.parse_known_args() if not _render(args): print('Render bazel graph failed!') if __name__ == '__main__': _main() |
**更多構建命令選項可參考: ** bazel help query
清理
只清理編譯目錄
刪除編譯項目的編譯過程中產生的目錄樹,不會刪除bazel產生的臨時文件
1 |
bazel clean |
完全清理
刪除輸出文件的目錄樹,刪除Bazel產生的所有臨時文件,刪除下載的臨時文件等
1 |
bazel clean --expunge |
測試
- 命令用法
1 |
bazel test <options> <test-targets> |
注:
- 命令運行前會先執行一次 bazel build
- 測試命令無法直接傳入測試程序的形參, 也並不需要
**更多測試命令選項可參考: ** bazel help test
運行
- 命令用法
1 |
bazel run <options> -- <binary target> <flags to binary> |
**更多運行命令選項可參考: ** bazel help run
高階用法
BUILD 文件語法
概念和術語
通用定義
-
Bourne shell 標記化
根據
Bourne shell
的標記化規則,某些規則的某些字符串屬性被拆分爲多個單詞:- 未加引號的空格分隔單獨的單詞
- 單引號和雙引號字符和反斜槓用於防止標記化
受此標記化影響的屬性在本文檔的定義中明確指出。
受**“Make”變量擴展和Bourne shell標記化的屬性通常用於將任意選項傳遞給編譯器和其他工具**。此類屬性的示例是
cc_library.copts
和java_library.javacopts
。這些替換一起允許單個字符串變量擴展爲特定於配置的選項字列表。 -
標籤表達式
-
編譯(build)規則的通用屬性
-
測試(test)規則的通用屬性 (*_test)
-
生成binary規則的通用屬性 (*_binary)
-
可配置的屬性
-
隱式輸出目標
"Make"變量
函數接口