En esta página, se abarcan los dos sistemas de visibilidad de Bazel: la visibilidad de destino y la 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 espacio de trabajo. También puedes usar la visibilidad cuando quitas una API pública para permitir que los usuarios actuales la usen, pero no los nuevos.
Visibilidad de destino
La visibilidad de destino controla quién puede depender de tu destino, es decir, quién puede
usar la etiqueta de tu destino dentro de un atributo como deps.
Un destino A es visible para un destino B 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
granularidad para decidir si se permite o no el acceso. Si B depende de A
pero A no es visible para B, cualquier intento de compilar B falla durante
el análisis.
Ten en cuenta que otorgar visibilidad a un paquete no otorga visibilidad a sus subpaquetes. Para obtener más detalles sobre los 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
visibility atributo en
los destinos de 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 destinos de reglas tienen un atributo visibility que toma una lista de etiquetas. Cada
etiqueta tiene una de las siguientes formas. Con la excepción de la última forma, estos
son solo marcadores de posición sintácticos que no corresponden a ningún destino 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/bary a todos sus subpaquetes directos e indirectos."//some_pkg:my_package_group": Otorga acceso a todos los paquetes que forman parte delpackage_group.- Los grupos de paquetes usan una
sintaxis diferente para
especificar paquetes. Dentro de un grupo de paquetes, las formas
"//foo/bar:__pkg__"y"//foo/bar:__subpackages__"se reemplazan por"//foo/bar"y"//foo/bar/...", respectivamente. Del mismo modo,"//visibility:public"y"//visibility:private"son solo"public"y"private".
- Los grupos de paquetes usan una
sintaxis diferente para
especificar paquetes. Dentro de un grupo de paquetes, las formas
Por ejemplo, si //some/package:mytarget tiene su visibility establecida en
[":__subpackages__", "//tests:__pkg__"], cualquier destino
que forme parte del árbol de origen //some/package/... podría usarlo, así como los destinos definidos
en //tests/BUILD, pero no los destinos definidos en //tests/integration/BUILD.
Práctica recomendada: Para que varios destinos sean visibles para el mismo conjunto
de paquetes, usa un 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 desincronizen.
Visibilidad de destino de reglas
La visibilidad de destino de una regla es la siguiente:
El valor de su atributo
visibility, si está configurado; de lo contrario,El valor del
default_visibilityargumento de la instrucciónpackageen el archivoBUILDdel destino, si existe tal declaración; de lo contrario,//visibility:private.
Práctica recomendada: Evita establecer default_visibility como público. Puede ser
conveniente para la creación de prototipos o en bases de código pequeñas, pero el riesgo de crear destinos públicos de forma involuntaria
aumenta a medida que crece la base de código. Es mejor ser
explícito sobre qué destinos 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 generados
Un destino de archivo generado tiene la misma visibilidad que el destino de regla que lo genera.
Visibilidad de destino de archivos fuente
Puedes establecer de forma explícita la visibilidad de un destino de archivo fuente llamando a
exports_files. Cuando no se pasa ningún visibility
argumento a exports_files, se hace pública la visibilidad.
exports_files no se puede usar para anular la visibilidad de un archivo generado.
Para los destinos de archivos fuente que no aparecen en una llamada a exports_files, la
visibilidad depende del valor de la marca
--incompatible_no_implicit_file_export:
Si se establece la marca, la visibilidad es privada.
De lo contrario, se aplica el comportamiento heredado: la visibilidad es la misma que la
BUILDdel archivodefault_visibility, o privada si no se especifica una visibilidad predeterminada.
Evita depender del comportamiento heredado. Siempre escribe una exports_files
declaración cuando un destino de archivo fuente necesite visibilidad no privada.
Práctica recomendada: Cuando sea posible, prefiere exponer un destino de regla en lugar de un
archivo fuente. Por ejemplo, en lugar de llamar a exports_files en un archivo .java,
envuelve el archivo en un destino java_library no privado. Por lo general, los destinos de reglas
solo deben hacer referencia directa a los archivos fuente que se encuentran 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
config_setting destinos a los que se
hace referencia en las claves de un select(). Existen dos marcas para quitar este comportamiento heredado:
--incompatible_enforce_config_setting_visibilityhabilita la verificación de visibilidad para estos destinos. Para ayudar con la migración, también hace que cualquierconfig_settingque no especifique unavisibilityse considere pública (independientemente de ladefault_visibilitya nivel del paquete).--incompatible_config_setting_private_default_visibilityhace que losconfig_settingque no especifican unavisibilityrespeten ladefault_visibilitydel paquete y recurran a la visibilidad privada, al igual que cualquier otro destino de regla. Es una operación no operativa si--incompatible_enforce_config_setting_visibilityno se establece.
Evita depender del comportamiento heredado. Cualquier config_setting que se deba
usar fuera del paquete actual debe tener una visibility explícita, si el
paquete aún no especifica una default_visibility adecuada.
Visibilidad de destino de grupos de paquetes
Los destinos package_group no tienen un atributo visibility. Siempre son
visibles de forma pública.
Visibilidad de dependencias implícitas
Algunas reglas tienen dependencias implícitas, es decir,
dependencias que no se especifican en un archivo BUILD, pero que son inherentes a
cada instancia de esa regla. Por ejemplo, una regla cc_library podría crear una
dependencia implícita de cada uno de sus destinos de reglas a un destino ejecutable
que represente un compilador de 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 de C++) debe ser visible para cada instancia de la regla. En la práctica, esto suele significar que el destino debe tener visibilidad pública.
Puedes cambiar este comportamiento si estableces
--incompatible_visibility_private_attributes_at_definition. Cuando está habilitado, el
destino en cuestión solo debe ser visible para la regla que lo declara como una dependencia implícita. Es decir, debe ser visible para el paquete que contiene el .bzl
archivo 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
cc_library regla.
Visibilidad de carga
La visibilidad de carga controla si se puede cargar un archivo .bzl desde otros
BUILD o archivos .bzl.
De la misma manera que la visibilidad de destino protege el código fuente que está encapsulado
por los destinos, la visibilidad de carga protege la lógica de compilación que está encapsulada por los archivos .bzl. Por ejemplo, es posible que el autor de un archivo BUILD desee factorizar algunas definiciones de destino repetitivas
en una macro en un archivo .bzl. Sin la protección de la visibilidad de carga, es posible que otros colaboradores reutilicen su macro en el mismo espacio de trabajo, de modo que la modificación de la macro interrumpa las compilaciones de otros equipos.
Ten en cuenta que un archivo .bzl puede tener o no un destino de archivo fuente correspondiente.
Si lo hace, no hay garantía de que la visibilidad de carga y la visibilidad de destino
coincidan. Es decir, es posible que el mismo archivo BUILD pueda cargar el
.bzl pero no lo incluya en el srcs de un filegroup,
o viceversa. Esto a veces puede causar problemas para las reglas que desean consumir
.bzl archivos como código fuente, como para la generación de documentación o las pruebas.
Para la creación de prototipos, puedes inhabilitar la aplicación de la visibilidad de carga si estableces
--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.
Cómo declarar la visibilidad de carga
Para establecer la visibilidad de carga de un .bzl archivo, llama a la
visibility() función desde el archivo.
El argumento para visibility() es una lista de especificaciones de paquetes, al igual que
el packages atributo 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, de manera ideal, inmediatamente 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 lugar del
espacio de trabajo. Es una buena idea agregar visibility("private") a la parte superior de cualquier
archivo .bzl nuevo que no esté destinado 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 sugerencias para administrar las declaraciones de visibilidad de carga.
Factorización de 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 la distorsión accidental entre las visibilidades de los distintos archivos .bzl'
. También es más legible cuando la lista clients es grande.
Composición de visibilidades
A veces, es posible que un archivo .bzl deba ser visible para una lista de entidades permitidas que se
compone de varias listas de entidades permitidas más pequeñas. Esto es análogo a la forma en que un
package_group puede incorporar otros package_group a través de su
includes atributo.
Supongamos que quitas una macro de uso generalizado. Quieres que solo sea visible solo para los usuarios existentes y para los paquetes que son propiedad de tu equipo. Podrías 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)
Deduplicació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 de paquetes
a un archivo .bzl, donde ambos tipos de declaraciones pueden hacer referencia a
ella. A partir del ejemplo de Factorización de visibilidades
anterior, podrías 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
No se puede cargar ningún símbolo de Starlark cuyo nombre comience con un guion bajo desde
otro archivo. Esto facilita la creación de símbolos privados, pero no te permite
compartir estos símbolos con un conjunto limitado de archivos de confianza. Por otro lado, la visibilidad de carga te permite controlar qué otros paquetes pueden ver tu
.bzl file, pero no te permite evitar que se cargue ningún símbolo sin guion bajo.
Por suerte, puedes combinar estas dos funciones para obtener un control detallado.
# //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 de Buildifier bzl-visibility
Hay un lint de Buildifier
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 elemento superior de ese
directorio. Este lint es anterior a la función de visibilidad de carga y no es necesario en
los espacios de trabajo en los que .bzl archivos declaran visibilidades.