存放區規則

回報問題 查看來源

本頁說明如何定義存放區規則,並提供範例以瞭解詳情。

外部存放區是一種目錄樹狀結構,當中包含可在 Bazel 建構作業中使用的來源檔案,該存放區會視需要執行對應的存放區規則。您可以用多種方式定義存放區,但最終,每個存放區都是透過叫用存放區規則來定義,就如同叫用建構規則來定義建構目標一樣。這些程式庫可用於依附第三方程式庫 (例如 Maven 封裝程式庫),也可以產生執行時所在的主機 Bazel 專屬的 BUILD 檔案。

存放區規則定義

.bzl 檔案中,使用 repository_rule 函式定義新的存放區規則,並儲存至全域變數。定義存放區規則後,該規則即可叫用為函式,用於定義存放區。此叫用通常是從模組擴充功能實作函式中執行。

存放區規則定義的兩個主要元件是屬性結構定義和實作函式。屬性結構定義會決定傳遞至存放區規則叫用的屬性名稱和類型,系統會在需要擷取存放區時執行實作函式。

屬性

屬性是傳遞至存放區規則叫用的引數。存放區規則接受的屬性結構定義是透過呼叫 repository_rule 定義時,請使用 attrs 引數指定。將 urlsha256 屬性定義為字串的範例:

http_archive = repository_rule(
    implementation=_impl,
    attrs={
        "url": attr.string(mandatory=True)
        "sha256": attr.string(mandatory=True)
    }
)

如要存取實作函式中的屬性,請使用 repository_ctx.attr.<attribute_name>

def _impl(repository_ctx):
    url = repository_ctx.attr.url
    checksum = repository_ctx.attr.sha256

所有 repository_rule 都有隱含定義的屬性 name。這是一個字串屬性,運作方式略有不同:指定做為存放區規則叫用的輸入內容時,系統會取得一個明確的存放區名稱;但使用 repository_ctx.attr.name 從存放區規則的實作函式讀取資料時,會傳回標準存放區名稱。

實作函式

每個存放區規則都需要 implementation 函式。其中包含規則的實際邏輯,且會嚴格在載入階段執行。

這個函式只有一個輸入參數 repository_ctx。函式會傳回 None,表示根據指定的參數,表示規則可重現;或是含有規則參數組合的字典,都會將規則轉化為可重現的相同存放區。例如,若規則追蹤 Git 存放區,該存放區會傳回特定修訂版本 ID,而非原先指定的浮動分支版本。

輸入參數 repository_ctx 可用於存取屬性值和非傳統函式 (尋找二進位檔、執行二進位檔、在存放區中建立檔案或從網際網路下載檔案)。如需更多背景資訊,請參閱 API 說明文件。示例:

def _impl(repository_ctx):
  repository_ctx.symlink(repository_ctx.attr.path, "")

local_repository = repository_rule(
    implementation=_impl,
    ...)

何時會執行實作函式?

當 Bazel 需要該存放區的目標時,就會執行存放區規則的實作函式。舉例來說,當 Bazel 中的另一個目標 (位於其他存放區) 需要該目標,或是該指令已在指令列中提及時,就會執行該存放區規則的實作函式。接著,實作函式應會在檔案系統中建立存放區。這就是所謂的「擷取」存放區。

相較於一般目標,如果某些變更會導致存放區不同,系統不一定會重新擷取存放區。這是因為 Bazel 無法偵測變更的部分,或是造成每次建構作業的負擔過多 (例如從網路擷取的項目)。因此,只有在下列其中一項情況發生變更時,系統才會重新擷取存放區:

  • 傳遞至存放區規則叫用的屬性。
  • 包含存放區規則實作的 Starlark 程式碼。
  • 傳遞至 repository_ctxgetenv() 方法,或使用 repository_ruleenviron 屬性宣告的任何環境變數值。這些環境變數的值可在指令列上利用 --repo_env 標記使用。
  • 傳遞至 read()execute()repository_ctx 的類似方法,由標籤參照 (例如 //mypkg:label.txt,而非 mypkg/label.txt)
  • 執行 bazel fetch --force 時。

repository_rule 有兩個參數可控制重新擷取存放區的時機:

  • 如果設定了 configure 旗標,系統只會在 --configure 參數傳遞 --configure 參數時,在 bazel fetch 上重新擷取存放區 (如果未設定屬性,這個指令不會導致重新擷取)
  • 如果設定了 local 旗標,除了上述情況以外,當 Bazel 伺服器重新啟動時,也會重新擷取存放區。

重新啟動實作函式

如果實作函式要求的依附元件「缺少」,則可在擷取存放區時重新啟動。在這種情況下,系統將停止實作函式,並且會解析缺少的依附元件,並在解析依附元件後重新執行函式。為避免不必要的重新啟動 (成本高昂,因為網路存取可能需要重複),系統會預先擷取標籤引數,前提是所有標籤引數皆可解析至現有檔案。請注意,解析僅在函式執行期間建構的字串或標籤路徑,仍可能會導致重新啟動。

強制重新擷取外部存放區

有時候,外部存放區可能會因定義或依附元件而過舊而過時。例如,擷取來源的存放區可能會追蹤第三方存放區的特定分支版本,而該分支版本中可用新的修訂版本。在這種情況下,您可以呼叫 bazel fetch --force --all,要求 bazel 無條件重新擷取所有外部存放區。

此外,某些存放區規則會檢查本機電腦,如果本機電腦升級,就可能過時。您可以在這裡要求 Bazel 只重新擷取 repository_rule 定義已設定 configure 屬性的外部存放區,請使用 bazel fetch --all --configure

範例

  • C++ 自動設定的工具鍊:它會使用存放區規則,尋找本機 C++ 編譯器、環境和 C++ 編譯器支援的標記,自動建立 Bazel 的 C++ 設定檔。

  • Go 存放區使用多個 repository_rule 來定義使用 Go 規則所需的依附元件清單。

  • rules_jvm_external 預設會建立一個名為 @maven 的外部存放區,此存放區會為遞移依附元件樹狀結構中的每個 Maven 構件產生建構目標。