存放區規則

回報問題 查看原始碼

本頁面說明如何建立存放區規則,並提供詳細資料範例。

外部存放區是只能在 WORKSPACE 檔案中使用的規則,可在 Bazel 的載入階段啟用非密封作業。每個外部存放區規則都會建立專屬的工作區,內含專屬的 BUILD 檔案和構件。這些目錄可以依附於第三方程式庫 (例如 Maven 封裝程式庫),也可產生執行主機 Bazel 專屬的 BUILD 檔案。

建立存放區規則

.bzl 檔案中,使用 repository_rule 函式建立新的存放區規則,並將其儲存在全域變數中。

自訂存放區規則的使用方式和原生存放區規則一樣。它具有必要的 name 屬性,而建構檔案中的每個目標都可以稱為 @<name>//package:target,其中 <name>name 屬性的值。

當您明確建構規則,或為建構作業的依附元件時,系統就會載入規則。在這種情況下,Bazel 會執行其 implementation 函式。這個函式會說明如何建立存放區、存放區內容和 BUILD 檔案。

屬性

屬性是以字典的形式傳遞到 attrs 規則引數的規則引數。定義存放區規則時,系統會列出屬性及其類型。將 urlsha256 屬性定義為字串的範例:

local_repository = repository_rule(
    implementation=_impl,
    local=True,
    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 (就像建構規則) 和 repo_mapping。存放區規則的名稱可透過 repository_ctx.name 存取。repo_mapping 的意義與原生存放區規則 local_repositorynew_local_repository 的意義相同。

如果屬性名稱開頭為 _,就會設為私人,使用者無法設定。

實作函式

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

這個函式只有一個輸入參數 repository_ctx。函式會傳回 None,表示在指定參數下可重現規則,或傳回包含該規則一組參數的 dict,然後將該規則轉成可重現的存放區。舉例來說,如果規則追蹤 Git 存放區的規則,則會傳回特定修訂版本 ID,而不是原先指定的浮動分支版本。

輸入參數 repository_ctx 可用於存取屬性值和非密封函式 (尋找二進位檔、執行二進位檔、在存放區中建立檔案或從網際網路下載檔案)。詳情請參閱程式庫。示例:

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

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

何時會執行實作函式?

當 Bazel 需要該存放區的目標時,就會執行存放區的實作功能,例如當 Bazel 中有其他目標指定該存放區時,或是在指令列中提及該目標時。接著,實作函式應會在檔案系統中建立存放區。這就是所謂的「擷取」存放區。

與一般目標相比,存放區不見得會重新擷取,導致存放區發生差異的變更。這是因為 Bazel 無法偵測變更內容,或是會造成每項建構作業產生過多負擔 (例如從網路擷取的內容)。因此,只有在發生下列其中一種情況時,系統才會重新擷取存放區:

  • 傳遞至 WORKSPACE 檔案中存放區宣告的參數。
  • 構成存放區實作的 Starlark 程式碼。
  • 傳遞至 repository_ctxgetenv() 方法,或以 repository_ruleenviron 屬性宣告的任何環境變數的值。這些環境變數的值可以用 --repo_env 標記在指令列上以硬式編碼的方式連接。
  • 傳遞至 read()execute() 以及由標籤參照的 repository_ctx 類似方法 (例如 //mypkg:label.txt,而非 mypkg/label.txt) 的任何檔案內容
  • 執行 bazel sync 時。

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

  • 如果已設定 configure 旗標,只有在將 --configure 參數傳遞至 bazel sync 時,才能在 bazel sync 重新擷取存放區 (如未設定這項屬性,這個指令不會導致重新擷取)
  • 如果您已經設定了 local 標記,除了上述情況外,也會在 Bazel 伺服器重新啟動,或影響存放區宣告的任何檔案 (例如 WORKSPACE 檔案或會載入的檔案) 變更時重新擷取存放區,無論變更是否導致存放區或其程式碼有所變更。

    在這種情況下,系統不會重新擷取非本機存放區。這是因為系統會假設這些存放區與網路通訊,或是所費不貲。

重新啟動實作函式

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

強制重新擷取外部存放區

有時候,外部存放區可能會過時,而無需變更其定義或依附元件。舉例來說,擷取來源的存放區可能會追蹤第三方存放區的特定分支版本,且該分支版本有新的修訂版本。在此情況下,您可以呼叫 bazel sync,要求 bazel 無條件重新擷取所有外部存放區。

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

示例

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

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

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