Esta página aborda os dois sistemas de visibilidade do Bazel: visibilidade de destino e visibilidade de carregamento.
Ambos os tipos de visibilidade ajudam outros desenvolvedores a distinguir entre a API pública da sua biblioteca e os detalhes de implementação, além de ajudar a impor a estrutura à medida que o espaço de trabalho cresce. Também é possível usar a visibilidade ao descontinuar uma API pública para permitir usuários atuais, mas negar novos.
Visibilidade de destino
A visibilidade de destino controla quem pode depender do seu destino, ou seja, quem pode usar o rótulo do destino em um atributo como deps.
Um destino A é visível para um destino B se eles estiverem no mesmo pacote ou se A conceder visibilidade ao pacote de 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.
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 de 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
visibility atributo em
destinos de regra. Esta seção descreve o formato desse atributo e como determinar a visibilidade de um destino.
Especificações de visibilidade
Todos os destinos de regra têm um atributo visibility que recebe uma lista de rótulos. Cada rótulo tem uma das seguintes formas. 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 adicional. Somente os destinos nesse pacote podem usar esse destino. Não pode ser combinado com nenhuma outra especificação."//foo/bar:__pkg__": concede acesso a//foo/bar, mas não aos seus subpacotes."//foo/bar:__subpackages__": concede acesso a//foo/bare a todos os seus subpacotes diretos e indiretos."//some_pkg:my_package_group": concede acesso a todos os pacotes que fazem parte dopackage_groupespecificado.- Os grupos de pacotes usam uma
sintaxe diferente para
especificar pacotes. Em um grupo de pacotes, as formas
"//foo/bar:__pkg__"e"//foo/bar:__subpackages__"são substituídas por"//foo/bar"e"//foo/bar/...", respectivamente. 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, as formas
Por exemplo, se //some/package:mytarget tiver a visibility definida como
[":__subpackages__", "//tests:__pkg__"], ela poderá ser usada 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 evita que as listas fiquem dessincronizadas.
Visibilidade do destino da regra
A visibilidade de um destino de regra é:
O valor do atributo
visibility, se definido; ouO valor do
default_visibilityargumento da instruçãopackageno arquivoBUILDdo destino, se houver uma declaração desse tipo; ou//visibility:private.
Prática recomendada:evite definir default_visibility como público. Isso pode ser conveniente para prototipagem ou em bases de código pequenas, mas o risco de criar destinos públicos inadvertidamente 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 destino do arquivo gerado
Um destino de arquivo gerado tem a mesma visibilidade do 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, ele torna a visibilidade 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á privada.
Caso contrário, o comportamento legado será aplicado: a visibilidade é a mesma que a
default_visibilitydo arquivoBUILDou privada se uma visibilidade padrão não for especificada.
Evite depender do comportamento legado. Sempre escreva uma declaração exports_files quando um destino de arquivo de origem precisar de visibilidade não privada.
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 privado. Geralmente, os destinos de regra só devem referenciar diretamente arquivos de origem que estejam 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
config_setting destinos que são
referenciados nas chaves de um select(). Há duas flags para remover esse comportamento legado:
--incompatible_enforce_config_setting_visibilityativa a verificação de visibilidade para esses destinos. Para ajudar na migração, ela também faz com que qualquerconfig_settingque não especifique umavisibilityseja considerada pública (independentemente dadefault_visibilityno nível do pacote).--incompatible_config_setting_private_default_visibilityfaz com queconfig_settings que não especificam umavisibilityrespeitem adefault_visibilitydo pacote e voltem à visibilidade privada, assim como qualquer outro destino de regra. Ela não faz nada se--incompatible_enforce_config_setting_visibilitynão estiver definida.
Evite depender do comportamento legado. Qualquer config_setting que seja usado fora do pacote atual precisa ter uma visibility explícita, se o pacote ainda não especificar uma default_visibility adequada.
Visibilidade do destino do grupo de pacotes
Os destinos package_group não têm um atributo visibility. Eles sã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 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++.
No momento, para fins de visibilidade, essas dependências implícitas são tratadas como qualquer outra dependência. Isso significa que o destino de 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 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 só precisa estar 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 privado, desde que esteja no mesmo pacote que a definição da regra cc_library.
Visibilidade de carregamento
A visibilidade de carregamento controla se um arquivo .bzl pode ser carregado de outros arquivos BUILD ou .bzl.
Da mesma forma que a visibilidade de destino protege o código-fonte encapsulado por destinos, a visibilidade de carregamento protege a lógica de build encapsulada por arquivos .bzl. Por exemplo, um autor de arquivo BUILD pode querer fatorar algumas definições de destino repetitivas 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, de modo que a modificação da macro interrompa os builds de outras equipes.
Observe que um arquivo .bzl pode ou não ter um destino de arquivo de origem correspondente.
Se tiver, 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
.bzl arquivo, mas não listá-lo nos srcs de um filegroup,
ou vice-versa. Isso às vezes pode causar problemas para regras que querem consumir arquivos .bzl como código-fonte, como para geração de documentação ou testes.
Para prototipagem, é possível desativar a aplicação da visibilidade de carregamento definindo --check_bzl_visibility=false. Assim como --check_visibility=false, isso não deve ser feito para o código enviado.
A visibilidade de carregamento está disponível no Bazel 6.0.
Declarar visibilidade de carregamento
Para definir a visibilidade de carregamento de um .bzl arquivo, chame a
visibility() função de dentro do arquivo.
O argumento para visibility() é uma lista de especificações de pacote, assim como
o packages atributo 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, idealmente, imediatamente após as instruções load().
Ao contrário da visibilidade de destino, a visibilidade de carregamento padrão é sempre pública. Os arquivos que não chamam visibility() sempre podem ser carregados de qualquer lugar no espaço de trabalho. É recomendável adicionar visibility("private") à parte de cima de qualquer
novo .bzl arquivo que não seja especificamente destinado 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 carregamento.
Fatorar visibilidades
Quando vários arquivos .bzl precisam ter a mesma visibilidade, pode ser útil fatorar as especificações de pacote em uma lista comum. Por 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 a distorção acidental entre as visibilidades dos vários arquivos .bzl. Também é mais legível quando a lista clients é grande.
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_groups pelo atributo
includes.
Suponha que você esteja descontinuando uma macro amplamente usada. Você quer que ela fique visível apenas para usuários atuais e para os pacotes pertencentes à 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)
Desduplicação com grupos de pacotes
Ao contrário da visibilidade de destino, não é possível definir uma visibilidade de carregamento em termos de um package_group. Se você quiser reutilizar a mesma lista de permissões para visibilidade de destino e visibilidade de carregamento, é melhor mover a lista de especificações de pacote para um arquivo .bzl, em que os dois tipos de declarações podem se referir a ela. Com base no exemplo em 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.
Proteger 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 compartilhar esses símbolos com um conjunto limitado de arquivos confiáveis. Por outro
lado, a visibilidade de carregamento oferece controle sobre quais outros pacotes podem ver seu
.bzl file, mas não permite impedir que qualquer símbolo não 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
Lint do Buildifier bzl-visibility
Há um lint do Buildifier
que fornece um aviso se os usuários carregarem um arquivo de um diretório chamado internal
ou private, quando o arquivo do usuário não estiver 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.