使用外部依附元件

Bazel 可以依附其他專案中的目標。這些其他專案的依附元件稱為外部依附元件

工作區目錄中的 WORKSPACE 檔案 (或 WORKSPACE.bazel 檔案) 會告知 Bazel 如何取得其他專案的來源。這些專案可包含一或多個 BUILD 檔案,其擁有專屬目標。主要專案中的 BUILD 檔案可以藉由使用 WORKSPACE 檔案中的名稱來依賴這些外部目標。

例如,假設系統中有兩個專案:

/
  home/
    user/
      project1/
        WORKSPACE
        BUILD
        srcs/
          ...
      project2/
        WORKSPACE
        BUILD
        my-libs/

如果 project1 想要依附於 /home/user/project2/BUILD 中定義的目標 :foo,則可以在 /home/user/project2 中找到名為 project2 的存放區。接著,/home/user/project1/BUILD 中的目標可以依附於 @project2//:foo

WORKSPACE 檔案可讓使用者依賴檔案系統其他部分的目標,或是從網際網路下載的目標。這會使用與 BUILD 檔案相同的語法,但允許使用一組稱為「存放區規則」的規則 (有時又稱為「工作區規則」)。Bazel 隨附一些內建存放區規則和一組內嵌 Starlark 存放區規則。使用者也可以編寫自訂存放區規則以獲取更複雜的行為。

支援的外部依附元件類型

以下提供幾種外部依附元件的基本類型:

視其他 Bazel 專案

如要使用第二個 Bazel 專案中的目標,可以使用 local_repositorygit_repositoryhttp_archive 對本機檔案系統進行符號連結,分別參照或下載 Git 存放區。

例如,假設您正在處理 my-project/ 專案,且想要依賴同事專案 coworkers-project/ 的目標。這兩種專案都使用 Bazel,因此您可以將同事的專案新增為外部依附元件,然後使用您的同事從您 BUILD 檔案定義的任何目標。您要在 my_project/WORKSPACE 中新增以下內容:

local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
)

如果您的同事具有目標 //foo:bar,您的專案可以將其稱為 @coworkers_project//foo:bar。外部專案名稱必須是有效的工作區名稱

視非 Bazel 專案

加上 new_ 前置字串的規則 (例如 new_local_repository),可讓您從未使用 Bazel 的專案建立目標。

舉例來說,假設您正在處理專案 my-project/,且想要依附同事的專案 coworkers-project/。您的同事的專案使用 make 進行建構,但您想要依賴其產生的其中一個 .so 檔案。如要執行此操作,請將以下內容新增至 my_project/WORKSPACE

new_local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
    build_file = "coworker.BUILD",
)

build_file 會指定要疊加在現有專案的 BUILD 檔案,例如:

cc_library(
    name = "some-lib",
    srcs = glob(["**"]),
    visibility = ["//visibility:public"],
)

接著,您就可以依附專案的 BUILD 檔案中的 @coworkers_project//:some-lib

視外部套件而定

Maven 構件和存放區

使用規則集 rules_jvm_external 從 Maven 存放區下載構件,並設為 Java 依附元件。

擷取依附元件

根據預設,系統會在 bazel build 期間視需要擷取外部依附元件。如要預先擷取一組特定目標所需的依附元件,請使用 bazel fetch。如要無條件擷取所有外部依附元件,請使用 bazel sync。擷取的存放區會儲存在輸出基底中,因此每個工作區都會擷取。

覆蓋依附元件

建議您盡可能在專案中提供單一版本政策。對於編譯的依附元件,且最終二進位檔要進行編譯,這是必要步驟。不過,如果並非如此,可能會遮蓋依附元件。請考量下列情境:

myproject/WORKSPACE

workspace(name = "myproject")

local_repository(
    name = "A",
    path = "../A",
)
local_repository(
    name = "B",
    path = "../B",
)

A/WORKSPACE

workspace(name = "A")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "...",
)

B/工作區

workspace(name = "B")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)

AB 都依附於 testrunner,但取決於不同版本的 testrunner。沒有原因會導致這些測試執行器無法在 myproject 中和平共存,但因為兩者的名稱相同,所以會彼此發生衝突。如要宣告這兩個依附元件,請更新 myproject/WORKSPACE:

workspace(name = "myproject")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner-v1",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "..."
)
http_archive(
    name = "testrunner-v2",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)
local_repository(
    name = "A",
    path = "../A",
    repo_mapping = {"@testrunner" : "@testrunner-v1"}
)
local_repository(
    name = "B",
    path = "../B",
    repo_mapping = {"@testrunner" : "@testrunner-v2"}
)

這個機制也可以用來連接鑽石。舉例來說,如果 AB 具有相同的依附元件,但名稱不同,即可在 myproject/WORKSPACE 中彙整這些依附元件。

透過指令列覆寫存放區

如要透過指令列以本機存放區覆寫已宣告的存放區,請使用 --override_repository 標記。使用這個標記會變更外部存放區的內容,而無需變更原始碼。

舉例來說,如要將 @foo 覆寫至本機目錄 /path/to/local/foo,請傳遞 --override_repository=foo=/path/to/local/foo 標記。

以下列舉一些用途:

  • 偵錯問題。舉例來說,您可以將 http_archive 存放區覆寫至本機目錄,讓進行變更變得更加輕鬆。
  • 供應商。如果您在無法發出網路呼叫的環境中,請將網路存放區規則覆寫為指向本機目錄。

使用 Proxy

Bazel 會從 HTTPS_PROXYHTTP_PROXY 環境變數中挑選 Proxy 位址,並使用這些位址下載 HTTP/HTTPS 檔案 (如有指定)。

支援 IPv6

在僅限 IPv6 的機器上,Bazel 可以下載依附元件,而沒有任何變更。不過,在雙重堆疊 IPv4/IPv6 機器上,Bazel 遵循與 Java 相同的慣例:如果啟用 IPv4,建議使用 IPv4。在某些情況下,例如 IPv4 網路無法解析/觸及外部位址時,這可能會導致 Network unreachable 例外狀況和建構失敗。在這種情況下,您可以使用 java.net.preferIPv6Addresses=true 系統屬性覆寫 Bazel 的行為,偏好使用 IPv6。詳細說明:

  • 使用 --host_jvm_args=-Djava.net.preferIPv6Addresses=true 啟動選項,例如在 .bazelrc 檔案中新增以下這一行:

    startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true

  • 如果執行的 Java 建構目標也需要一併連上網際網路 (整合測試有時需要),請一併使用 --jvmopt=-Djava.net.preferIPv6Addresses=true 工具旗標,舉例來說,在 .bazelrc 檔案中加入以下這行程式碼:

    build --jvmopt=-Djava.net.preferIPv6Addresses

  • 如果您使用 rules_jvm_external 例如針對依附元件版本解析,請一併新增 -Djava.net.preferIPv6Addresses=trueCOURSIER_OPTS 環境變數,以提供 Coursier 的 JVM 選項

遞移依附元件

Bazel 只會讀取 WORKSPACE 檔案中列出的依附元件。如果您的專案 (A) 依附於另一個專案 (B),而該專案在其 WORKSPACE 檔案中列出第三個專案 (C) 的依附元件,您必須在專案的 WORKSPACE 檔案中同時新增 BC。這項規定可能限制 WORKSPACE 的檔案大小,但會限制一個程式庫在 1.0 版包含 C,而另一個程式庫包含 C 2.0 的可能性。

外部依附元件快取

根據預設,Bazel 只會在外部依附元件的定義變更時重新下載。也會將定義中參照的檔案 (例如修補程式或 BUILD 檔案) 納入考量。

如要強制重新下載,請使用 bazel sync

版面配置

外部依附元件都會下載到輸出基礎external 子目錄下。如果是本機存放區,系統會建立符號連結,而不是建立新的目錄。您可以執行以下指令來查看 external 目錄:

ls $(bazel info output_base)/external

請注意,執行 bazel clean 並不會實際刪除外部目錄。如要移除所有外部構件,請使用 bazel clean --expunge

離線版本

您有時需要或必須以離線方式執行版本。如果是簡單的用途 (例如在飛機上移動),只要使用 bazel fetchbazel sync 預先擷取所需存放區即可。此外,使用 --nofetch 選項即可在建構期間停用擷取其他存放區的功能。

對於真正的離線建構作業,Bazel 支援 --distdir 選項,因此需要檔案是由 bazel 不同的實體執行。每當存放區規則要求 bazel 透過 ctx.downloadctx.download_and_extract 擷取檔案,並提供所需檔案的雜湊總和時,bazel 會針對符合第一個網址基礎名稱的檔案,查看由這個選項指定的目錄,並在雜湊相符時使用該本機副本。

Bazel 本身會使用這項技術,從發行成果離線啟動。方法是在內部 distdir_tar收集所有所需的外部依附元件

不過,bazel 可讓您在存放區規則中執行任意指令,但不知道這些指令是否向網路發出。因此,bazel 無法強制規定建構作業完全離線。因此,要測試建構作業是否可在離線狀態下正常運作,就必須像 bazel 測試的 Bootstrap 測試一樣,封鎖網路的外部封鎖。

最佳做法

存放區規則

存放區規則通常應負責:

  • 偵測系統設定並將其寫入檔案。
  • 尋找系統其他位置的資源。
  • 正在從網址下載資源。
  • 產生 BUILD 檔案或為外部存放區目錄建立符號連結。

請盡量避免使用 repository_ctx.execute。舉例來說,如果您使用的非 Bazel C++ 程式庫有使用 Make 的建構作業,建議您改用 repository_ctx.download(),然後編寫建構該程式庫的 BUILD 檔案,而不是執行 ctx.execute(["make"])

建議使用 http_archive 搭配 git_repositorynew_git_repository。原因如下:

  • Git 存放區規則依附於系統 git(1),HTTP 下載工具則建構在 Bazel 中,且沒有系統依附元件。
  • http_archive 支援將 urls 清單做為鏡像,而 git_repository 僅支援單一 remote
  • http_archive 適用於存放區快取,但不支援 git_repository。詳情請參閱 #5116

不要使用 bind()。如要進一步討論這個問題和替代方案,請參閱「考慮移除繫結」。