Nesta página, descrevemos os dois sistemas de visibilidade do Bazel: visibilidade do destino e visibilidade de carga.
Os dois tipos de visibilidade ajudam outros desenvolvedores a distinguir entre a API pública da sua biblioteca e os detalhes de implementação dela, além de ajudar a aplicar a estrutura à medida que seu espaço de trabalho cresce. Também é possível usar a visibilidade ao suspender o uso de uma API pública para permitir os usuários atuais e negar novos.
Visibilidade desejada
A visibilidade do destino controla quem pode depender do destino, ou seja, quem pode
usar o rótulo do destino dentro de um atributo como deps
.
Um A
de destino ficará visível para um B
de destino se estiverem no mesmo pacote ou se
A
conceder 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
vai falhar durante
a análise.
Observe que conceder visibilidade a um pacote não concede visibilidade aos subpacotes. Para mais detalhes sobre pacotes e subpacotes, consulte Conceitos e terminologia.
Para prototipagem, é possível desativar a aplicação da visibilidade desejada 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
nas
metas de regra. Esta seção descreve o formato desse atributo e como
determinar a visibilidade de um destino.
Especificações de visibilidade
Todas as segmentações de regras têm um atributo visibility
que usa 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 combinada com qualquer outra especificação."//visibility:private"
: não concede nenhum acesso extra. Somente os destinos neste pacote podem usar esse destino. Não pode ser combinado com outra especificação."//foo/bar:__pkg__"
: concede acesso a//foo/bar
, mas não aos subpacotes."//foo/bar:__subpackages__"
: concede acesso a//foo/bar
e a todos os subpacotes diretos e indiretos."//some_pkg:my_package_group"
: concede acesso a todos os pacotes que fazem parte dopackage_group
especificado.- Os grupos de pacotes usam uma
sintaxe diferente para
especificar pacotes. Dentro de 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. Dentro de 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 //some/package/...
, bem como por 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 impede que as listas fiquem fora de sincronia.
Visibilidade do destino da regra
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 destinos públicos
inadvertidamente 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
Para definir explicitamente a visibilidade de um destino de arquivo de origem, chame exports_files
. Quando nenhum argumento visibility
é transmitido para exports_files
, a visibilidade se torna pública.
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á particular.
Caso contrário, o comportamento legado será aplicado: a visibilidade será a mesma que a
default_visibility
do arquivoBUILD
ou particular se uma visibilidade padrão não for especificada.
Evite depender do comportamento legado. Sempre escreva uma declaração exports_files
sempre que o destino de um 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 os 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 configuração
Historicamente, o Bazel não aplicou a visibilidade para destinos
config_setting
que são
referenciados nas chaves de uma select()
. Há
duas sinalizações para remover esse comportamento legado:
--incompatible_enforce_config_setting_visibility
ativa a verificação de visibilidade para esses destinos. Para auxiliar 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 a substituição na visibilidade particular, assim como qualquer outro destino de regra. Um ambiente autônomo será executado se--incompatible_enforce_config_setting_visibility
não estiver definido.
Evite depender do comportamento legado. Qualquer config_setting
que se destine
para ser usado fora do pacote atual precisa ter um visibility
explícito, caso o
pacote ainda não especifique um default_visibility
adequado.
Visibilidade do destino do grupo de pacotes
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 expressas 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 dependente (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.
É possível mudar esse comportamento definindo
--incompatible_visibility_private_attributes_at_definition
. Quando ativado, o destino em questão precisa estar visível apenas para a regra que o declara uma dependência implícita. Ou seja, ela 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
.
Carregar visibilidade
A 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 do carregamento protege a lógica do build encapsulada por arquivos
.bzl
. Por exemplo, um autor de arquivo BUILD
pode querer fatorar algumas definições de
destino repetitivos em uma macro em um arquivo .bzl
. Sem a proteção da visibilidade
de carregamento, a macro pode ser reutilizada por outros colaboradores no
mesmo espaço de trabalho. Assim, modificar a macro interrompe os builds de outras equipes.
Observe que um arquivo .bzl
pode ou não ter um destino de arquivo de origem correspondente.
Caso isso aconteça, não há garantia de que a visibilidade da carga e a visibilidade da segmentação coincidem. Ou seja, o mesmo arquivo BUILD
pode carregar o
arquivo .bzl
, mas não o listar no srcs
de uma filegroup
ou vice-versa. Isso às vezes 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, você pode desativar a aplicação da visibilidade de carga configurando
--check_bzl_visibility=false
. Assim como acontece com --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 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()
precisa ocorrer apenas uma vez por arquivo, no nível superior (não dentro de uma função) e, de preferência, logo após as instruções load()
.
Ao contrário da visibilidade desejada, a visibilidade de carregamento padrão é sempre pública. Os arquivos que não chamam visibility()
sempre podem ser carregados de qualquer lugar do espaço de trabalho. É recomendável adicionar visibility("private")
à parte superior 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
...
Carregar práticas de visibilidade
Nesta seção, descrevemos 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
fatorar as especificações do pacote 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 o desvio acidental entre as visibilidades dos vários
arquivos .bzl
. Ele também é mais legível quando a lista de clients
é grande.
Como criar visibilidades
Às vezes, um arquivo .bzl
pode precisar estar visível para uma lista de permissões
composta de várias listas menores. Isso é análogo a como uma
package_group
pode incorporar outros package_group
s com o
atributo includes
.
Suponha que você esteja descontinuando uma macro amplamente usada. Você quer que ele fique visível apenas para os usuários atuais e para os pacotes 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)
Eliminar duplicação com grupos de pacotes
Ao contrário da visibilidade desejada, não é possível definir uma visibilidade de carregamento em termos de
package_group
. Se você quiser reutilizar a mesma lista de permissões para visibilidade
de destino e de carregamento, é melhor mover a lista de especificações de
pacote para um arquivo .bzl, em que ambos os tipos de declarações possam se
referir. Com base no exemplo em Visibilidades de fatoração
acima, é possível escrever:
# //mylib/BUILD
load(":internal_defs", "clients")
package_group(
name = "my_pkg_grp",
packages = clients,
)
Isso só vai funcionar se a lista não tiver especificações de pacote negativas.
Proteção de símbolos individuais
Qualquer símbolo Starlark cujo nome comece com 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ê compartilhe esses símbolos 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 que você impeça o carregamento de
símbolos não sublinhados.
Felizmente, você pode 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
Bzl-visibilidade do lint do Buildifier
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 antecede o recurso de visibilidade de carregamento e é desnecessário em
espaços de trabalho em que arquivos .bzl
declaram visibilidade.