使用外部依赖项

报告问题 查看源代码 每夜版 · 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"],
)

然后,您就可以在项目的 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

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。如果您所处的环境无法进行网络调用,请替换基于网络的代码库规则,使其指向本地目录。

使用代理

Bazel 将从 HTTPS_PROXYHTTP_PROXY 环境变量中获取代理地址,并使用这些地址下载 HTTP/HTTPS 文件(如果已指定)。

对 IPv6 的支持

在仅限 IPv6 的机器上,Bazel 将能够下载依赖项,而无需进行任何更改。不过,在双栈 IPv4/IPv6 机器上,Bazel 遵循与 Java 相同的惯例:如果启用了 IPv4,则优先使用 IPv4。在某些情况下(例如,当 IPv4 网络无法解析/访问外部地址时),这可能会导致 Network unreachable 异常和 build 失败。在这些情况下,您可以使用 java.net.preferIPv6Addresses=true 系统属性替换 Bazel 的行为,以优先使用 IPv6。具体而言:

传递依赖项

Bazel 只会读取 WORKSPACE 文件中列出的依赖项。如果您的项目 (A) 依赖于另一个项目 (B),而后者在其 WORKSPACE 文件中列出了对第三个项目 (C) 的依赖项,那么您必须将 BC 都添加到项目的 WORKSPACE 文件中。此要求可能会使 WORKSPACE 文件大小大幅增加,但可减少一个库包含 1.0 版的 C 而另一个库包含 2.0 版的 C 的可能性。

外部依赖项的缓存

默认情况下,仅当外部依赖项的定义发生更改时,Bazel 才会重新下载这些依赖项。bazel 还会考虑对定义中引用的文件(例如补丁或 BUILD 文件)所做的更改。

如需强制重新下载,请使用 bazel sync

布局

所有外部依赖项都会下载到输出库中子目录 external 下的某个目录。如果是本地代码库,则会在其中创建一个符号链接,而不是创建新目录。 您可以通过运行以下命令查看 external 目录:

ls $(bazel info output_base)/external

请注意,运行 bazel clean 实际上不会删除外部目录。如需移除所有外部制品,请使用 bazel clean --expunge

离线 build

有时,我们希望或需要以离线方式运行 build。对于简单用例(例如乘坐飞机旅行),使用 bazel fetchbazel sync prefetching所需代码库就足够了;此外,使用 --nofetch 选项可以在 build 期间停用进一步的代码库提取。

对于真正的离线 build,如果所需文件的提供方不是 Bazel,则 Bazel 支持 --distdir 选项。每当代码库规则要求 Bazel 通过 ctx.downloadctx.download_and_extract 获取文件并提供所需文件的哈希值时,Bazel 会先在相应选项指定的目录中查找与提供的第一个网址的基本名称匹配的文件,如果哈希值匹配,则使用该本地副本。

Bazel 本身也使用此技术从分发工件离线启动。 为此,它会在内部 distdir_tar收集所有必需的外部依赖项

不过,bazel 允许在代码库规则中执行任意命令,而无需知道它们是否会调用网络。因此,bazel 无法强制执行完全离线构建。因此,测试 build 是否在离线状态下正常运行需要像 bazel 在其引导测试中所做的那样,从外部阻止网络。

最佳做法

代码库规则

存储库规则通常应负责:

  • 检测系统设置并将其写入文件。
  • 在系统中的其他位置查找资源。
  • 从网址下载资源。
  • 将 BUILD 文件生成或符号链接到外部代码库目录中。

请尽可能避免使用 repository_ctx.execute。例如,当使用通过 Make 进行 build 的非 Bazel C++ 库时,最好使用 repository_ctx.download(),然后编写一个用于 build 该库的 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()。如需详细了解其问题和替代方案,请参阅“考虑移除 bind”。