公开范围

<ph type="x-smartling-placeholder"></ph> 报告问题 查看来源 敬上 每晚 · 7.3。 · 7.2。 · 7.1。 · 7.0。 · 6.5

本页面介绍了 Bazel 的两种可见性系统: 目标可见性加载可见性

这两种可见性有助于其他开发者区分 库的公共 API 及其实现详情,并帮助强制执行结构 帮助您快速上手弃用公开属性时,您也可以使用可见性 允许现有用户同时拒绝新用户的 API。

目标可见性

目标可见性可控制哪些人可能取决于您的目标,也就是说,谁可能取决于您的目标。 在属性(例如 deps)中使用目标的标签。

如果目标 A 位于同一软件包中,或者目标 B 中处于可见状态, A 可授予对 B 的软件包的可见性。因此,软件包是 用于决定是否授予访问权限的精细程度。如果 B 依赖于 AAB 不可见,则在测试期间,任何尝试构建 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:mytargetvisibility 设置为 [":__subpackages__", "//tests:__pkg__"],那么它可能会被任何目标使用 (属于 //some/package/... 源代码树的一部分)以及已定义的目标, 中的 //tests/BUILD,而不是 //tests/integration/BUILD 中定义的目标。

最佳做法:让多个目标对同一组可见 请使用 package_group,而不是在每个软件包中重复列出 目标的 visibility 属性。这可以提高可读性, 名单的同步情况。

规则目标可见性

规则目标的可见性为:

  1. visibility 属性的值(如果已设置);或者其他

  2. default_visibility package 语句的 target 的 BUILD 文件(如果存在此类声明);或者其他

  3. //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 用于为这些目标启用可见性检查。为协助迁移 还会导致任何未指定 visibilityconfig_setting 被视为公开项(无论软件包级 default_visibility 如何)。

  • --incompatible_config_setting_private_default_visibility 会使未指定 visibilityconfig_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 文件,但未将其列在 filegroupsrcs 中, 反之亦然。这有时会导致 .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 文件声明可见性的工作区。