En esta página, se analizan los dos sistemas de visibilidad de Bazel: visibilidad de destino y visibilidad de carga.
Ambos tipos de visibilidad ayudan a otros desarrolladores a distinguir entre la API pública de tu biblioteca y sus detalles de implementación, y ayudan a aplicar la estructura a medida que crece tu lugar de trabajo. También puedes usar la visibilidad cuando das de baja una API pública para permitir a los usuarios actuales, a la vez que rechazas a los nuevos.
Visibilidad objetivo
La visibilidad del destino controla quién puede depender de tu objetivo, es decir, quién puede usar la etiqueta de tu objetivo dentro de un atributo como deps
.
Un A
de destino es visible para un B
de destino si están en el mismo paquete o si A
otorga visibilidad al paquete de B
. Por lo tanto, los paquetes son la unidad de detalle para decidir si se permite o no el acceso. Si B
depende de A
pero A
no es visible para B
, cualquier intento de compilación B
fallará durante el análisis.
Ten en cuenta que otorgar visibilidad a un paquete no otorga por sí misma visibilidad para sus subpaquetes. Para obtener más detalles sobre paquetes y subpaquetes, consulta Conceptos y terminología.
Para la creación de prototipos, puedes inhabilitar la aplicación de la visibilidad de destino si estableces la marca --check_visibility=false
. Esto no se debe hacer para el uso de producción en el código enviado.
La forma principal de controlar la visibilidad es con el atributo visibility
en los objetivos de las reglas. En esta sección, se describe el formato de este atributo y cómo determinar la visibilidad de un destino.
Especificaciones de visibilidad
Todos los objetivos de las reglas tienen un atributo visibility
que toma una lista de etiquetas. Cada etiqueta tiene uno de los siguientes formatos. A excepción de la última forma, estos son solo marcadores de posición sintácticos que no corresponden a ningún objetivo real.
"//visibility:public"
: Otorga acceso a todos los paquetes. (No se puede combinar con ninguna otra especificación)."//visibility:private"
: No otorga ningún acceso adicional; solo los destinos de este paquete pueden usar este destino. (No se puede combinar con ninguna otra especificación)."//foo/bar:__pkg__"
: Otorga acceso a//foo/bar
(pero no a sus subpaquetes)."//foo/bar:__subpackages__"
: Otorga acceso a//foo/bar
y a todos sus subpaquetes indirectos y directos."//some_pkg:my_package_group"
: Otorga acceso a todos los paquetes que forman parte de lapackage_group
determinada.- Los grupos de paquetes utilizan una sintaxis diferente para especificar los paquetes. Dentro de un grupo de paquetes, los formularios
"//foo/bar:__pkg__"
y"//foo/bar:__subpackages__"
se reemplazan respectivamente por"//foo/bar"
y"//foo/bar/..."
. Del mismo modo,"//visibility:public"
y"//visibility:private"
son solo"public"
y"private"
.
- Los grupos de paquetes utilizan una sintaxis diferente para especificar los paquetes. Dentro de un grupo de paquetes, los formularios
Por ejemplo, si //some/package:mytarget
tiene su visibility
configurado en [":__subpackages__", "//tests:__pkg__"]
, puede usarlo cualquier destino que forme parte del árbol de fuentes de //some/package/...
y los objetivos definidos en //tests/BUILD
, pero no los definidos en //tests/integration/BUILD
.
Práctica recomendada: Para que varios destinos sean visibles para el mismo conjunto de paquetes, usa package_group
en lugar de repetir la lista en el atributo visibility
de cada destino. Esto aumenta la legibilidad y evita que las listas se desincronizan.
Visibilidad del objetivo de la regla
El nivel de visibilidad de un objetivo de regla es el siguiente:
El valor del atributo
visibility
, si se estableció; o bienEl valor del argumento
default_visibility
de la declaraciónpackage
en el archivoBUILD
del destino, si existe una declaración de este tipo; o bien//visibility:private
.
Práctica recomendada: Evita configurar default_visibility
como público. Puede ser conveniente para el prototipado o para bases de código pequeñas, pero el riesgo de crear destinos públicos aumenta de forma involuntaria a medida que crece la base de código. Es mejor ser explícito acerca de los destinos que forman parte de la interfaz pública de un paquete.
Ejemplo
Archivo //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"],
)
Archivo //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",
],
)
Visibilidad de destino de archivos generada
Un destino de archivo generado tiene la misma visibilidad que el destino de regla que lo genera.
Visibilidad del destino del archivo de origen
Puedes establecer de forma explícita la visibilidad de una orientación de archivo de origen llamando a exports_files
. Cuando no se pasa ningún argumento visibility
a exports_files
, la visibilidad es pública.
No se puede usar exports_files
para anular la visibilidad de un archivo generado.
Para los objetivos de archivo de origen que no aparecen en una llamada a exports_files
, la visibilidad depende del valor de la marca --incompatible_no_implicit_file_export
:
Si la marca está configurada, la visibilidad es privada.
De lo contrario, se aplica el comportamiento heredado: la visibilidad es la misma que el archivo
default_visibility
del archivoBUILD
o privada si no se especifica una visibilidad predeterminada.
Evita depender del comportamiento heredado. Siempre escribe una declaración exports_files
cada vez que un destino del archivo de origen necesite visibilidad no privada.
Práctica recomendada: Cuando sea posible, se recomienda exponer un destino de regla en lugar de un archivo de origen. Por ejemplo, en lugar de llamar a exports_files
en un archivo .java
, une el archivo en un destino java_library
no privado. Por lo general, los objetivos de las reglas solo deben hacer referencia directamente a los archivos de origen que se encuentren en el mismo paquete.
Ejemplo
Archivo //frobber/data/BUILD
:
exports_files(["readme.txt"])
Archivo //frobber/bin/BUILD
:
cc_binary(
name = "my-program",
data = ["//frobber/data:readme.txt"],
)
Visibilidad de la configuración
Históricamente, Bazel no aplicó la visibilidad para los destinos
config_setting
a los que
se hace referencia en las claves de un select()
. Hay dos marcas para quitar este comportamiento heredado:
--incompatible_enforce_config_setting_visibility
habilita la verificación de visibilidad para estos objetivos. Para ayudar con la migración, también hace que cualquierconfig_setting
que no especifique unvisibility
se considere público (independientemente deldefault_visibility
del nivel de paquete).--incompatible_config_setting_private_default_visibility
hace que lasconfig_setting
que no especifiquen unavisibility
respeten eldefault_visibility
del paquete y resguarden la visibilidad privada, como cualquier otro objetivo de regla. Si no se configura--incompatible_enforce_config_setting_visibility
, es una no-op.
Evita depender del comportamiento heredado. Cualquier config_setting
que esté destinado a usarse fuera del paquete actual debe tener un visibility
explícito, si el paquete aún no especifica un default_visibility
adecuado.
Visibilidad del grupo de paquetes
Los objetivos package_group
no tienen un atributo visibility
. Siempre son visibles públicamente.
Visibilidad de dependencias implícitas
Algunas reglas tienen dependencias implícitas: dependencias que no están escritas en un archivo BUILD
, pero son inherentes a todas las instancias de esa regla. Por ejemplo, una regla cc_library
puede crear una dependencia implícita de cada uno de sus objetivos de regla a un destino ejecutable que representa un compilador C++.
Actualmente, para fines de visibilidad, estas dependencias implícitas se tratan como cualquier otra dependencia. Esto significa que el destino del que se depende (como nuestro compilador C++) debe ser visible para cada instancia de la regla. Por lo general, esto significa que el destino debe tener visibilidad pública.
Puedes cambiar este comportamiento si configuras --incompatible_visibility_private_attributes_at_definition
. Cuando está habilitado, el objetivo en cuestión solo debe ser visible para la regla que lo declara una dependencia implícita. Es decir, debe ser visible para el paquete que contiene el archivo .bzl
en el que se define la regla. En nuestro ejemplo, el compilador de C++ podría ser privado, siempre que se encuentre en el mismo paquete que la definición de la regla cc_library
.
Cargar visibilidad
La visibilidad de carga controla si un archivo .bzl
se puede cargar desde otros archivos BUILD
o .bzl
fuera del paquete actual.
De la misma manera que la visibilidad de destino protege el código fuente que está encapsulado por los objetivos, la visibilidad de carga protege la lógica de compilación que está encapsulada por los archivos .bzl
. Por ejemplo, un autor de un archivo BUILD
podría querer factorizar algunas definiciones de objetivos repetitivos en una macro en un archivo .bzl
. Sin la protección de la visibilidad de carga, es posible que la macro la reutilicen otros colaboradores en el mismo lugar de trabajo, por lo que modificar la macro interrumpe las compilaciones de otros equipos.
Ten en cuenta que un archivo .bzl
puede tener o no un destino de archivo de origen correspondiente.
Si es así, no se garantiza que la visibilidad de la carga y la de destino coincidan. Es decir, el mismo archivo BUILD
podría cargar el archivo .bzl
, pero no mostrarlo en el srcs
de un filegroup
, o viceversa. A veces, se pueden generar problemas para las reglas que desean consumir archivos .bzl
como código fuente, por ejemplo, para la generación o prueba de documentación.
Para la creación de prototipos, puedes inhabilitar la aplicación de la visibilidad de carga mediante la configuración de --check_bzl_visibility=false
. Al igual que con --check_visibility=false
, esto no se debe hacer para el código enviado.
La visibilidad de carga está disponible a partir de Bazel 6.0.
Declara la visibilidad de carga
Para configurar la visibilidad de carga de un archivo .bzl
, llama a la función visibility()
desde el archivo.
El argumento para visibility()
es una lista de especificaciones de paquetes, al igual que el atributo packages
de package_group
. Sin embargo, visibility()
no acepta especificaciones de paquetes negativas.
La llamada a visibility()
solo debe ocurrir una vez por archivo, en el nivel superior (no dentro de una función) y, idealmente, después de las instrucciones load()
.
A diferencia de la visibilidad de destino, la visibilidad de carga predeterminada siempre es pública. Los archivos que no llaman a visibility()
siempre se pueden cargar desde cualquier parte del lugar de trabajo. Se recomienda agregar visibility("private")
a la parte superior de cualquier archivo .bzl
nuevo que no esté diseñado específicamente para usarse fuera del paquete.
Ejemplo
# //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
...
Prácticas de visibilidad de carga
En esta sección, se describen las sugerencias para administrar las declaraciones de visibilidad de carga.
Factorizar visibilidades
Cuando varios archivos .bzl
deben tener la misma visibilidad, puede ser útil factorizar sus especificaciones de paquetes en una lista común. Por ejemplo:
# //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)
...
Esto ayuda a evitar el sesgo accidental entre las diversas visibilidades de los diversos archivos .bzl
. También es más legible cuando la lista clients
es grande.
Composición de visibilidades
En ocasiones, es posible que un archivo .bzl
deba ser visible para una lista de entidades permitidas que está compuesta por varias listas de entidades más pequeñas. Esto es análogo a la forma en que una package_group
puede incorporar otras package_group
mediante su atributo includes
.
Supongamos que dará de baja una macro muy utilizada. Quieres que solo sea visible para los usuarios existentes y los paquetes que pertenecen a tu propio equipo. Puedes escribir lo siguiente:
# //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)
Anula la duplicación con grupos de paquetes
A diferencia de la visibilidad de destino, no puedes definir una visibilidad de carga en términos de un package_group
. Si deseas reutilizar la misma lista de entidades permitidas para la visibilidad de destino y la visibilidad de carga, es mejor mover la lista de especificaciones del paquete a un archivo .bzl, en el que ambos tipos de declaraciones pueden hacer referencia a él. Si compilas el ejemplo anterior sobre la factorización de visibilidades, puedes escribir lo siguiente:
# //mylib/BUILD
load(":internal_defs", "clients")
package_group(
name = "my_pkg_grp",
packages = clients,
)
Esto solo funciona si la lista no contiene especificaciones de paquetes negativas.
Protección de símbolos individuales
Cualquier símbolo de Starlark cuyo nombre comience con un guion bajo no se puede cargar desde otro archivo. Esto facilita la creación de símbolos privados, pero no te permite
compartirlos con un conjunto limitado de archivos de confianza. Por otro lado, la visibilidad de carga te permite controlar lo que otros paquetes pueden ver en tu .bzl file
, pero no te permite evitar que se carguen símbolos sin guiones bajos.
Afortunadamente, puedes combinar estas dos funciones para obtener un control más preciso.
# //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
Lint del compilador de bzl-visibility
Existe un Buildifier lint que proporciona una advertencia si los usuarios cargan un archivo desde un directorio llamado internal
o private
, cuando el archivo del usuario no está debajo del directorio superior de ese directorio. Este lint es anterior a la función de visibilidad de carga y no es necesario en lugares de trabajo en los que los archivos .bzl
declaran visibilidad.