Bazel 使用非常一致的配置來儲存建構構件,是開發人員常會感到混淆的管道。本頁會嘗試釐清運作方式。
Bazel output_base
根據設計,bazel build
指令一律不會將檔案寫入專案的來源目錄 (或其子目錄)。Bazel 改為使用使用者專屬的
平行目錄來儲存所有輸出內容 (稱為 user_output_root
),如下所示:
- Linux 系統中的
~/.cache/bazel/_bazel_$USER
。 /private/var/tmp/_bazel_$USER
(macOS)。%HOME%\_bazel_%USERNAME%
(Windows)。
針對執行 bazel
的每個工作區目錄,在 user_output_root
下會建立名為 output_base
的獨立目錄,如下所示:
${user_output_root}/<WORKSPACE_HASH>`
其中 <WORKSPACE_HASH>
是長十六進位雜湊 (根據目錄的絕對路徑計算)。
由於這個路徑完全無法預測,因此在 Bazel 專案中使用時,bazel info output_base
指令會列印該路徑。例如:
$ mkdir -p /tmp/project1 && cd /tmp/project1 && touch WORKSPACE.bazel
$ bazel info output_base
Starting local Bazel server and connecting to it...
/usr/local/google/home/digit/.cache/bazel/_bazel_digit/6c7b78994da78136b5cb6b7607361ad3
$ mkdir -p /tmp/project2 && cd /tmp/project2 && touch WORKSPACE.bazel
$ bazel info output_base
Starting local Bazel server and connecting to it...
/usr/local/google/home/digit/.cache/bazel/_bazel_digit/c37b9d68308ee5abe2f781dd38b733b9
$ mkdir -p /tmp/not-a-project && cd /tmp/not-a-project
$ bazel info output_base
WARNING: Invoking Bazel in batch mode since it is not invoked from within a workspace (below a directory having a WORKSPACE file).
ERROR: The 'info' command is only supported from within a workspace (below a directory having a WORKSPACE file).
See documentation at https://bazel.build/concepts/build-ref#workspace
這個配置很有彈性,但並不完美:
Pro:同一部機器的多位使用者可共用同一個唯讀專案目錄。
Pro:同一位使用者的多個專案目錄一律會使用獨立的輸出路徑。
缺點:想直接從指令列或圖形探索工具查看產生的檔案非常困難。
缺點:移除專案目錄 (例如使用
rm -rf .../my-project
) 並不會移除其輸出內容 (這是重要的廢棄物來源)。缺點:移動專案目錄 (例如使用
mv my-project my-project2
) 時,不會重複使用先前的output_base
內容,而舊有的output_base
內容現已無法存取。缺點:
user_output_root
的預設位置,因此output_base
通常不位於專案的相同檔案系統 / 分區。這可能會對效能 / 磁碟用量造成非預期的結果。
呼叫 bazel clean
即可從目前的 output_base
移除建構輸出內容。這項作業必須「先」完成,再移除來源專案目錄。
在實務上,您可以輕鬆利用過時的 Bazel 專案 (從未經過妥善清理) 清除的建構構件,破壞 user_output_root
的內容。更糟的是,如果嘗試直接移除 user_output_root
,可能就無法成功,因為根據預設,Bazel 會建立唯讀建構構件,這會導致 rm -rf ~/.cache/bazel
等指令無法運作!
Bazel output_base
內容:
其實有數個項目都儲存在 output_base
底下:
外部存放區的 Workspace 目錄:
這些依附元件會對應到外部專案依附元件。通常這些都不是專案原始碼樹狀結構的一部分,而是從網路下載或以程式產生的。
其內容會儲存在
${output_base}/external/<repository_name>
底下,其中external
部分是硬式編碼,<repository_name>
則與外部存放區的標準名稱相符。建構構件:
執行
bazel build
產生的檔案。儲存位置如下:${output_base}/execroot/<workspace_name>/bazel-out/<config_dir>/bin/
在此情況下:
execroot
、bazel-out
和bin
部分是硬式編碼,無法變更。針對專案自己的
BUILD.bazel
檔案中定義的目標,<workspace_name>
會預設為__main__
,除非您在專案的WORKSPACE.bazel
檔案中設定具有類似指令的指令:
workspace( name = "my_project", ) ``` - For targets defined in external repositories, `<workspace_name>` matches the repository's canonical name. - The `<config_dir>` value is a name derived from the build configuration used to configure the target that generated the build artifact. This allows rebuilding the same target in different ways, each time using a different `<config_dir>` value. Note: The `<config_dir>` value is **generally unpredictable**. More on this [here][bazel-config-dirs]
測試結果:
呼叫
bazel test
時產生的記錄檔,儲存在${output_base}/execroot/<workspace_name>/bazel-out/<config_dir>/testlogs/
下。內部快取和設定檔:
供遠端建構作業和遠端快取功能使用。開發人員可忽略這些檔案。
Bazel execroot
目錄:
execroot
會用於執行產生建構構件的 Bazel 指令,但做法取決於特定動作是否啟用沙箱機制。
在 Linux 和 MacOS 中,所有動作皆預設啟用沙箱功能。 從 Bazel 7 開始,Windows 沒有沙箱支援。
Bazel 動作可以在定義中使用
no-sandbox
標記,刻意停用沙箱功能。叫用
bazel
時,您可以使用--spawn_strategy=local
等選項在全域停用沙箱功能。
未使用沙箱:
停用沙箱功能後,凡是為特定工作區產生構件的建構動作,都會將輸出檔案放在 ${output_base}/execroot/<workspace_name>
下。
動作指令中顯示的來源檔案和建構構件的所有路徑都會因此成為相對路徑。
Bazel 確保在啟動指令之前,系統會在 execroot 下建立指令使用的輸入來源的符號連結。
舉例來說,會編譯 //src/foo/foo.cc
檔案 (包含 #include "foo.h"
) 且對應至 //src/foo/foo.h
的動作看起來可能會像這樣:
gcc -c -o bazel-out/k8-fastbuild/bin/src/foo/foo.o src/foo/foo.cc -Isrc/foo
有效原因:
執行指令之前,Bazel 會建立指向
$PROJECT/src
的符號連結${output_base}/execroot/__main__/src
,以便src/foo/foo.cc
和src/foo/foo.h
按照預期方式解析$PROJECT/src/foo/foo.cc
和$PROJECT/src/foo/foo.h
。bazel-out/k8-fastbuild/bin/src/foo/foo.o
位置是最終輸出路徑,對於透過這個指令使用建構設定編譯foo.cc
建立的物件檔案而言。
使用沙箱功能:
啟用沙箱機制後,Bazel 會為每個指令建立暫時目錄 (例如 ${output_base}/sandbox/linux-sandbox/<random-number>
),並建立符號連結樹狀結構,藉此模擬其下的執行根版面配置,但僅適用於已知的輸入內容。在本例中,看起來會像這樣:
輸入內容
${sandbox}/execroot/__main__/src/foo/foo.cc
和${sandbox}/execroot/__main__/src/foo/foo.h
的符號連結分別指向$PROJECT/src/foo/foo.cc
和$PROJECT/src/foo/foo.h
。在
${sandbox}/execroot/__main__
下方執行完全相同的指令。而不是${output_base}/execroot/__main__
。指令完成後,將已知輸出內容從
${sandbox}/execroot/__main__/bazel-out/k8-fastbuild/bin/src/foo/foo.o
的沙箱路徑複製到其${output_base}/execroot/__main__/bazel-out/k8-fastbuild/bin/src/foo/foo.o
的最終位置。最後,沙箱目錄及其所有內容都會遭到移除。這也表示系統會忽略未宣告的輸出內容。