使用 Bazel 建構程式

回報問題 查看原始碼 夜間 7.2 7.1 7.0 6.5 6.4

本頁說明如何使用 Bazel 建構程式、建構指令語法,以及 目標模式語法

快速入門導覽課程

如要執行 Bazel,請前往基礎 workspace 目錄 或其子目錄,並輸入 bazel。如果您符合下列情況,請參閱「建構」一節 就得建立新的工作區

bazel help
                             [Bazel release bazel version]
Usage: bazel command options ...

可使用的指令

  • analyze-profile:分析建構設定檔資料。
  • aquery:在「後分析」動作圖表上執行查詢。
  • build:建構指定的目標。
  • canonicalize-flags:對 Bazel 標記進行標準化。
  • clean:移除輸出檔案,並視需要停止伺服器。
  • cquery:執行後分析依附元件圖表查詢。
  • dump:傾印 Bazel 伺服器程序的內部狀態。
  • help:顯示指令或索引的說明。
  • info:顯示 bazel 伺服器的執行階段資訊。
  • fetch:擷取目標的所有外部依附元件。
  • mobile-install:在行動裝置上安裝應用程式。
  • query:執行依附關係圖查詢。
  • run:執行指定的目標。
  • shutdown:停止 Bazel 伺服器。
  • test:建構並執行指定的測試目標。
  • version:列印 Bazel 的版本資訊。

取得說明

  • bazel help command:列印 command
  • bazel helpstartup_options:JVM 託管 Bazel 的選項。
  • bazel helptarget-syntax:說明指定目標的語法。
  • bazel help info-keys:顯示資訊指令使用的索引鍵清單。

bazel 工具會執行許多函式,稱為指令。最常見的 分別是 bazel buildbazel test您可以瀏覽線上說明 使用 bazel help 傳送訊息。

建立目標

您需要先具備工作區,才能開始建構。工作區 目錄樹狀結構,內含建構 應用程式。Bazel 可讓您從完全唯讀 磁碟區

如要使用 Bazel 建構程式,請輸入 bazel build,並在後面加上 目標

bazel build //foo

發出指令建構 //foo 後,您會看到類似以下的輸出內容:

INFO: Analyzed target //foo:foo (14 packages loaded, 48 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 9.905s, Critical Path: 3.25s
INFO: Build completed successfully, 6 total actions

首先,Bazel 會「載入」目標依附元件圖表中的所有套件。這個 包含宣告的依附元件,即直接列於目標的 BUILD 中的檔案 檔案以及「遞移依附元件」,該檔案列於BUILD 指定的依附元件找出所有依附元件後,Bazel 會分析 並設定正確的建構動作。最後,Bazel 會執行 建構作業的編譯器和其他工具

在建構執行階段,Bazel 會顯示進度訊息。進度 訊息包括目前的建構步驟 (例如編譯器或連結器) ,以及建構動作總數中完成的次數。身為 隨著 Bazel 發現 整個動作圖表,但這個數字會在幾秒內穩定下來。

在建構作業結束時,Bazel 會輸出要求的目標 未成功建構。如果是,輸出檔案則可 找到。執行建構作業的指令碼可確實剖析這項輸出內容。看 --show_result

如果您再次輸入相同的指令,建構作業的執行速度會更快。

bazel build //foo
INFO: Analyzed target //foo:foo (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 0.144s, Critical Path: 0.00s
INFO: Build completed successfully, 1 total action

此為空值建構。由於沒有變更,因此沒有可重新載入的套件 不必執行任何建構步驟如果「foo」沒有任何變更或其 Bazel 會重新執行部分建構動作 漸進式建構

建立多個目標

Bazel 允許透過多種方式指定要建構的目標。我們將 這些稱為目標模式這個語法可用於以下命令: buildtestquery

標籤則用於指定個別 目標,例如用於在 BUILD 檔案中宣告依附元件,Bazel 的目標 這種模式會指定多個目標目標模式是指 目標的標籤語法 (使用萬用字元)。最簡單的情況下 有效的標籤也是有效的目標模式,能夠識別一組剛好 目標。

系統會依據目前屬性解析所有以 // 為開頭的目標模式 工作區

//foo/bar:wiz 只有單一目標 //foo/bar:wiz
//foo/bar 等同於 //foo/bar:bar
//foo/bar:all 套件 foo/bar 中的所有規則目標。
//foo/... foo 目錄下所有套件中的所有規則目標。
//foo/...:all foo 目錄下所有套件中的所有規則目標。
//foo/...:* foo 目錄下所有套件中的所有目標 (規則和檔案)。
//foo/...:all-targets foo 目錄下所有套件中的所有目標 (規則和檔案)。
//... 工作區套件中的所有目標。不包含目標 來自外部存放區
//:all 如果頂層套件中有 `BUILD` 檔案,則頂層套件中的所有目標 工作區的根層級。

開頭不是 // 的目標模式會相對於 目前的工作目錄。這些範例假設是 foo 的工作目錄:

:foo 等同於 //foo:foo
bar:wiz 等同於 //foo/bar:wiz
bar/wiz 等同於:
  • 如果 foo/bar/wiz 是套件,則為 //foo/bar/wiz:wiz
  • 如果 foo/bar 是套件,則為 //foo/bar:wiz
  • 其他情況則為 //foo:bar/wiz
bar:all 等同於 //foo/bar:all
:all 等同於 //foo:all
...:all 等同於 //foo/...:all
... 等同於 //foo/...:all
bar/...:all 等同於 //foo/bar/...:all

根據預設,系統會依循遞迴性的目標模式,遵循目錄符號連結。 但對於位於輸出基礎之下的函式除外 在工作區根目錄中建立的符號連結。

此外,Bazel 在評估遞迴目標時,不會追蹤符號連結 任何目錄,且包含以下檔案名稱: DONT_FOLLOW_SYMLINKS_WHEN_TRAVERSING_THIS_DIRECTORY_VIA_A_RECURSIVE_TARGET_PATTERN

foo/... 是「套件」的萬用字元,表示所有套件會以遞迴方式表示 在 foo 目錄下 (適用於套件路徑的所有根目錄)。:all是 萬用字元取代 目標,比對套件中的所有規則。這兩個可能性 與 foo/...:all 相輔相成,且在同時使用這兩種萬用字元的情況下,可能會 縮寫為 foo/...

此外,:* (或 :all-targets) 是符合每個目標的萬用字元 相符套件中的檔案,包括通常並非由任何規則建構的檔案 例如與 java_binary 規則相關聯的 _deploy.jar 檔案

這表示 :* 表示 :all超集;而潛在 這種語法能讓您使用熟悉的 :all 萬用字元 不需要建構 _deploy.jar 等目標。

此外,Bazel 允許使用斜線取代 標籤語法以使用 Bash 檔案名稱擴充檔案來說,這種做法通常相當方便。 舉例來說,foo/bar/wiz 等於 //foo/bar:wiz (如果有 foo/bar 套件) 或 //foo:bar/wiz (如果有 foo 套件的話)。

許多 Bazel 指令接受目標模式清單做為引數,而且所有 遵循前置字串否定運算子 -。可用於排除一組 來自前述引數指定的集合目標。請注意 這點非常重要例如:

bazel build foo/... bar/...

也就是「建立 foo 底下的所有目標,並且」建立 bar 底下的所有目標,而

bazel build -- foo/... -foo/bar/...

亦即「建立 foo 底下的所有目標,但 foo/bar 底下的目標除外」。( 必須提供 -- 引數,以防後續引數開頭為 - 不顯示為其他選項)

請特別注意 保證不會建構,因為它們可能是目標的依附關係 就必須輸入其他的數字例如,假設某個目標為 //foo:all-apis 其他依附於 //foo/bar:api 的其他依附於 其中一個部分

萬用字元的目標模式不含含有 tags = ["manual"] 的目標 (...:*:all 等)。 bazel buildbazel test (但包含在 排除萬用字元目標模式,否則系統會將所有可能列入計算。請 請使用明確的目標模式,在指令列中指定這類測試目標 (如果可能發生這種情況) 以便 Bazel 建構/測試映像檔相對地,bazel query 表現不如預期 自動進行上述行為 bazel query)。

擷取外部依附元件

根據預設,Bazel 會在 建構應用程式然而,這可能並非垃圾資訊 或想將新的外部依附元件 「預先擷取」依附關係 (例如在航班起飛前)。如果發生以下情況: 為了避免在建構期間新增依附元件 可以指定 --fetch=false 標記請注意,這個旗標僅適用於 會套用到未指向本機目錄的存放區規則 以及檔案系統變更 (例如 local_repository) new_local_repository 以及 Android SDK 和 NDK 存放區規則 不論 --fetch 值為何,都會一律生效。

如果您在建構期間不允許擷取,且 Bazel 會發現新的外部外部 您的建構將會失敗。

您可以執行 bazel fetch,手動擷取依附元件。如果 如果您在建構期間不允許擷取,則必須執行 bazel fetch

  • 首次建構之前。
  • 新增外部依附元件之後。

測試完畢後,在 WORKSPACE 環境中就不需要再執行一次 檔案變更。

fetch 會使用目標清單來擷取依附元件。適用對象 例如,系統會擷取建構 //foo:bar 所需的依附元件 和 //bar:baz

bazel fetch //foo:bar //bar:baz

如要擷取工作區的所有外部依附元件,請執行下列指令:

bazel fetch //...

手邊所有工具都準備就緒之後,就不需要執行 bazel 擷取 ,並在工作區根目錄底下使用 (從程式庫 jar 到 JDK 本身)。 不過,如果您在工作區目錄以外使用任何內容,則請使用 Bazel 會在執行 bazel fetch前自動執行 bazel build

存放區快取

Bazel 會嘗試避免多次擷取同一個檔案,即使 無論檔案是位於不同工作區 已變更存放區,但仍需下載相同的檔案。方法如下 bazel 會快取存放區快取中下載的所有檔案 位於 ~/.cache/bazel/_bazel_$USER/cache/repos/v1/--repository_cache 選項即可變更位置。 所有工作區與已安裝的 bazel 版本會共用快取。 如果發生以下情況,系統會從快取中擷取項目: Bazel 會確保擁有正確檔案的副本 下載請求具有指定檔案的 SHA256 總和,以及具有此 雜湊在快取中。因此,為每個外部檔案指定雜湊值 從資安的角度來看,這不只是個好主意也有助於避開 不需要下載任何項目

每次快取命中時,快取中檔案的修改時間會是 已更新。如此一來,即可輕易上次在快取目錄中使用檔案 例如手動清除快取絕不會快取 因為該檔案備份的檔案副本 可用的串流。

發布檔案目錄

發行目錄是另一種 Bazel 機制,可避免不必要的檔案 下載。Bazel 會在存放區快取之前,搜尋發行版目錄。 主要差異在於發布目錄需要手動操作 做好萬全準備

使用 --distdir=/path/to-directory 選項,您可以指定其他唯讀目錄來尋找檔案 而不需要擷取這些物件如果檔案名稱為 等於網址的基礎名稱,此外還有檔案的雜湊碼為 等於下載要求中指定的值。只有在 檔案雜湊是在 WORKSPACE 宣告中指定。

儘管檔案名稱的條件不一定正確,但 將候選檔案數量縮減至每個指定目錄一個。在本 因此,即使對 YAML 檔案 這類目錄內的檔案數量會大幅增加。

在氣隙環境中執行 Bazel

為了縮減 Bazel 二進位檔的大小,系統將擷取 Bazel 的隱含依附元件 都會連線到網路這些隱含的依附元件 內含可能並非所有人所需的工具鍊和規則。適用對象 例如,只有在建構 Android 時,才能拆散及擷取 Android 工具 Google Cloud 的 Resource Manager 工具 經特別設計,能以程式輔助方式協助您管理專案

然而,這類隱含的依附元件可能會在執行時引發問題 位於氣隙環境中的 Bazel,即使您已將所有 WORKSPACE 依附元件。如要解決這個問題,請先準備發布目錄 具備這些依附元件的機器、具備網路存取權的機器 並透過離線方式 將裝置轉移到氣隙環境

如要準備發布目錄,請使用 --distdir敬上 旗標。您必須針對每個新的 Bazel 二進位檔版本執行這項作業一次,因為 每個版本的隱含依附元件可能都不同。

如要在 Airgapped 環境之外建構這些依附元件,請先 檢查 Bazel 來源樹狀結構的正確版本:

git clone https://github.com/bazelbuild/bazel "$BAZEL_DIR"
cd "$BAZEL_DIR"
git checkout "$BAZEL_VERSION"

接著,建構內含隱含執行階段依附元件的 tarball 特定的 Bazel 版本:

bazel build @additional_distfiles//:archives.tar

將這個 tarball 匯出至可以複製到 Airgapped 的目錄 環境。注意 --strip-components 旗標,因為 --distdir 可以是 對目錄巢狀層級很複雜:

tar xvf bazel-bin/external/additional_distfiles/archives.tar \
  -C "$NEW_DIRECTORY" --strip-components=3

最後,在 Airgapped 環境中使用 Bazel 時,請將 --distdir 旗標,開啟目錄。為了方便起見,您可以將其新增為 .bazelrc 項目:

build --distdir=path/to/directory

建構設定和跨程式碼編譯

指定特定建構行為和結果的所有輸入項目都可以 可以劃分為兩個不同的類別第一種是內建函式 儲存在專案的 BUILD 檔案中的資訊:建構規則、 屬性的所有值,以及轉換式依附元件的完整集合。 第二種是使用者提供的外部或環境資料,或 建構工具:選擇目標架構、編譯和連結 以及其他工具鍊設定選項。我們現在要介紹 做為「設定」

在任何建構作業中,可能都有多項設定。假設 跨平台程式碼編譯,分別針對 64 位元版本建構 //foo:bin 執行檔 但工作站是 32 位元的機器顯然,建構作業 必須使用能夠建立 64 位元的工具鍊建構 //foo:bin 但是,建構系統也必須建立各種工具, 例如透過原始碼建構的工具 用於 Genrule 等工具 因此必須建立於工作站因此 現在可以識別兩種設定:exec 設定,用於 ,以及目標設定 (或要求設定,不過我們通常會說「目標設定」 但這個字詞已經有多種含意,可以用來建立 最終要求的二進位資料

一般來說,要求同時具備眾多程式庫的必備條件。 建構目標 (//foo:bin) 和一或多個 exec 工具,例如 基本程式庫。這類程式庫必須建立兩次,一次是為 exec 另一個則用於目標設定Bazel 會負責 確保建構兩個變化版本,並保留衍生檔案 以免干擾這類目標通常可以並行建構 因為兩者是相互獨立的如果看到進度訊息 表示特定目標會建立兩次,這很有可能是 解釋。

exec 設定衍生自目標設定,如下所示:

  • 使用與--crosstool_top 要求設定 (除非指定 --host_crosstool_top)。
  • 針對 --cpu 使用 --host_cpu 的值 (預設值:k8)。
  • 請使用要求中所指定的相同值 設定:--compiler--use_ijars,以及 --host_crosstool_top 是否為 ,則 --host_cpu 的值會用來查詢 Crosstool 中的 default_toolchain (忽略 --compiler) 用於執行項目 此外還會從 0 自動調整資源配置 您完全不必調整資源調度設定
  • 針對「--javabase」使用「--host_javabase」的值
  • 針對「--java_toolchain」使用「--host_java_toolchain」的值
  • 使用針對 C++ 程式碼 (-c opt) 的最佳化版本。
  • 不產生偵錯資訊 (--copt=-g0)。
  • 從執行檔和共用程式庫中移除偵錯資訊 (--strip=always).
  • 將所有衍生檔案放在特殊位置,而不是用來 任何可能的要求設定
  • 利用建構資料限制二進位檔的戳記 (請參閱 --embed_* 選項)。
  • 所有其他值都保留預設值。

傾向選擇不同高階主管的理由有很多 從要求設定中移除最重要的是:

第一,運用經過最佳化的精簡二進位檔,有助於減少 連結和執行工具、工具佔用的磁碟空間, 分散式建構中的網路 I/O 時間

其次,將 exec 和要求設定分離所有建構作業中的設定後 避免在進行微幅調整後 以及要求設定 (例如變更連結器選項等),

修正漸進式重建錯誤

Bazel 專案的其中一項主要目標是確保正確的漸進式內容 和重建作業使用過去的建構工具,尤其是以 Make 為基礎的工具 實作漸進式建構作業。

首先,檔案的時間戳記會隨時間增加。雖然這是 這種情況就很容易忽略這樣的假設同步到 造成檔案修改時間縮短。 製造系統無法重建。

一般而言,「Make」會偵測檔案變更,但無法偵測變更 下達指令如果您修改了指定版本中傳遞至編譯器的選項 步驟,Make 不會重新執行編譯器,因此您必須手動捨棄 先前建構作業使用 make clean 時的無效輸出內容。

此外,Make 方法未成功終止應用程式的其中一項資源終止運作 子程序開始寫入輸出檔案之後。雖然 目前的 Make 執行失敗,然後對 Make 的後續叫用動作 盲人誤認為截斷的輸出檔案有效 (因為 也不會重新建構。同樣地,如果 Make 程序 也可能會發生類似情況

Bazel 能避免這些假設和其他假設。Bazel 會維護 先前完成的工作,並且只會省略建構步驟 (如果找到建構步驟) 在該建構步驟的輸入檔案 (及其時間戳記) 剛好符合資料庫中的一個指令 資料庫項目之輸出檔案組 (及其時間戳記) 完全符合 磁碟上檔案的時間戳記。對輸入檔案或輸出內容所做的任何變更 或指令本身,會導致重新執行建構步驟。

採用正確的漸進式建構技術,能為使用者帶來的好處:縮短作業所需時間 混淆不清(此外,無論是有必要或先佔情況,使用 make clean 導致的重新建構時間也縮短了)。

建構一致性及漸進式建構

正式上,系統會將建構狀態定義為「一致」, 每個輸出檔案存在,而且其內容正確無誤,如步驟或 建立 Pod 時所需遵循的規則編輯來源檔案時, 聲稱建構作業不一致,且在您下次執行前會保持不一致 才能使用建構工具順利完成這種狀況是不穩定 不保持一致的情況,因為這是暫時性的,而且系統會根據 執行建構工具

還有另一種常見的不一致情形:穩定 不一致如果版本達到穩定不一致的狀態,則 成功叫用建構工具並不會還原一致性:建構作業 的輸出內容仍為「卡住」,輸出卻仍不正確。穩定狀態不一致 是 Make (及其他建構工具) 使用者輸入 make clean 的主要原因。 發現建構工具以這種方式失敗 (然後復原) 可能會相當耗時且令人困擾

從概念上來說,如要打造一致的版本,最簡單的方法就是捨棄 再次啟動先前建構作業的輸出內容:讓每個建構作業都建構乾淨的建構作業 這種做法顯然太耗時,只能實際操作 因此,建構工具必須能夠 在不影響一致性的情況下執行漸進式建構作業

修正漸進式依附性分析並不容易,如上所述, 不過,其他建構工具仍能避免 漸進式建構作業相對地,Bazel 則提供以下保證: 您未進行任何編輯的情況下成功叫用建構工具, 保持在一致狀態(如果您在 Bazel 無法保證模型的一致性 目前的版本但保證「下一個」版本的結果將會 復原一致性)。

和所有保證一樣,有一些附屬細則:一些已知方法 而不是在使用 Bazel 取得穩定不一致的狀態時我們不保證 我們會積極嘗試找出 依靠依附元件分析,但我們會調查並盡力修正 因正常或「合理」而造成的所有穩定狀態狀態使用 建立工具

如果您透過 Bazel 偵測到穩定不一致的狀態,請回報錯誤。

採用沙箱機制的執行作業

Bazel 使用沙箱來確保動作會自動執行 正確。Bazel 會在符合下列條件的沙箱中執行 spawns (大致說法:動作) 僅包含工具執行工作所需的最低限度檔案。目前 採用 CONFIG_USER_NS 選項的 Linux 3.12 以上版本才適用沙箱機制 且在 macOS 10.11 以上版本

如果您的系統不支援使用沙箱機制來快訊,Bazel 就會顯示警告 事實上,建構不保證是密封的,而且可能會影響 以不明方式存取主機系統如要停用這則警告,可以 --ignore_unsupported_sandboxing 標記傳送至 Bazel。

在某些平台上,例如 Google Kubernetes Engine 叢集節點或 Debian 為了安全起見,命名空間 疑慮。您可以查看檔案 /proc/sys/kernel/unprivileged_userns_clone:如果存在且包含 0, 即可在 Android Studio 中 sudo sysctl kernel.unprivileged_userns_clone=1

在某些情況下,Bazel 沙箱會因為系統的緣故而無法執行規則 設定。問題通常為失敗,輸出的訊息類似 namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory。 此時,請嘗試使用以下指令,停用 genrules 的沙箱: 「--strategy=Genrule=standalone」及其他 --spawn_strategy=standalone。另請回報 Issue Tracker,並提及您使用的 Linux 發行版,以便我們 調查並在後續版本中提供修正。

建構階段

在 Bazel 中,建構作業有三個不同的階段而是要瞭解 可讓您深入瞭解控管建構作業的可用選項 (請見下文)。

正在載入階段

第一種是「載入」,在此期間會需要所有必要的 BUILD 檔案。 並載入初始目標及其依附元件的遞移情形 已經過剖析、評估及快取。

對啟動 Bazel 伺服器後的第一個建構作業而言,載入階段通常 因為從檔案系統載入 BUILD 檔案要花幾秒鐘時間。於 後續的建構作業,特別是在沒有 BUILD 檔案變更的情況下發生載入

這個階段回報的錯誤包括:找不到套件、找不到目標、 BUILD 檔案中的詞法和文法錯誤,以及評估錯誤。

分析階段

第二階段「分析」包含語意分析和驗證 每個建構規則、建構依附元件圖的結構 決定每個步驟中要執行的工作。

如同載入,分析也需要幾秒鐘的時間才能完整計算。 然而,Bazel 會將依附元件圖表從一個建構作業快取 會重新分析其功用,大幅加快漸進式建構作業的執行速度 套件自上次版本後未曾變更。

此階段回報的錯誤包括:不當依附性、無效 輸入規則,以及與規則相關的所有錯誤訊息

Bazel 會避免不必要的檔案,因此載入和分析階段的速度相當快 這個階段的 I/O 大會只讀取 BUILD 檔案,以便判斷要使用的成果 完成。這是依據設計而得,讓 Bazel 成為分析工具的絕佳基礎。 例如 Bazel 的 query 指令 事實上,Gartner 的資料顯示 只有一半的企業機器學習專案通過前測階段

執行階段

版本的第三個階段是「執行」。這個階段可確保 建構中每個步驟的輸出內容與輸入內容一致,然後重新執行 編譯/連結等視需要調整工具。這個步驟是建構項目 大部分的時間,從幾秒鐘到多小時不等,大型時間長達一小時 建構應用程式在這個階段回報的錯誤包括:缺少來源檔案、錯誤 例如由某些建構動作執行,或者工具故障,導致無法產生 預期會產生一組預期的輸出內容