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_repository
、git_repository
或 http_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(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 = "..."
)
依赖项 A
和 B
都依赖于 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"}
)
此机制也可用于联接菱形。例如,如果 A
和 B
具有相同的依赖项,但调用它的名称不同,则可以将这些依赖项联接到 myproject/WORKSPACE 中。
从命令行替换代码库
如需从命令行使用本地代码库替换声明的代码库,请使用 --override_repository
标志。使用此标志可以更改外部代码库的内容,而不会更改源代码。
例如,如需将 @foo
替换到本地目录 /path/to/local/foo
,请传递 --override_repository=foo=/path/to/local/foo
标志。
部分用例包括:
- 调试问题。例如,您可以将
http_archive
代码库替换为本地目录,以便在其中更轻松地进行更改。 - Selling。如果处于无法进行网络调用的环境中,请将基于网络的代码库规则替换为指向本地目录。
使用代理
Bazel 将从 HTTPS_PROXY
和 HTTP_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 build 目标也需要连接到互联网(集成测试有时也需要连接到互联网),也请使用
--jvmopt=-Djava.net.preferIPv6Addresses=true
工具标志,例如在.bazelrc
文件中添加以下代码行:build --jvmopt=-Djava.net.preferIPv6Addresses
如果您使用 rules_jvm_external 进行依赖项版本解析,还应将
-Djava.net.preferIPv6Addresses=true
添加到COURSIER_OPTS
环境变量中,以为 Coursier 提供 JVM 选项
传递依赖项
Bazel 仅读取 WORKSPACE
文件中列出的依赖项。如果您的项目 (A
) 依赖于另一个项目 (B
),而该项目的 WORKSPACE
文件中列出了第三个项目 (C
) 的依赖项,您必须将 B
和 C
都添加到项目的 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
。
离线 build
有时需要或必须离线运行构建。对于简单的用例(例如飞机出行),使用 bazel fetch
或 bazel sync
预提取所需代码库就已足够;此外,使用 --nofetch
选项,可以在构建期间停用对更多代码库的提取。
对于真正的离线 build(所需文件由 bazel 以外的实体提供),bazel 支持选项 --distdir
。每当代码库规则要求 bazel 通过 ctx.download
或 ctx.download_and_extract
提取文件并提供所需文件的哈希总和时,bazel 都会首先在该选项指定的目录中查找与提供的第一个网址基名匹配的文件,如果哈希匹配,则使用该本地副本。
Bazel 本身使用此技术从分发工件离线引导。为此,它会在内部 distdir_tar
中收集所需的所有外部依赖项。
但是,bazel 允许执行代码库规则中的任意命令,而无需知道这些命令是否向网络调用。因此,bazel 无法强制让构建完全离线。因此,要测试 build 能否在离线状态下正常运行,需要从外部屏蔽网络,就像 bazel 在引导测试中所做的那样。
最佳实践
代码库规则
代码库规则通常应负责:
- 检测系统设置并将其写入文件。
- 在系统上其他位置查找资源。
- 从网址下载资源。
- 生成 BUILD 文件或将其符号链接到外部代码库目录中。
尽可能避免使用 repository_ctx.execute
。例如,在使用具有使用 Make 的构建的非 Bazel C++ 库时,最好使用 repository_ctx.download()
,然后编写用于构建它的 BUILD 文件,而不是运行 ctx.execute(["make"])
。
首选 http_archive
,而非 git_repository
和 new_git_repository
。原因如下:
- Git 代码库规则依赖于系统
git(1)
,而 HTTP 下载程序内置于 Bazel 中,并且没有系统依赖项。 http_archive
支持将urls
列表作为镜像,git_repository
仅支持单个remote
。http_archive
适用于代码库缓存,但不适用于git_repository
。如需了解详情,请参阅 #5116。
请勿使用 bind()
。请参阅“考虑移除绑定”,详细了解其问题和替代方案。