1.bazel介紹
Bazel是一個開源的構建和測試工具,類似於Make、Maven和Gradle。Bazel支持多種語言的項目,並為多種平台構建輸出。Bazel支持跨多個存儲庫和大量用戶的大型代碼庫。
2.bazel安裝
bazel安裝有兩種方法,一種是通過源安裝,另一種是通過下載安裝包本地安裝。官網安裝教程。本人選取的是第一種安裝方式。
第一步:添加Bazel分發URI作為包源
sudo apt install curl gnupg
curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
第二步:安裝或更新bazel
sudo apt update && sudo apt install bazel
如果已經安裝了bazel,需要升級到最新版本的bazel
sudo apt update && sudo apt full-upgrade
安裝特定版本的bazel,例如需要安裝bazel-1.0.0
sudo apt install bazel-1.0.0
3.bazel構建工程
bazel里面有構建C++工程的例子,可以通過命令git clone https://github.com/bazelbuild/examples
下載,本教程的示例項目位於examples/cpp-tutorial目錄中,其結構如下:
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
下面對這個C++工程進行解讀。
3.1 設置工作區
在構建項目將之前,需要設置項目工作區,這個工作區就是一個包含項目源文件的目錄和bazel構建輸出,bazel識別WORKSPACE
和BUILD
這兩個文件。
- WORKSPACE文件將目錄及其內容標識為Bazel工作區,位於項目目錄結構的根目錄中
- BUILD文件會有一個或者多個,分別構建項目的不同部分。
3.2 理解BUILD文件
BUILD
文件包含Bazel的幾種不同類型的指令。最重要的類型是構建規則,它告訴Bazel如何構建所需的輸出,比如可執行的二進制文件或庫。構建文件中構建規則的每個實例稱為目標,並指向一組特定的源文件和依賴項。一個目標也可以指向其他目標。
看一下cpp-tutorial/stage1/main
中BUILD文件:
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
)
在我們的示例中,hello-world目標實例化了Bazel的內置cc_binary規則。規則告訴bazel建立一個獨立的可執行二進制hello world.cc源文件沒有依賴性。
3.3 使用bazel編譯項目
構建示例項目。切換到cpp-tutorial/stage1目錄,運行以下命令:
bazel build //main:hello-world
注意目標標簽——//main:部分是構建文件相對於工作區根的位置,hello-world是我們在構建文件中命名的目標。
Bazel輸出結果如下:
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 2.267s, Critical Path: 0.25s
現在測試新構建的二進制文件:
bazel-bin/main/hello-world
3.4 查看依賴圖
成功的構建將在構建文件中顯式地聲明其所有依賴項。Bazel使用這些語句創建項目的依賴關系圖,從而實現精確的增量構建。
將示例項目的依賴關系可視化。首先,生成依賴關系圖的文本表示(在工作區根運行此命令):
bazel query --notool_deps --noimplicit_deps "deps(//main:hello-world)" \
--output graph
上面的命令告訴Bazel尋找target //main:hello-world的所有依賴項(不包括主機和隱式依賴項),並將輸出格式化為圖形。
然后,將文本粘貼到GraphViz中。
然后你可以通過管道直接輸出到xdot來生成和查看圖形:
sudo apt update && sudo apt install graphviz xdot
然后你可以通過管道直接輸出到xdot來生成和查看圖形:
xdot <(bazel query --notool_deps --noimplicit_deps "deps(//main:hello-world)" \
--output graph)
如你所見,這個示例項目的第一階段有一個單一的目標,它構建一個沒有附加依賴關系的單一源文件:
接下來,增加點難度
3.5 多個目標文件(target)編譯
將示例項目構建分成兩個目標。看一下cpp-tutorial/stage2/主目錄中的構建文件:
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",
],
)
對於這個構建文件,Bazel首先構建hello-greet庫(使用Bazel內置的cc_library規則),然后構建hello-world二進制文件。hello-world目標中的deps屬性告訴Bazel需要hello-greet庫來構建hello-world二進制文件。
切換到cpp-tutorial/stage2目錄,運行以下命令:
bazel build //main:hello-world
Bazel的輸出如下:
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 2.399s, Critical Path: 0.30s
現在測試新構建的二進制文件:
bazel-bin/main/hello-world
如果修改hello-greet.cc和重建項目,Bazel將只重新編譯該文件。
看看依賴關系圖,你可以看到hello-world和以前一樣依賴於相同的輸入,但是構建的結構是不同的:
現在已經構建了帶有兩個目標的項目。hello-world目標構建一個源文件,並依賴於另一個目標(//main:hello-greet)。
3.6 多個項目包(packages)編譯
現在將項目分離成多個包,看一下cpp-tutorial/stage3目錄的內容:
└──stage3
├── main
│ ├── BUILD
│ ├── hello-world.cc
│ ├── hello-greet.cc
│ └── hello-greet.h
├── lib
│ ├── BUILD
│ ├── hello-time.cc
│ └── hello-time.h
└── WORKSPACE
注意,現在有兩個子目錄,每個子目錄都包含一個BUILD
文件。因此,對於Bazel來說,工作空間現在包含兩個包,lib
和main
。
先看lib/BUILD
文件
cc_library(
name = "hello-time",
srcs = ["hello-time.cc"],
hdrs = ["hello-time.h"],
visibility = ["//main:__pkg__"],
)
還有main/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-world目標依賴於lib包中的hello-time目標(因此是目標標簽//lib:hello-time)—Bazel通過deps屬性知道這一點。看看依賴關系圖:
請注意,為了使構建成功,我們使用visibility屬性使lib/ build中的//lib:hello-time目標對main/ build中的目標顯式可見。這是因為在默認情況下,目標只對同一構建文件中的其他目標可見。(Bazel使用目標可見性來防止諸如庫中包含的實現細節泄漏到公共api等問題。)
切換到cpp-tutorial/stage3目錄,運行以下命令:
bazel build //main:hello-world
Bazel的輸出如下:
INFO: Found 1 target...
Target //main:hello-world up-to-date:
bazel-bin/main/hello-world
INFO: Elapsed time: 0.167s, Critical Path: 0.00s
現在測試新構建的二進制文件:
bazel-bin/main/hello-world
到此具有三個目標的兩個包的項目構建完成,並了解了它們之間的依賴關系。