Bazel簡介:構建C ++項目

Bazel簡介:構建C ++項目

在本教程中,您將學習使用Bazel構建C ++應用程序的基礎知識。您將設置工作區并構建一個簡單的C ++項目,該項目說明了Bazel的關鍵概念,例如目標和BUILD文件。完成本教程后,請查看 Common C ++ Build Use Cases,以獲取有關更高級概念(如編寫和運行C ++測試)的信息。

預計完成時間:30分鐘。

您將學到什么

在本教程中,您將學習如何:

  • 建立目標
  • 可視化項目的依賴關系
  • 將項目分為多個目標和程序包
  • 控制整個程序包的目標可見性
  • 通過標簽參考目標

內容

在你開始之前

要開始本教程,請先安裝Bazel(如果尚未安裝)。然后,從Bazel的GitHub存儲庫中檢索示例項目:

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

如您所見,共有三組文件,每組文件代表本教程中的一個階段。在第一階段,您將構建一個駐留在單個程序包中的單個目標。在第二階段,您將把您的項目分成多個目標,但將其保存在一個程序包中。在第三階段(也是最后一個階段)中,您將把您的項目分成多個包,并使用多個目標進行構建。

用Bazel構建

設置工作區

在構建項目之前,您需要設置其工作區。工作區是一個目錄,其中包含項目的源文件和Bazel的構建輸出。它還包含Bazel認為特殊的文件:

  • WORKSPACE文件將目錄及其內容標識為Bazel工作區,并位于項目目錄結構的根目錄中,

  • 一個或多個BUILD文件,這些文件告訴Bazel如何構建項目的不同部分。(工作空間中包含BUILD文件的目錄是一個。您將在本教程的后面部分中學習有關包的信息。)

要將目錄指定為Bazel工作區,請WORKSPACE在該目錄中創建一個空文件 。

Bazel構建項目時,所有輸入和依賴項必須位于同一工作空間中。除非鏈接,否則位于不同工作空間中的文件彼此獨立,這超出了本教程的范圍。

了解BUILD文件

一個BUILD文件包含幾種不同類型的用于Bazel指令。最重要的類型是構建規則(build rule),該規則(rule)告訴Bazel如何構建所需的輸出,例如可執行二進制文件或庫。BUILD文件中構建規則的每個實例都稱為目標(target)并指向一組特定的源文件和依賴項。一個目標也可以指向其他目標。

看一下目錄BUILD中的cpp-tutorial/stage1/main文件:

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
)

在我們的示例中,hello-world目標實例化了Bazel內置的 cc_binaryrule。該規則告訴Bazel從hello-world.cc源文件構建一個自包含的可執行二進制文件,而沒有任何依賴關系。

目標中的屬性明確聲明其依賴項和選項。雖然該name屬性是強制性的,但許多是可選的。例如,在 hello-world目標中name是不言自明的, 其srcs屬性指定Bazel從中構建目標的源文件。

建立項目

讓我們構建您的示例項目。切換到cpp-tutorial/stage1目錄并運行以下命令:

bazel build //main:hello-world

注意目標標簽-該//main:部分是BUILD 文件相對于工作空間根目錄的位置,hello-world也是我們在BUILD文件中命名該目標的名稱。(您將在本教程的最后詳細了解目標標簽。)

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目標!Bazel將構建輸出放置bazel-bin在工作空間根目錄中的目錄中。瀏覽其內容以了解Bazel的輸出結構。

現在測試您新構建的二進制文件:

bazel-bin/main/hello-world

查看依賴關系圖

成功的構建具有在BUILD 文件中明確聲明的所有依賴項。Bazel使用這些語句來創建項目的依賴關系圖,從而實現準確的增量構建。

讓我們可視化示例項目的依賴關系。首先,生成依賴關系圖的文本表示(在工作區根目錄運行命令):

bazel query --notool_deps --noimplicit_deps 'deps(//main:hello-world)' \
  --output graph

上面的命令告訴Bazel查找目標的所有依賴關系 //main:hello-world(不包括主機和隱式依賴關系),并將輸出格式化為圖形。

然后,將文本粘貼到GraphViz中

在Ubuntu上,可以通過安裝GraphViz和xdot Dot Viewer在本地查看圖形:

sudo apt update && sudo apt install graphviz xdot

然后,您可以通過將上面的文本輸出直接傳遞到xdot來生成和查看圖形:

xdot <(bazel query --notool_deps --noimplicit_deps 'deps(//main:hello-world)' \
  --output graph)

如您所見,示例項目的第一階段有一個目標,該目標構建一個沒有附加依賴項的源文件:

“ hello-world”的依賴圖

既然您已經設置了工作區,構建了項目并檢查了其依賴性,那么讓我們增加一些復雜性。

優化您的Bazel構建

盡管單個目標足以滿足小型項目的需要,但您可能希望將較大的項目拆分為多個目標和程序包,以實現快速增量構建(即,僅重建更改的內容)并通過同時構建項目的多個部分來加快構建速度。

指定多個構建目標

讓我們將示例項目構建分為兩個目標??匆幌?目錄BUILD中的cpp-tutorial/stage2/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",
    ],
)

BUILDBazel 使用此文件首先構建hello-greet庫(使用Bazel的內置cc_library規則),然后構建hello-world二進制文件。目標中的deps屬性hello-world告訴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”的依賴圖

您現在已經用兩個目標構建了該項目。的hello-world目標建立一個源文件,并依賴于目標(//main:hello-greet),它建立兩個附加的源文件。

使用多個包

現在讓我們將項目分成多個包。看一下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 = ["http://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",
        "http://lib:hello-time",
    ],
)

如上所示,hello-world在目標main包依賴于 hello-time目標lib包(因此目標標簽 //lib:hello-time) -Bazel通過知道這個deps屬性??匆幌乱蕾噲D:

“ hello-world”的依賴圖

注意,為使構建成功,我們使用 屬性使//lib:hello-time目標對于目標 lib/BUILD明確可見。這是因為默認情況下,目標僅對同一文件中的其他目標可見。(Bazel使用目標可見性來防止諸如包含實現詳細信息的庫之類的問題泄漏到公共API中。)main/BUILD``visibility``BUILD

讓我們構建項目的最終版本。切換到 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

現在,您已經將項目構建為帶有三個目標的兩個程序包,并了解了它們之間的依賴性。

使用標簽參考目標

BUILD文件中和命令行中,Bazel使用標簽來引用目標(例如//main:hello-world或)//lib:hello-time。它們的語法是:

//path/to/package:target-name

如果目標是規則目標,則path/to/package是包含BUILD文件的目錄的路徑,并且target-name是您在BUILD文件中命名目標(name屬性)的名稱。如果目標是文件目標,則path/to/package是包根目錄的路徑,并且 target-name是目標文件的名稱,包括其完整路徑。

在存儲庫根目錄引用目標時,包路徑為空,只需使用即可//:target-name。在同一BUILD 文件中引用目標時,您甚至可以跳過//工作空間的根標識符,而只需使用 :target-name。

進一步閱讀

恭喜你!您現在知道了使用Bazel構建C ++項目的基礎知識。接下來,閱讀最常見的C ++構建用例。然后,檢查以下內容:

構建愉快!

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容