Target visibility
Target visibility controls who may depend on your target — that is, who may use your target’s label inside an attribute such asdeps.
A target A is visible to a target B if they are in the same package, or if
A grants visibility to B’s package. Thus, packages are the unit of
granularity for deciding whether or not to allow access. If B depends on A
but A is not visible to B, then any attempt to build B fails during
analysis.
Note that granting visibility to a package does not by itself grant visibility
to its subpackages. For more details on package and subpackages, see
Concepts and terminology.
For prototyping, you can disable target visibility enforcement by setting the
flag --check_visibility=false. This should not be done for production usage in
submitted code.
The primary way to control visibility is with the
visibility attribute on
rule targets. This section describes the format of this attribute, and how to
determine a target’s visibility.
Visibility specifications
All rule targets have avisibility attribute that takes a list of labels. Each
label has one of the following forms. With the exception of the last form, these
are just syntactic placeholders that do not correspond to any actual target.
-
"//visibility:public": Grants access to all packages. (May not be combined with any other specification.) -
"//visibility:private": Does not grant any additional access; only targets in this package can use this target. (May not be combined with any other specification.) -
"//foo/bar:__pkg__": Grants access to//foo/bar(but not its subpackages). -
"//foo/bar:__subpackages__": Grants access//foo/barand all of its direct and indirect subpackages. -
"//some_pkg:my_package_group": Grants access to all of the packages that are part of the givenpackage_group.- Package groups use a
different syntax for
specifying packages. Within a package group, the forms
"//foo/bar:__pkg__"and"//foo/bar:__subpackages__"are respectively replaced by"//foo/bar"and"//foo/bar/...". Likewise,"//visibility:public"and"//visibility:private"are just"public"and"private".
- Package groups use a
different syntax for
specifying packages. Within a package group, the forms
//some/package:mytarget has its visibility set to
[":__subpackages__", "//tests:__pkg__"], then it could be used by any target
that is part of the //some/package/... source tree, as well as targets defined
in //tests/BUILD, but not by targets defined in //tests/integration/BUILD.
Best practice: To make several targets visible to the same set
of packages, use a package_group instead of repeating the list in each
target’s visibility attribute. This increases readability and prevents the
lists from getting out of sync.
Note: The visibility attribute may not specify non-package_group targets.
Doing so triggers a “Label does not refer to a package group” or “Cycle in
dependency graph” error.
Rule target visibility
A rule target’s visibility is:-
The value of its
visibilityattribute, if set; or else -
The value of the
default_visibilityargument of thepackagestatement in the target’sBUILDfile, if such a declaration exists; or else -
//visibility:private.
default_visibility to public. It may be
convenient for prototyping or in small codebases, but the risk of inadvertently
creating public targets increases as the codebase grows. It’s better to be
explicit about which targets are part of a package’s public interface.
Example
File//frobber/bin/BUILD:
//frobber/BUILD:
Generated file target visibility
A generated file target has the same visibility as the rule target that generates it.Source file target visibility
You can explicitly set the visibility of a source file target by callingexports_files. When no visibility
argument is passed to exports_files, it makes the visibility public.
exports_files may not be used to override the visibility of a generated file.
For source file targets that do not appear in a call to exports_files, the
visibility depends on the value of the flag
--incompatible_no_implicit_file_export:
- If the flag is set, the visibility is private.
-
Else, the legacy behavior applies: The visibility is the same as the
BUILDfile’sdefault_visibility, or private if a default visibility is not specified.
exports_files
declaration whenever a source file target needs non-private visibility.
Best practice: When possible, prefer to expose a rule target rather than a
source file. For example, instead of calling exports_files on a .java file,
wrap the file in a non-private java_library target. Generally, rule targets
should only directly reference source files that live in the same package.
Example
File//frobber/data/BUILD:
//frobber/bin/BUILD:
Config setting visibility
Historically, Bazel has not enforced visibility forconfig_setting targets that are
referenced in the keys of a select(). There
are two flags to remove this legacy behavior:
-
--incompatible_enforce_config_setting_visibilityenables visibility checking for these targets. To assist with migration, it also causes anyconfig_settingthat does not specify avisibilityto be considered public (regardless of package-leveldefault_visibility). -
--incompatible_config_setting_private_default_visibilitycausesconfig_settings that do not specify avisibilityto respect the package’sdefault_visibilityand to fallback on private visibility, just like any other rule target. It is a no-op if--incompatible_enforce_config_setting_visibilityis not set.
config_setting that is intended to
be used outside the current package should have an explicit visibility, if the
package does not already specify a suitable default_visibility.
Package group target visibility
package_group targets do not have a visibility attribute. They are always
publicly visible.
Visibility of implicit dependencies
Some rules have implicit dependencies — dependencies that are not spelled out in aBUILD file but are inherent to
every instance of that rule. For example, a cc_library rule might create an
implicit dependency from each of its rule targets to an executable target
representing a C++ compiler.
The visibility of such an implicit dependency is checked with respect to the
package containing the .bzl file in which the rule (or aspect) is defined. In
our example, the C++ compiler could be private so long as it lives in the same
package as the definition of the cc_library rule. As a fallback, if the
implicit dependency is not visible from the definition, it is checked with
respect to the cc_library target.
You can change this behavior by disabling
--incompatible_visibility_private_attributes_at_definition.
When disabled, implicit dependencies are treated like any other dependency.
This means that the target being depended on (such as our C++ compiler) must be
visible to every instance of the rule. In practice this usually means the target
must have public visibility.
If you want to restrict the usage of a rule to certain packages, use
load visibility instead.
Load visibility
Load visibility controls whether a.bzl file may be loaded from other
BUILD or .bzl files.
In the same way that target visibility protects source code that is encapsulated
by targets, load visibility protects build logic that is encapsulated by .bzl
files. For instance, a BUILD file author might wish to factor some repetitive
target definitions into a macro in a .bzl file. Without the protection of load
visibility, they might find their macro reused by other collaborators in the
same workspace, so that modifying the macro breaks other teams’ builds.
Note that a .bzl file may or may not have a corresponding source file target.
If it does, there is no guarantee that the load visibility and the target
visibility coincide. That is, the same BUILD file might be able to load the
.bzl file but not list it in the srcs of a filegroup,
or vice versa. This can sometimes cause problems for rules that wish to consume
.bzl files as source code, such as for documentation generation or testing.
For prototyping, you may disable load visibility enforcement by setting
--check_bzl_visibility=false. As with --check_visibility=false, this should
not be done for submitted code.
Load visibility is available as of Bazel 6.0.
Declaring load visibility
To set the load visibility of a.bzl file, call the
visibility() function from within the file.
The argument to visibility() is a list of package specifications, just like
the packages attribute of
package_group. However, visibility() does not accept negative package
specifications.
The call to visibility() must only occur once per file, at the top level (not
inside a function), and ideally immediately following the load() statements.
Unlike target visibility, the default load visibility is always public. Files
that do not call visibility() are always loadable from anywhere in the
workspace. It is a good idea to add visibility("private") to the top of any
new .bzl file that is not specifically intended for use outside the package.
Example
Load visibility practices
This section describes tips for managing load visibility declarations.Factoring visibilities
When multiple.bzl files should have the same visibility, it can be helpful to
factor their package specifications into a common list. For example:
.bzl files’
visibilities. It also is more readable when the clients list is large.
Composing visibilities
Sometimes a.bzl file might need to be visible to an allowlist that is
composed of multiple smaller allowlists. This is analogous to how a
package_group can incorporate other package_groups via its
includes attribute.
Suppose you are deprecating a widely used macro. You want it to be visible only
to existing users and to the packages owned by your own team. You might write:
Deduplicating with package groups
Unlike target visibility, you cannot define a load visibility in terms of apackage_group. If you want to reuse the same allowlist for both target
visibility and load visibility, it’s best to move the list of package
specifications into a .bzl file, where both kinds of declarations may refer to
it. Building off the example in Factoring visibilities
above, you might write:
Protecting individual symbols
Any Starlark symbol whose name begins with an underscore cannot be loaded from another file. This makes it easy to create private symbols, but does not allow you to share these symbols with a limited set of trusted files. On the other hand, load visibility gives you control over what other packages may see your.bzl file, but does not allow you to prevent any non-underscored symbol from
being loaded.
Luckily, you can combine these two features to get fine-grained control.
bzl-visibility Buildifier lint
There is a Buildifier lint that provides a warning if users load a file from a directory namedinternal
or private, when the user’s file is not itself underneath the parent of that
directory. This lint predates the load visibility feature and is unnecessary in
workspaces where .bzl files declare visibilities.