Nesta página, abordamos os dois sistemas de visibilidade do Bazel: visibilidade do destino e visibilidade do carregamento.
Os dois tipos de visibilidade ajudam outros desenvolvedores a distinguir entre a API pública da biblioteca e os detalhes de implementação, além de ajudar a aplicar a estrutura conforme o espaço de trabalho aumenta. Também é possível usar a visibilidade ao suspender o uso de uma API pública para permitir os usuários atuais, mas negar os novos.
Visibilidade desejada
A visibilidade do destino controla quem pode depender do objetivo, ou seja, quem pode
usar o rótulo do destino em um atributo, como deps
.
Um A
de destino fica visível para um B
de destino se eles estão no mesmo pacote ou se
A
concede visibilidade ao pacote do B
. Assim, os pacotes são a unidade de
granularidade para decidir se o acesso será permitido ou não. Se B
depender de A
,
mas A
não estiver visível para B
, qualquer tentativa de criar B
falhará durante a
análise.
Observe que conceder visibilidade a um pacote não concede visibilidade por si só aos subpacotes. Para ver mais detalhes sobre pacotes e subpacotes, consulte Conceitos e terminologia.
Para prototipagem, é possível desativar a aplicação da visibilidade do destino definindo a
flag --check_visibility=false
. Isso não deve ser feito para uso de produção no código enviado.
A principal maneira de controlar a visibilidade é com o
atributo visibility
nos
destinos de regras. Esta seção descreve o formato desse atributo e como
determinar a visibilidade de um destino.
Especificações de visibilidade
Todos os destinos de regras têm um atributo visibility
que aceita uma lista de rótulos. Cada rótulo tem um dos formatos a seguir. Com exceção da última forma, esses
são apenas marcadores de posição sintáticos que não correspondem a nenhum destino real.
"//visibility:public"
: concede acesso a todos os pacotes. Não pode ser combinado com nenhuma outra especificação."//visibility:private"
: não concede nenhum acesso extra. Somente destinos neste pacote podem usá-lo. Não pode ser combinado com nenhuma outra especificação."//foo/bar:__pkg__"
: concede acesso a//foo/bar
, mas não aos subpacotes dele."//foo/bar:__subpackages__"
: concede acesso//foo/bar
e todos os subpacotes diretos e indiretos."//some_pkg:my_package_group"
: concede acesso a todos os pacotes que fazem parte dopackage_group
fornecido.- Os grupos de pacotes usam uma
sintaxe diferente para
especificar pacotes. Em um grupo de pacotes, os formulários
"//foo/bar:__pkg__"
e"//foo/bar:__subpackages__"
são respectivamente substituídos por"//foo/bar"
e"//foo/bar/..."
. Da mesma forma,"//visibility:public"
e"//visibility:private"
são apenas"public"
e"private"
.
- Os grupos de pacotes usam uma
sintaxe diferente para
especificar pacotes. Em um grupo de pacotes, os formulários
Por exemplo, se //some/package:mytarget
tiver o visibility
definido como
[":__subpackages__", "//tests:__pkg__"]
, ele poderá ser usado por qualquer destino
que faça parte da árvore de origem de //some/package/...
, bem como destinos definidos
em //tests/BUILD
, mas não por destinos definidos em //tests/integration/BUILD
.
Prática recomendada:para tornar vários destinos visíveis para o mesmo conjunto
de pacotes, use um package_group
em vez de repetir a lista no
atributo visibility
de cada destino. Isso aumenta a legibilidade e evita
que as listas fiquem dessincronizadas.
Visibilidade da meta da regra
A visibilidade da meta de uma regra é:
O valor do atributo
visibility
, se definido. Caso contrário,O valor do argumento
default_visibility
da instruçãopackage
no arquivoBUILD
do destino, se essa declaração existir; ou//visibility:private
.
Prática recomendada:evite definir default_visibility
como pública. Isso pode ser conveniente para prototipagem ou em pequenas bases de código, mas o risco de criar destinos públicos acidentalmente aumenta à medida que a base do código aumenta. É melhor ser
explícito sobre quais destinos fazem parte da interface pública de um pacote.
Exemplo
Arquivo //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"],
)
Arquivo //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",
],
)
Visibilidade do destino do arquivo gerado
Um destino de arquivo gerado tem a mesma visibilidade que o destino da regra que o gera.
Visibilidade do destino do arquivo de origem
É possível definir explicitamente a visibilidade de um destino de arquivo de origem chamando
exports_files
. Quando nenhum argumento visibility
é transmitido para exports_files
, a visibilidade é definida como pública.
exports_files
não pode ser usado para substituir a visibilidade de um arquivo gerado.
Para destinos de arquivo de origem que não aparecem em uma chamada para exports_files
, a
visibilidade depende do valor da flag
--incompatible_no_implicit_file_export
:
Se a flag for definida, a visibilidade será particular.
Caso contrário, o comportamento legado será aplicado: a visibilidade será igual ao
default_visibility
do arquivoBUILD
ou particular se uma visibilidade padrão não for especificada.
Evite confiar no comportamento legado. Sempre grave uma declaração exports_files
sempre que um destino de arquivo de origem precisar de visibilidade não particular.
Prática recomendada:quando possível, prefira expor um destino de regra em vez de um
arquivo de origem. Por exemplo, em vez de chamar exports_files
em um arquivo .java
, una o arquivo em um destino java_library
não particular. Geralmente, os destinos de regra
só precisam referenciar diretamente arquivos de origem que residem no mesmo pacote.
Exemplo
Arquivo //frobber/data/BUILD
:
exports_files(["readme.txt"])
Arquivo //frobber/bin/BUILD
:
cc_binary(
name = "my-program",
data = ["//frobber/data:readme.txt"],
)
Visibilidade da definição de configuração
Historicamente, o Bazel não aplica a visibilidade para
destinos config_setting
que são
referenciados nas chaves de um select()
(links em inglês). Há duas sinalizações para remover esse comportamento legado:
--incompatible_enforce_config_setting_visibility
ativa a verificação de visibilidade para esses destinos. Para ajudar na migração, ele também faz com que qualquerconfig_setting
que não especifique umvisibility
seja considerado público, independente dodefault_visibility
no nível do pacote.--incompatible_config_setting_private_default_visibility
faz com queconfig_setting
s que não especificam umvisibility
respeitem odefault_visibility
do pacote e façam fallback na visibilidade particular, assim como qualquer outro destino de regra. Será um ambiente autônomo se--incompatible_enforce_config_setting_visibility
não estiver definido.
Evite confiar no comportamento legado. Qualquer config_setting
destinado ao
uso fora do pacote atual precisará ter um visibility
explícito, se o
pacote ainda não especificar um default_visibility
adequado.
Visibilidade do destino do grupo de pacotes
Os destinos package_group
não têm um atributo visibility
. Elas estão sempre
visíveis publicamente.
Visibilidade de dependências implícitas
Algumas regras têm dependências implícitas,
dependências que não são escritas em um arquivo BUILD
, mas são inerentes a
todas as instâncias dessa regra. Por exemplo, uma regra cc_library
pode criar uma
dependência implícita de cada um dos destinos de regra para um destino executável
que representa um compilador C++.
Atualmente, para fins de visibilidade, essas dependências implícitas são tratadas como qualquer outra dependência. Isso significa que o destino de que depende (como nosso compilador C++) precisa estar visível para todas as instâncias da regra. Na prática, isso geralmente significa que o destino precisa ter visibilidade pública.
Para mudar esse comportamento, defina
--incompatible_visibility_private_attributes_at_definition
. Quando ativado, o
destino em questão só precisa ficar visível para a regra que o declara como uma dependência
implícita. Ou seja, ele precisa estar visível para o pacote que contém o arquivo .bzl
em que a regra está definida. No nosso exemplo, o compilador C++ pode ser
particular, desde que esteja no mesmo pacote que a definição da
regra cc_library
.
Visibilidade da carga
Visibilidade de carregamento: controla se um arquivo .bzl
pode ser carregado de outros
arquivos BUILD
ou .bzl
.
Da mesma forma que a visibilidade do destino protege o código-fonte encapsulado
por destinos, a visibilidade de carga protege a lógica de build encapsulada por arquivos
.bzl
. Por exemplo, um autor de arquivo BUILD
pode considerar algumas definições
de destino repetitivas em uma macro em um arquivo .bzl
. Sem a proteção da visibilidade de
carga, eles podem encontrar a macro reutilizada por outros colaboradores no
mesmo espaço de trabalho, de modo que a modificação dela corrompa as builds de outras equipes.
Um arquivo .bzl
pode ou não ter um destino de arquivo de origem correspondente.
Se isso acontecer, não há garantia de que a visibilidade da carga e a visibilidade do destino coincidam. Ou seja, o mesmo arquivo BUILD
pode carregar o
arquivo .bzl
, mas não o listar no srcs
de um filegroup
ou vice-versa. Às vezes, isso pode causar problemas para regras que querem consumir
arquivos .bzl
como código-fonte, por exemplo, para geração ou teste de documentação.
Para prototipagem, é possível desativar a aplicação da visibilidade de carga definindo
--check_bzl_visibility=false
. Assim como em --check_visibility=false
, isso
não deve ser feito para o código enviado.
A visibilidade de carregamento está disponível a partir do Bazel 6.0.
Como declarar a visibilidade da carga
Para definir a visibilidade de carregamento de um arquivo .bzl
, chame a função visibility()
no arquivo.
O argumento para visibility()
é uma lista de especificações de pacote, assim como o
atributo packages
de
package_group
. No entanto, visibility()
não aceita especificações negativas
de pacote.
A chamada para visibility()
precisa ocorrer apenas uma vez por arquivo, no nível superior (não dentro de uma função) e, de preferência, imediatamente após as instruções load()
.
Ao contrário da visibilidade do destino, a visibilidade de carga padrão é sempre pública. Os arquivos
que não chamam visibility()
são sempre carregáveis de qualquer lugar no
espaço de trabalho. É recomendável adicionar visibility("private")
à parte de cima de qualquer
novo arquivo .bzl
que não se destina especificamente para uso fora do pacote.
Exemplo
# //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áticas de visibilidade de carregamento
Esta seção descreve dicas para gerenciar declarações de visibilidade de carga.
Visibilidades de fatoração
Quando vários arquivos .bzl
precisam ter a mesma visibilidade, pode ser útil
incluir as especificações dos pacotes em uma lista comum. Exemplo:
# //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)
...
Isso ajuda a evitar desvios acidentais entre as visibilidades dos vários arquivos .bzl
. Também é mais legível quando a lista de clients
é grande.
Visibilidades de composição
Às vezes, um arquivo .bzl
pode precisar estar visível para uma lista de permissões composta por várias listas menores. Isso é análogo à forma como uma
package_group
pode incorporar outros package_group
s usando o atributo
includes
.
Suponha que você esteja descontinuando uma macro amplamente utilizada. Ele precisa ficar visível apenas para os usuários atuais e para os pacotes de propriedade da sua equipe. Você pode escrever:
# //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)
Eliminação de duplicação com grupos de pacotes
Ao contrário da visibilidade do destino, não é possível definir uma visibilidade de carga em termos de um
package_group
. Se você quiser reutilizar a mesma lista de permissões para visibilidade
do destino e de carga, é melhor mover a lista de especificações
do pacote para um arquivo .bzl, em que os dois tipos de declarações podem se referir a
ela. Com base no exemplo em Visibilidades de fatoração acima, escreva:
# //mylib/BUILD
load(":internal_defs", "clients")
package_group(
name = "my_pkg_grp",
packages = clients,
)
Isso só funciona se a lista não tiver especificações de pacote negativas.
Proteção de símbolos individuais
Qualquer símbolo Starlark com um nome iniciado por um sublinhado não pode ser carregado de
outro arquivo. Isso facilita a criação de símbolos privados, mas não permite que você os compartilhe com um conjunto limitado de arquivos confiáveis. Por outro lado, a visibilidade de carregamento oferece controle sobre o que outros pacotes podem ver seu
.bzl file
, mas não permite evitar que qualquer símbolo não sublinhado seja
carregado.
Felizmente, você pode combinar esses dois recursos para obter um controle refinado.
# //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 do Buildifier bzl-visibilidade
Há um lint do Buildifier
que fornece um aviso quando os usuários carregam um arquivo de um diretório chamado internal
ou private
, quando o arquivo do usuário não está abaixo do pai desse
diretório. Esse lint é anterior ao recurso de visibilidade de carregamento e é desnecessário em
espaços de trabalho em que os arquivos .bzl
declaram visibilidades.