本页面介绍了 Bazel 的两种可见性系统: 目标可见性和加载可见性。
这两种可见性有助于其他开发者区分 库的公共 API 及其实现详情,并帮助强制执行结构 帮助您快速上手弃用公开属性时,您也可以使用可见性 允许现有用户同时拒绝新用户的 API。
目标可见性
目标可见性可控制哪些人可能取决于您的目标,也就是说,谁可能取决于您的目标。
在属性(例如 deps
)中使用目标的标签。
如果目标 A
位于同一软件包中,或者目标 B
中处于可见状态,
A
可授予对 B
的软件包的可见性。因此,软件包是
用于决定是否授予访问权限的精细程度。如果 B
依赖于 A
但 A
对 B
不可见,则在测试期间,任何尝试构建 B
的尝试都会失败
分析。
请注意,向软件包授予可见性权限本身并不意味着授予可见性 子软件包如需详细了解软件包和子软件包,请参阅 概念和术语。
对于原型设计,您可以通过设置
标志 --check_visibility=false
。不应在生产使用时执行此操作
已提交代码。
控制可见性的主要方法是使用
visibility
属性已开启
规则目标。本部分介绍了此属性的格式,以及如何
决定目标的可见性。
可见性规范
所有规则目标都有一个采用标签列表的 visibility
属性。每个
采用以下形式之一。除了最后一种表单外
只是与任何实际目标均不对应的语法占位符。
"//visibility:public"
:授予对所有软件包的访问权限。(不可合并 与任何其他规范保持一致。)"//visibility:private"
:不授予任何其他访问权限;仅限目标 可以使用此目标(不能与其他任何 specification.)"//foo/bar:__pkg__"
:授予对//foo/bar
的访问权限(但不向其授予对它的访问权限) 子软件包)。"//foo/bar:__subpackages__"
:可授予对“//foo/bar
”及其所有访问权限 直接和间接子文件包。"//some_pkg:my_package_group"
:授予对 属于指定package_group
。- 软件包组使用
不同的语法
指定软件包在软件包组内,表单
"//foo/bar:__pkg__"
和"//foo/bar:__subpackages__"
分别是 替换为"//foo/bar"
和"//foo/bar/..."
。同样,"//visibility:public"
和"//visibility:private"
只是"public"
和"private"
。
- 软件包组使用
不同的语法
指定软件包在软件包组内,表单
例如,如果 //some/package:mytarget
的 visibility
设置为
[":__subpackages__", "//tests:__pkg__"]
,那么它可能会被任何目标使用
(属于 //some/package/...
源代码树的一部分)以及已定义的目标,
中的 //tests/BUILD
,而不是 //tests/integration/BUILD
中定义的目标。
最佳做法:让多个目标对同一组可见
请使用 package_group
,而不是在每个软件包中重复列出
目标的 visibility
属性。这可以提高可读性,
名单的同步情况。
规则目标可见性
规则目标的可见性为:
其
visibility
属性的值(如果已设置);或者其他default_visibility
package
语句的 target 的BUILD
文件(如果存在此类声明);或者其他//visibility:private
。
最佳实践:避免将 default_visibility
设为公开。它可能会
很方便进行原型设计或在小型代码库中运行,但无意中
公开目标的数量会随着代码库规模的扩大而增加。最好采用
明确指出哪些目标属于软件包的公共接口。
示例
文件 //frobber/bin/BUILD
:
# This target is visible to everyone
cc_binary(
name = "executable",
visibility = ["//visibility:public"],
deps = [":library"],
)
# This target is visible only to targets declared in the same package
cc_library(
name = "library",
# No visibility -- defaults to private since no
# package(default_visibility = ...) was used.
)
# This target is visible to targets in package //object and //noun
cc_library(
name = "subject",
visibility = [
"//noun:__pkg__",
"//object:__pkg__",
],
)
# See package group "//frobber:friends" (below) for who can
# access this target.
cc_library(
name = "thingy",
visibility = ["//frobber:friends"],
)
文件 //frobber/BUILD
:
# This is the package group declaration to which target
# //frobber/bin:thingy refers.
#
# Our friends are packages //frobber, //fribber and any
# subpackage of //fribber.
package_group(
name = "friends",
packages = [
"//fribber/...",
"//frobber",
],
)
生成的文件目标的公开范围
生成的文件目标的公开范围与 生成它。
源文件目标可见性
您可以通过调用
exports_files
。如果没有 visibility
参数传递给 exports_files
,从而将可见性设为公开。
exports_files
不能用于覆盖所生成文件的公开范围。
对于未出现在 exports_files
调用中的源文件目标,
可见性取决于标志的值
--incompatible_no_implicit_file_export
:
如果设置了此标志,则公开范围为不公开状态。
否则,旧版行为适用:可见性与
BUILD
文件的default_visibility
;如果默认公开范围为“不公开”,则设为“不公开” 未指定。
避免依赖于旧版行为。始终写入 exports_files
声明。
最佳做法:如果可能,最好公开规则目标,
源文件。例如,不要对 .java
文件调用 exports_files
,
将文件封装在非私密 java_library
目标中。一般来说,规则目标
只能直接引用位于同一软件包中的源文件。
示例
文件 //frobber/data/BUILD
:
exports_files(["readme.txt"])
文件 //frobber/bin/BUILD
:
cc_binary(
name = "my-program",
data = ["//frobber/data:readme.txt"],
)
配置设置可见性
以前,Bazel 未针对
config_setting
个目标
select()
的键中引用。那里
两个标记可消除这种旧版行为:
--incompatible_enforce_config_setting_visibility
用于为这些目标启用可见性检查。为协助迁移 还会导致任何未指定visibility
的config_setting
被视为公开项(无论软件包级default_visibility
如何)。--incompatible_config_setting_private_default_visibility
会使未指定visibility
的config_setting
遵循 软件包的default_visibility
并在不公开可见性模式下进行回退, 与任何其他规则目标一样如果存在以下情况,则为空操作: 未设置--incompatible_enforce_config_setting_visibility
。
避免依赖于旧版行为。任何旨在config_setting
在当前软件包之外使用的应用应具有显式 visibility
,如果
软件包尚未指定合适的 default_visibility
。
软件包组目标可见性
package_group
目标没有 visibility
属性。它们始终
公开显示。
隐式依赖项的可见性
有些规则具有隐式依赖项:
依赖项,这些依赖项未在 BUILD
文件中明确列出,但这些依赖项是
该规则的每个实例例如,cc_library
规则可能会创建
从每个规则目标到可执行目标的隐式依赖项
代表 C++ 编译器。
系统将根据以下标准检查此类隐式依赖项的可见性:
软件包,其中包含定义了规则(或切面)的 .bzl
文件。在
在我们的示例中,只要 C++ 编译器与
定义为 cc_library
规则的定义。作为后备方法,如果
隐式依赖项未显示在定义中,可使用
相对于 cc_library
目标而言。
要更改这一行为,您可以停用
--incompatible_visibility_private_attributes_at_definition
。
停用后,系统会像处理任何其他依赖项一样处理隐式依赖项。
这意味着所依赖的目标(例如我们的 C++ 编译器)必须是
对规则的每个实例可见在实践中,这通常意味着
必须公开。
如果您想将规则的使用范围限制为特定软件包,请使用 加载可见性。
加载可见性
加载可见性控制能否从其他来源加载 .bzl
文件
BUILD
或 .bzl
文件。
目标可见性可以保护被封装的源代码
根据目标,加载可见性可保护由 .bzl
封装的构建逻辑
文件。例如,BUILD
文件作者可能希望将一些重复的
将目标定义转换为 .bzl
文件中的宏。未保护负载
可能发现其他协作者在重复使用宏,
同一个工作区,因此修改宏会破坏其他团队的build。
请注意,.bzl
文件不一定有对应的源文件目标。
如果包含,则无法保证加载可见性和目标
也就是说,同一个 BUILD
文件或许能够加载
.bzl
文件,但未将其列在 filegroup
的 srcs
中,
反之亦然。这有时会导致
.bzl
文件作为源代码,例如用于生成或测试文档。
对于原型设计,您可以通过设置
--check_bzl_visibility=false
。与 --check_visibility=false
一样,这应该
请勿对提交的代码执行此类操作。
从 Bazel 6.0 开始,可以使用加载可见性。
声明加载可见性
如需设置 .bzl
文件的加载可见性,请调用
visibility()
函数。
visibility()
的参数是一个软件包规范列表,就像
packages
属性
package_group
。但是,visibility()
不接受排除包
。
对 visibility()
的调用只能在顶层(而不是
),最好紧跟在 load()
语句之后。
与目标可见性不同,默认的加载可见性始终是公开的。文件极客
不调用 visibility()
的实例始终可从
工作区。最好将 visibility("private")
添加到任何
新的 .bzl
文件(并非专门用于软件包外部使用)。
示例
# //mylib/internal_defs.bzl
# Available to subpackages and to mylib's tests.
visibility(["//mylib/...", "//tests/mylib/..."])
def helper(...):
...
# //mylib/rules.bzl
load(":internal_defs.bzl", "helper")
# Set visibility explicitly, even though public is the default.
# Note the [] can be omitted when there's only one entry.
visibility("public")
myrule = rule(
...
)
# //someclient/BUILD
load("//mylib:rules.bzl", "myrule") # ok
load("//mylib:internal_defs.bzl", "helper") # error
...
加载可见性做法
本部分介绍了有关管理加载可见性声明的提示。
考虑公开范围
如果多个 .bzl
文件应具有相同的公开范围,这很有帮助
将其软件包规格分解为一个通用列表。例如:
# //mylib/internal_defs.bzl
visibility("private")
clients = [
"//foo",
"//bar/baz/...",
...
]
# //mylib/feature_A.bzl
load(":internal_defs.bzl", "clients")
visibility(clients)
...
# //mylib/feature_B.bzl
load(":internal_defs.bzl", "clients")
visibility(clients)
...
这有助于防止各种 .bzl
文件的
公开范围。此外,当 clients
列表较大时,也更便于阅读。
撰写显示设置
有时,一个 .bzl
文件可能需要对满足以下条件的许可名单可见:
由多个较小的许可名单组成这类似于
package_group
可以通过其package_group
includes
属性。
假设您要弃用一个广泛使用的宏。你希望它只对你可见 现有用户以及您自己的团队拥有的软件包。您可能会这样写:
# //mylib/macros.bzl
load(":internal_defs.bzl", "our_packages")
load("//some_big_client:defs.bzl", "their_remaining_uses)
# List concatenation. Duplicates are fine.
visibility(our_packages + their_remaining_uses)
使用软件包组进行重复数据删除
与目标可见性不同,您无法根据目标可见性定义加载可见性,
package_group
。如果您想为这两个目标重复使用同一许可名单
可见性和加载可见性,最好将软件包列表
转换为 .bzl 文件,其中这两种声明都可能引用
。以可见度因式分解中的示例为基础
您可以编写:
# //mylib/BUILD
load(":internal_defs", "clients")
package_group(
name = "my_pkg_grp",
packages = clients,
)
仅当列表不包含任何排除包时,此方法才有效 。
保护单个符号
无法从名称以下划线开头的 Starlark 符号中加载
另一个文件。这样便于创建私有符号,但不允许创建
可与数量有限的一组可信文件共享这些符号。另一个
加载可见性可让您控制其他软件包可能会看到您的
.bzl file
,但不允许防止任何非下划线符号
加载中。
幸运的是,您可以将这两项功能结合起来进行精细控制。
# //mylib/internal_defs.bzl
# Can't be public, because internal_helper shouldn't be exposed to the world.
visibility("private")
# Can't be underscore-prefixed, because this is
# needed by other .bzl files in mylib.
def internal_helper(...):
...
def public_util(...):
...
# //mylib/defs.bzl
load(":internal_defs", "internal_helper", _public_util="public_util")
visibility("public")
# internal_helper, as a loaded symbol, is available for use in this file but
# can't be imported by clients who load this file.
...
# Re-export public_util from this file by assigning it to a global variable.
# We needed to import it under a different name ("_public_util") in order for
# this assignment to be legal.
public_util = _public_util
bzl-visibility 构建器 lint
有一个 Buildifier lint
在用户从名为 internal
的目录加载文件时发出警告
或 private
,但前提是用户的文件本身不在该文件的父级之下
目录。此 lint 早于加载可见性功能,在
.bzl
文件声明可见性的工作区。