在 Windows 上編寫規則

回報問題 查看原始碼 Nightly 8.1 · 8.0 · 7.5 · 7.4 · 7.3 · 7.2

本頁重點說明如何編寫與 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。沒有單一檔案系統根目錄。

    如果規則會檢查路徑是否為絕對路徑,請注意這一點。請避免使用絕對路徑,因為這些路徑通常無法移植。

解決方法:

環境變數

問題:

  • 區分大小寫: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.runexecutable。檔案也沒有 +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() (來源source說明文件):將檔案複製到其他位置,並視需要將其設為可執行檔案

    • write_file() (source, documentation):寫入文字檔案,並加上所需的列結尾 (autounixwindows),可視需要將其設為可執行 (如果是指令碼)

    • run_binary() (source, documentation):以指定的輸入內容和預期輸出內容,執行二進位檔 (或 *_binary 規則) 做為建構動作 (這是 ctx.actions.run 的建構規則包裝函式)

    • native_binary() (source說明文件):在 *_binary 規則中包裝原生二進位檔,您可以 bazel run 或在 run_binary()tool 屬性或 native.genrule()tools 屬性中使用

    測試規則範例:

  • 在 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:。原則上,請盡快關閉句柄。