Bazel 使用一种很有主见的方案来存储构建工件, 常常会给开发者造成混淆本页将介绍 工作。
Bazel output_base
根据设计,bazel build
命令绝不会向项目的源写入文件
目录(或其某个子目录)。Bazel 会使用特定于用户的
Parallel 目录来存储所有输出(称为 user_output_root
),
的默认值为:
~/.cache/bazel/_bazel_$USER
(在 Linux 上)。%HOME%\_bazel_%USERNAME%
(在 Windows 上)。
对于运行 bazel
的每个工作区目录,都会有一个名为
output_base
是在 user_output_root
下创建的,如下所示:
${user_output_root}/<WORKSPACE_HASH>`
其中 <WORKSPACE_HASH>
是一个长十六进制哈希(根据
目录的绝对路径)。
由于此路径完全不可预测,因此命令
在 Bazel 项目中使用时,bazel info output_base
将输出此消息。
例如:
$ mkdir -p /tmp/project1 && cd /tmp/project1 && touch WORKSPACE.bazel
$ bazel info output_base
Starting local Bazel server and connecting to it...
/usr/local/google/home/digit/.cache/bazel/_bazel_digit/6c7b78994da78136b5cb6b7607361ad3
$ mkdir -p /tmp/project2 && cd /tmp/project2 && touch WORKSPACE.bazel
$ bazel info output_base
Starting local Bazel server and connecting to it...
/usr/local/google/home/digit/.cache/bazel/_bazel_digit/c37b9d68308ee5abe2f781dd38b733b9
$ mkdir -p /tmp/not-a-project && cd /tmp/not-a-project
$ bazel info output_base
WARNING: Invoking Bazel in batch mode since it is not invoked from within a workspace (below a directory having a WORKSPACE file).
ERROR: The 'info' command is only supported from within a workspace (below a directory having a WORKSPACE file).
See documentation at https://bazel.build/concepts/build-ref#workspace
此架构非常灵活,但并不完美:
Pro:同一台计算机上的多个用户可以共享同一项只读文件 项目目录中。
Pro:同一用户的多个项目目录将始终使用 独立的输出路径。
Con:可直接从命令行查看生成的文件,甚至可以 图形探索器很困难。
Con:移除项目目录(例如包含
rm -rf .../my-project
) 不会移除其输出(造成严重浪费)。Con:移动项目目录(例如使用
mv my-project my-project2
) 不会重复使用之前的output_base
内容(并将旧内容保留 ,现在无法访问)。Con:
user_output_root
的默认位置,因此output_base
通常与 项目。这可能会对性能 / 磁盘造成意想不到的后果 。
调用 bazel clean
即可从当前 output_base
中移除构建输出。
此操作必须在移除源项目目录之前完成。
在实践中,使用user_output_root
从未适当清理的过时 Bazel 项目构建工件。
更糟糕的是,尝试直接手动移除 user_output_root
可能不起作用,
因为默认情况下,Bazel 会创建只读的构建制品,这会阻止
运行命令(例如 rm -rf ~/.cache/bazel
)!
Bazel output_base
内容:
实际上,以下内容存储在 output_base
下:
外部代码库的工作区目录:
它们对应于外部项目依赖项。这些通常不是 项目的源代码树,但是从网络上下载或生成的 以编程方式
其内容存储在
${output_base}/external/<repository_name>
下, 其中external
部分经过硬编码,<repository_name>
用于匹配 外部代码库的规范名称。构建工件:
运行
bazel build
生成的文件。这些文件存储在以下位置:${output_base}/execroot/<workspace_name>/bazel-out/<config_dir>/bin/
其中:
execroot
、bazel-out
和bin
部分是硬编码的, 已更改。对于在项目自己的
BUILD.bazel
文件中定义的目标,<workspace_name>
默认为__main__
,除非是在项目的WORKSPACE.bazel
文件,其中包含以下指令:
workspace( name = "my_project", ) ``` - For targets defined in external repositories, `<workspace_name>` matches the repository's canonical name. - The `<config_dir>` value is a name derived from the build configuration used to configure the target that generated the build artifact. This allows rebuilding the same target in different ways, each time using a different `<config_dir>` value. Note: The `<config_dir>` value is **generally unpredictable**. More on this [here][bazel-config-dirs]
测试结果:
调用
bazel test
时生成的日志文件,存储在${output_base}/execroot/<workspace_name>/bazel-out/<config_dir>/testlogs/
。内部缓存和配置文件:
供远程构建和远程缓存功能使用。要忽略这类文件, 开发者。
Bazel execroot
目录:
execroot
用于运行生成 build 工件、
但具体如何实现取决于特定操作是否启用了沙盒。
在 Linux 上,默认情况下,系统会为所有操作启用沙盒。 Windows 不支持沙盒(从 Bazel 7 开始)。
Bazel 操作可以有意停用沙盒,只需使用 定义中包含
no-sandbox
标记。您可以通过如下选项全局停用沙盒: 在调用
bazel
时返回--spawn_strategy=local
。
未采用沙盒机制时:
停用沙盒后,为以下项目生成工件的所有构建操作:
指定的工作区会将输出文件放在
${output_base}/execroot/<workspace_name>
。
该操作的 因此将是相对于该节点的
Bazel 可确保命令所用输入源的符号链接 在 execroot 下创建。
例如,要编译 //src/foo/foo.cc
文件的操作,
(包含与 //src/foo/foo.h
对应的 #include "foo.h"
)可以
如下所示:
gcc -c -o bazel-out/k8-fastbuild/bin/src/foo/foo.o src/foo/foo.cc -Isrc/foo
这样做的原因是:
在运行该命令之前,Bazel 会创建符号链接, 指向
$PROJECT/src
的${output_base}/execroot/__main__/src
, 以使src/foo/foo.cc
和src/foo/foo.h
解析为$PROJECT/src/foo/foo.cc
和$PROJECT/src/foo/foo.h
。该位置
bazel-out/k8-fastbuild/bin/src/foo/foo.o
是最后一个位置 输出路径(适用于通过在foo.cc
此命令使用的 build 配置。
使用沙盒时:
启用沙盒后,Bazel 会为每个命令创建一个临时
目录(例如 ${output_base}/sandbox/linux-sandbox/<random-number>
)
并创建一个符号链接树来模拟其下的 execroot 布局,但是
只针对它知道的输入。在这种情况下,如下所示:
输入
${sandbox}/execroot/__main__/src/foo/foo.cc
的符号链接 和${sandbox}/execroot/__main__/src/foo/foo.h
指向 分别为$PROJECT/src/foo/foo.cc
和$PROJECT/src/foo/foo.h
。在
${sandbox}/execroot/__main__
下运行相同的命令。 而非${output_base}/execroot/__main__
。命令完成后,从位于
${sandbox}/execroot/__main__/bazel-out/k8-fastbuild/bin/src/foo/foo.o
到达最终地点:${output_base}/execroot/__main__/bazel-out/k8-fastbuild/bin/src/foo/foo.o
。最后是沙盒目录及其所有内容。这也 表示将忽略未声明的输出。