本頁重點說明如何編寫與 Windows 相容的規則、編寫可攜式規則的常見問題,以及一些解決方案。
路徑
問題:
長度限制:路徑長度上限為 259 個字元。
雖然 Windows 也支援較長的路徑 (最多 32767 個半形字元),但許多程式都是以較低的限制建構。
請留意在動作中執行的程式。
工作目錄:長度上限為 259 個半形字元。
程序無法將
cd
放入長度超過 259 個字元的目錄。區分大小寫:Windows 路徑不區分大小寫,Unix 路徑則區分大小寫。
建立動作的命令列時,請留意這一點。
路徑分隔符:是反斜線 (
\`), not forward slash (
/`)。Bazel 會以
/
分隔符儲存 Unix 風格的路徑。雖然部分 Windows 程式支援 Unix 風格的路徑,但其他程式則不支援。部分 cmd.exe 內建指令支援這些功能,部分則不支援。建立動作的指令列和環境變數時,建議一律使用
\` separators on Windows: replace
/with
。絕對路徑:開頭不得以斜線 (
/
) 開頭。Windows 上的絕對路徑開頭為磁碟機代號,例如
C:\foo\bar.txt
。沒有單一檔案系統根目錄。如果規則會檢查路徑是否為絕對路徑,請注意這一點。請避免使用絕對路徑,因為這些路徑通常無法移植。
解決方法:
路徑應簡短。
避免使用長的目錄名稱、深層巢狀目錄結構、長檔案名稱、長工作區名稱和長目標名稱。
所有這些都可能成為動作輸入檔案的路徑元件,並且可能會耗盡路徑長度限制。
使用較短的輸出根目錄。
使用
--output_user_root=<path>
旗標指定 Bazel 輸出的簡短路徑。建議您為 Bazel 輸出內容 (例如D:\`), and adding this line to your
.bazelrc 檔案) 專門建立一個磁碟 (或虛擬磁碟):build --output_user_root=D:/
或
build --output_user_root=C:/_bzl
使用交叉點。
簡單來說,交叉點就是目錄符號連結[1]。路徑點很容易建立,而且可以指向 (位於同一台電腦上的) 長路徑目錄。如果建構動作建立的路徑短但目標長的連接點,則具有短路徑限制的工具可以存取連接點目錄中的檔案。
您可以在
.bat
檔案或 cmd.exe 中建立交叉連結,如下所示:mklink /J c:\path\to\junction c:\path\to\very\long\target\path
[1]:嚴格來說,路徑並非符號連結,但為了建構動作,您可以將路徑視為目錄符號連結。
在動作 / 環境變數的路徑中,將
/
替換為 ``。為動作建立指令列或環境變數時,請使用 Windows 風格的路徑。範例:
def as_path(p, is_windows): if is_windows: return p.replace("/", "\\") else: return p
環境變數
問題:
區分大小寫:Windows 環境變數名稱不區分大小寫。
舉例來說,在 Java 中,
System.getenv("SystemRoot")
和System.getenv("SYSTEMROOT")
會產生相同的結果。(這也適用於其他語言)。Hermeticity:動作應盡量少用自訂環境變數。
環境變數是動作快取鍵的一部分。如果動作使用經常變更的環境變數,或使用者自訂的變數,就會導致規則較不易快取。
解決方法:
請只使用大寫字母的環境變數名稱。
這項功能適用於 Windows、macOS 和 Linux。
盡量減少動作環境。
使用
ctx.actions.run
時,請將環境設為ctx.configuration.default_shell_env
。如果動作需要更多環境變數,請將這些變數全部放入字典中,然後將字典傳遞至動作。範例:load("@bazel_skylib//lib:dicts.bzl", "dicts") def _make_env(ctx, output_file, is_windows): out_path = output_file.path if is_windows: out_path = out_path.replace("/", "\\") return dicts.add(ctx.configuration.default_shell_env, {"MY_OUTPUT": out_path})
動作
問題:
可執行輸出內容:每個可執行檔案都必須有可執行的副檔名。
最常見的副檔名為
.exe
(二進位檔案) 和.bat
(批次指令碼)。請注意,Windows 無法執行殼層指令碼 (
.sh
),因此您無法將其指定為ctx.actions.run
的executable
。檔案也沒有+x
權限,因此您無法像在 Linux 上一樣執行任意檔案。Bash 指令:為方便移植,請勿在動作中直接執行 Bash 指令。
Bash 在類 Unix 系統中很常見,但在 Windows 上通常無法使用。Bazel 本身越來越少依賴 Bash (MSYS2),因此日後使用者不太可能會在安裝 Bazel 時一併安裝 MSYS2。為方便在 Windows 上使用規則,請避免在動作中執行 Bash 指令。
行結尾:Windows 使用 CRLF (
\r\n
),類 Unix 系統則使用 LF (\n
)。比較文字檔案時請留意這一點。請留意 Git 設定,特別是在檢出或提交時留意行結尾。(請參閱 Git 的
core.autocrlf
設定)。
解決方法:
使用專為特定用途而設計的 Bash 規則。
native.genrule()
是 Bash 指令的包裝函式,通常用於解決簡單的問題,例如複製檔案或寫入文字檔。您可以避免依賴 Bash (並重複發明輪子):請查看 bazel-skylib 是否有專門為您需求而設計的規則。在 Windows 上建構/測試時,這些工具都不會依附於 Bash。建構規則範例:
copy_file()
(來源、說明文件):將檔案複製到其他位置,並視需要將其設為可執行檔案write_file()
(source, documentation):寫入文字檔案,並加上所需的列結尾 (auto
、unix
或windows
),可視需要將其設為可執行 (如果是指令碼)run_binary()
(source, documentation):以指定的輸入內容和預期輸出內容,執行二進位檔 (或*_binary
規則) 做為建構動作 (這是ctx.actions.run
的建構規則包裝函式)native_binary()
(來源、說明文件):在*_binary
規則中包裝原生二進位檔,您可以bazel run
或在run_binary()
的tool
屬性或native.genrule()
的tools
屬性中使用
測試規則範例:
diff_test()
(source, documentation):比較兩個檔案內容的測試native_test()
(source, documentation):在*_test
規則中包裝原生二進位檔,您可以bazel test
在 Windows 上,請考慮使用
.bat
指令碼執行瑣事。您可以使用
.bat
指令碼解決簡單的任務,而非.sh
指令碼。舉例來說,如果您需要的指令碼是什麼都不做、顯示訊息,或以固定錯誤代碼結束,那麼只要使用簡單的
.bat
檔案即可。如果規則傳回DefaultInfo()
供應器,executable
欄位可能會參照 Windows 上的.bat
檔案。由於 macOS 和 Linux 不受限於檔案副檔名,因此您可以一律使用
.bat
做為副檔名,即使是殼層指令碼也是如此。請注意,空白的
.bat
檔案無法執行。如需空白指令碼,請在其中輸入一個空格。以有原則的方式使用 Bash。
在 Starlark 建構和測試規則中,使用
ctx.actions.run_shell
以動作執行 Bash 指令碼和 Bash 指令。在 Starlark 巨集中,請將 Bash 指令碼和指令包在
native.sh_binary()
或native.genrule()
中。Bazel 會檢查是否可使用 Bash,並透過 Bash 執行指令碼或指令。在 Starlark 存放區規則中,請盡量避免使用 Bash。Bazel 目前無法提供在存放區規則中以原則方式執行 Bash 指令的方式。
刪除檔案
問題:
開啟檔案時無法刪除檔案。
根據預設,開啟的檔案無法刪除,嘗試刪除會導致「Access Denied」錯誤。如果無法刪除檔案,可能是因為仍有執行中的程序正在開啟該檔案。
無法刪除執行中程序的工作目錄。
處理程序會對其工作目錄開啟句柄,因此在處理程序結束前,無法刪除目錄。
解決方法:
在程式碼中,請盡可能立即關閉檔案。
在 Java 中,請使用
try-with-resources
。在 Python 中,請使用with open(...) as f:
。原則上,請盡快關閉句柄。