bazel構建C++工程


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識別WORKSPACEBUILD這兩個文件。

  • 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來說,工作空間現在包含兩個包,libmain
先看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

到此具有三個目標的兩個包的項目構建完成,並了解了它們之間的依賴關系。

4.Reference

  1. Google開源構建工具Bazel
  2. Introduction to Bazel: Build a C++ Project


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM