Esta página aborda os dois sistemas de visibilidade do Bazel: visibilidade de destino e visibilidade de 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.
Segmentação por visibilidade
A visibilidade do alvo controla quem pode depender do seu alvo, ou seja, quem pode
usar o rótulo do seu alvo dentro de 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
. Portanto, os pacotes são a unidade de
granularidade para decidir se o acesso é 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.
Conceder visibilidade a um pacote não concede visibilidade a seus subpacotes. Para mais detalhes sobre pacotes e subpacotes, consulte Conceitos e terminologia.
Para prototipar, é possível desativar a aplicação de visibilidade do destino definindo a
flag --check_visibility=false
. Isso não deve ser feito para uso em produção no
código enviado.
A principal forma de controlar a visibilidade é com o atributo
visibility
em
alvos 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 recebe uma lista de identificadores. Cada rótulo tem um dos formatos a seguir. Com exceção do último formulário, 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 adicional. Somente os destinos deste pacote podem usar esse destino. Não pode ser combinada 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 substituídos respectivamente 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árias metas visíveis para o mesmo conjunto
de pacotes, use um package_group
em vez de repetir a lista no atributo visibility
de cada
meta. Isso aumenta a legibilidade e evita
que as listas fiquem dessincronizadas.
Visibilidade da segmentação de regras
A visibilidade de um destino de regra é:
O valor do atributo
visibility
, se definido; ouO 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úblico. Isso pode ser
conveniente para prototipagem ou em pequenas bases de código, mas o risco de criar
alvos públicos acidentalmente aumenta à medida que a base de código cresce. É 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 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 fica pública.
O exports_files
não pode ser usado para substituir a visibilidade de um arquivo gerado.
Para destinos de arquivos 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 estiver definida, a visibilidade será privada.
Caso contrário, o comportamento legado será aplicado: a visibilidade será a mesma do
default_visibility
do arquivoBUILD
ou privada se uma visibilidade padrão não for especificada.
Evite depender do 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
,
encapsule 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"],
)
Configuração de visibilidade
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 essas metas. Para ajudar na migração, qualquerconfig_setting
que não especifique umvisibility
também é considerado público (independentemente dodefault_visibility
no nível do pacote).--incompatible_config_setting_private_default_visibility
faz com queconfig_setting
s que não especificam umvisibility
respeitem adefault_visibility
do pacote e usem a visibilidade privada como fallback, assim como qualquer outro destino de regra. Não vai funcionar se--incompatible_enforce_config_setting_visibility
não estiver definido.
Evite confiar no comportamento legado. Qualquer config_setting
que tenha a intenção de
ser usado fora do pacote atual precisa ter um visibility
explícito, se o
pacote 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
. Eles estão sempre
visíveis publicamente.
Visibilidade de dependências implícitas
Algumas regras têm dependências implícitas,
que não são especificadas em um arquivo BUILD
, mas são inerentes a
cada instância 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++.
A visibilidade dessa dependência implícita é verificada em relação ao
pacote que contém o arquivo .bzl
em que a regra (ou aspecto) está definido. Em
nosso exemplo, o compilador C++ pode ser particular, desde que esteja no mesmo
pacote que a definição da regra cc_library
. Como alternativa, se a
dependência implícita não estiver visível na definição, ela será verificada em
relação ao destino cc_library
.
É possível mudar esse comportamento desativando
--incompatible_visibility_private_attributes_at_definition
.
Quando desativadas, as dependências implícitas são tratadas como qualquer outra dependência.
Isso significa que o destino em que se depende (como nosso compilador C++) precisa estar
visível para todas as instâncias da regra. Na prática, isso geralmente significa que o alvo
precisa ter visibilidade pública.
Se você quiser restringir o uso de uma regra a determinados pacotes, use a visibilidade de carregamento.
Carregar visibilidade
A visibilidade de carregamento controla se um arquivo .bzl
pode ser carregado de outros
arquivos BUILD
ou .bzl
fora do pacote atual.
Da mesma forma que a visibilidade de 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 da macro vai corromper os 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 de carregamento e a visibilidade
de 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 prototipar, é 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 carga está disponível no Bazel 6.0.
Como declarar a visibilidade do carregamento
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 de pacote
negativas.
A chamada para visibility()
só pode ocorrer 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 da meta, a visibilidade de carregamento padrão é sempre pública. Os arquivos
que não chamam visibility()
podem ser carregados de qualquer lugar no
espaço de trabalho. É recomendável adicionar visibility("private")
ao início de qualquer
novo arquivo .bzl
que não seja destinado especificamente ao 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
considerar as especificações de 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 distorções acidentais entre as várias visibilidades
dos arquivos .bzl
. Ele também é mais legível quando a lista clients
é grande.
Como compor visibilidades
Às vezes, um arquivo .bzl
precisa estar visível para uma lista de permissões
composta por várias listas de permissões menores. Isso é análogo a como um
package_group
pode incorporar outros package_group
s pelo atributo
includes
.
Suponha que você esteja descontinuando uma macro amplamente usada. Você quer que ele seja visível apenas para os usuários e pacotes que pertencem à 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)
Como eliminar a 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 a visibilidade
de destino e de carregamento, é melhor mover a lista de especificações
do pacote para um arquivo .bzl, em que ambos os tipos de declarações possam se referir a
ele. Com base no exemplo em Como fatorar visibilidades
acima, você pode escrever:
# //mylib/BUILD
load(":internal_defs", "clients")
package_group(
name = "my_pkg_grp",
packages = clients,
)
Isso só funciona se a lista não contiver especificações de pacote negativas.
Proteção de símbolos individuais
Qualquer símbolo do Starlark cujo nome comece com um sublinhado não pode ser carregado de
outro arquivo. Isso facilita a criação de símbolos particulares, mas não permite
que você compartilhe esses símbolos com um conjunto limitado de arquivos confiáveis. Por outro
lado, a visibilidade de carregamento permite controlar o que outros pacotes podem ver do
.bzl file
, mas não permite impedir que qualquer símbolo sem sublinhado seja
carregado.
Felizmente, é possível combinar esses dois recursos para ter 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
Buildifier lint bzl-visibility
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 carga e é desnecessário em
espaços de trabalho em que os arquivos .bzl
declaram visibilidades.