使用外部依附元件

回報問題 查看來源 Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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"],
)

然後,您就可以從專案的 @coworkers_project//:some-lib 檔案依附 BUILD 檔案。

依附外部套件

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

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 存放區覆寫至本機目錄,以便更輕鬆地進行變更。
  • Vendoring。如果您處於無法發出網路呼叫的環境,請覆寫以網路為準的存放區規則,改為指向本機目錄。

使用 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。詳細說明:

遞移依附元件

Bazel 只會讀取 WORKSPACE 檔案中列出的依附元件。如果專案 (A) 依附於另一個專案 (B),而該專案的 WORKSPACE 檔案列出第三個專案 (C) 的依附元件,您就必須將 BC 都加入專案的 WORKSPACE 檔案。這項規定可能會導致 WORKSPACE 檔案大小暴增,但可減少一個程式庫包含 C 1.0 版,而另一個程式庫包含 C 2.0 版的情況。

外部依附元件的快取

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

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

版面配置

所有外部依附元件都會下載到輸出基準的子目錄 external 底下。如果是本機存放區,系統會在該處建立符號連結,而不是建立新目錄。執行下列指令即可查看 external 目錄:

ls $(bazel info output_base)/external

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

離線建構

有時您會希望或需要離線執行建構作業。對於簡單的用途 (例如搭乘飛機旅行),使用 bazel fetchbazel syncprefetching所需存放區就已足夠;此外,使用 --nofetch 選項時,可以在建構期間停用進一步擷取存放區。

如要進行真正的離線建構,也就是由 Bazel 以外的實體提供所需檔案,Bazel 支援 --distdir 選項。每當存放區規則要求 Bazel 透過 ctx.downloadctx.download_and_extract 擷取檔案,並提供所需檔案的雜湊總和時,Bazel 會先在該選項指定的目錄中,尋找與第一個網址的 basename 相符的檔案,並在雜湊相符時使用該本機副本。

Bazel 本身會使用這項技術,從發行版構件啟動離線作業。方法是收集所有必要的外部依附元件,並放在內部 distdir_tar 中。

不過,Bazel 允許在存放區規則中執行任意指令,而不必瞭解這些指令是否會呼叫網路。因此,Bazel 無法強制建構作業完全離線。因此,如要離線測試建構作業是否正常運作,必須像 Bazel 在其啟動程序測試中一樣,從外部封鎖網路。

最佳做法

存放區規則

存放區規則通常應負責:

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

請盡量避免使用 repository_ctx.execute。舉例來說,使用透過 Make 建構的非 Bazel C++ 程式庫時,最好使用 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()。如要深入瞭解相關問題和替代方案,請參閱「考慮移除繫結」。