Esta página aborda as diretrizes de estilo básicas do Starlark e também inclui informações sobre macros e regras.
O Starlark é uma linguagem que define como o software é criado e, como tal, é uma linguagem de programação e de configuração.
Você vai usar o Starlark para escrever arquivos BUILD
, macros e regras de build. Macros e
regras são essencialmente meta-linguagens: elas definem como os arquivos BUILD
são gravados.
Os arquivos BUILD
são simples e repetitivos.
Todo software é lido com mais frequência do que escrito. Isso é especialmente verdadeiro para
Starlark, já que os engenheiros leem arquivos BUILD
para entender as dependências dos
alvos e os detalhes dos builds. Essa leitura geralmente acontece de passagem,
com pressa ou em paralelo a outra tarefa. Consequentemente,
simplicidade e legibilidade são muito importantes para que os usuários possam analisar e
compreender arquivos BUILD
rapidamente.
Quando um usuário abre um arquivo BUILD
, ele quer saber rapidamente a lista de destinos no
arquivo, revisar a lista de origens dessa biblioteca C++ ou remover uma
dependência desse binário Java. Sempre que você adiciona uma camada de abstração, dificulta que o usuário realize essas tarefas.
Os arquivos BUILD
também são analisados e atualizados por muitas ferramentas diferentes. As ferramentas podem não
editar o arquivo BUILD
se ele usar abstrações. Manter os arquivos BUILD
simples permite que você tenha ferramentas melhores. À medida que a base de código cresce, fica cada vez mais frequente fazer mudanças em muitos arquivos BUILD
para
atualizar uma biblioteca ou fazer uma limpeza.
Recomendações gerais
- Use o Buildifier como um formatador e lint.
- Siga as diretrizes de teste.
Estilo
Estilo Python
Em caso de dúvida, siga o guia de estilo PEP 8 sempre que possível. Em particular, use quatro espaços em vez de dois para seguir a convenção do Python.
Como
o Starlark não é Python,
alguns aspectos do estilo Python não se aplicam. Por exemplo, o PEP 8 aconselha que
as comparações com singletons sejam feitas com is
, que não é um operador no
Starlark.
Docstring
Documente arquivos e funções usando docstrings.
Use um docstring na parte de cima de cada arquivo .bzl
e um docstring para cada função
pública.
Documentar regras e aspectos
As regras e os aspectos, junto com os atributos, bem como os provedores e os campos, precisam ser documentados usando o argumento doc
.
Convenção de nomenclatura
- Os nomes de variáveis e funções usam letras minúsculas com palavras separadas por
sublinhados (
[a-z][a-z0-9_]*
), comocc_library
. - Os valores privados de nível superior começam com um sublinhado. O Bazel exige que valores privados não possam ser usados em outros arquivos. As variáveis locais não podem usar o prefixo de sublinhado.
Comprimento da linha
Assim como nos arquivos BUILD
, não há um limite estrito de comprimento de linha, já que os rótulos podem ser longos.
Sempre que possível, tente usar no máximo 79 caracteres por linha (seguindo o guia de estilo
do Python, PEP 8). Essa diretriz
não deve ser aplicada de forma rígida: os editores devem mostrar mais de 80 colunas,
as mudanças automáticas vão introduzir linhas mais longas com frequência, e as pessoas não devem
gastar tempo dividindo linhas que já são legíveis.
Argumentos de palavra-chave
Em argumentos de palavra-chave, é preferível usar espaços ao redor do sinal de igual:
def fct(name, srcs):
filtered_srcs = my_filter(source = srcs)
native.cc_library(
name = name,
srcs = filtered_srcs,
testonly = True,
)
Valores booleanos
Prefira os valores True
e False
(em vez de 1
e 0
) para valores booleanos,
como ao usar um atributo booleano em uma regra.
Usar a impressão apenas para depuração
Não use a função print()
no código de produção. Ela é destinada apenas ao
depuração e vai enviar spam para todos os usuários diretos e indiretos do arquivo .bzl
. A
única exceção é que você pode enviar um código que usa print()
se ele estiver desativado
por padrão e só puder ser ativado editando a origem. Por exemplo, se todos
os usos de print()
forem protegidos por if DEBUG:
, em que DEBUG
é codificado para
False
. Verifique se essas declarações são úteis o suficiente para justificar
o impacto na legibilidade.
Macros
Uma macro é uma função que instancia uma ou mais regras durante a fase de carregamento. Em geral, use regras sempre que possível em vez de macros. O gráfico de build que o usuário vê não é o mesmo usado pelo Bazel durante o build. As macros são expandidas antes que o Bazel faça qualquer análise do gráfico de build.
Por isso, quando algo dá errado, o usuário precisa entender
a implementação da macro para resolver problemas de build. Além disso, os resultados de bazel
query
podem ser difíceis de interpretar porque as metas mostradas nos resultados
vêm da expansão de macros. Por fim, os aspectos não têm conhecimento de macros, portanto, as ferramentas
que dependem de aspectos (IDEs e outros) podem falhar.
Um uso seguro de macros é para definir outras metas destinadas a ser referenciadas diretamente na CLI do Bazel ou nos arquivos BUILD: nesse caso, apenas os usuários finais dessas metas precisam saber sobre elas, e todos os problemas de build introduzidos por macros nunca estão longe do uso.
Para macros que definem destinos gerados (detalhes de implementação da macro que não devem ser referenciados na CLI ou depender de destinos não instanciados por essa macro), siga estas práticas recomendadas:
- Uma macro precisa receber um argumento
name
e definir um destino com esse nome. Essa meta se torna a principal da macro. - Os destinos gerados, ou seja, todos os outros destinos definidos por uma macro, precisam:
- Ter o nome prefixado por
<name>
ou_<name>
. Por exemplo, usandoname = '%s_bar' % (name)
. - Ter visibilidade restrita (
//visibility:private
); - Use uma tag
manual
para evitar a expansão em destinos de caractere curinga (:all
,...
,:*
etc.).
- Ter o nome prefixado por
- O
name
só deve ser usado para derivar nomes de destinos definidos pela macro, e não para qualquer outra coisa. Por exemplo, não use o nome para derivar uma dependência ou um arquivo de entrada que não seja gerado pela própria macro. - Todas as metas criadas na macro precisam ser vinculadas de alguma forma à meta principal.
- Mantenha a consistência dos nomes dos parâmetros na macro. Se um parâmetro for transmitido
como um valor de atributo para o destino principal, mantenha o mesmo nome. Se um parâmetro de macro
servir para o mesmo propósito que um atributo de regra comum, como
deps
, nomeie como você faria com o atributo (veja abaixo). - Ao chamar uma macro, use apenas argumentos de palavra-chave. Isso é consistente com as regras e melhora muito a legibilidade.
Os engenheiros geralmente escrevem macros quando a API Starlark de regras relevantes é insuficiente para o caso de uso específico, independentemente de a regra ser definida no Bazel em código nativo ou no Starlark. Se você estiver enfrentando esse problema, pergunte ao autor da regra se ele pode estender a API para alcançar suas metas.
Como regra geral, quanto mais as macros se assemelharem às regras, melhor.
Consulte também macros.
Regras
- As regras, os aspectos e os atributos deles precisam usar nomes em letras minúsculas ("snake case").
- Os nomes de regras são substantivos que descrevem o tipo principal de artefato produzido pela
regra, do ponto de vista das dependências dela (ou, no caso de regras de folha, do
usuário). Não é necessariamente um sufixo de arquivo. Por exemplo, uma regra que
produz artefatos C++ destinados a serem usados como extensões do Python pode ser chamada de
py_extension
. Para a maioria dos idiomas, as regras típicas incluem:*_library
: uma unidade de compilação ou "módulo".*_binary
: um destino que produz um executável ou uma unidade de implantação.*_test
: um destino de teste. Isso pode incluir vários testes. Todos os testes em um destino*_test
devem ser variações do mesmo tema, por exemplo, testar uma única biblioteca.*_import
: um destino que encapsula um artefato pré-compilado, como um.jar
ou um.dll
usado durante a compilação.
- Use nomes e tipos consistentes para os atributos. Alguns atributos geralmente aplicáveis
incluem:
srcs
:label_list
, permitindo arquivos: arquivos de origem, geralmente criados por humanos.deps
:label_list
, normalmente não permite arquivos: dependências de compilação.data
:label_list
, permitindo arquivos: arquivos de dados, como dados de teste etc.runtime_deps
:label_list
: dependências de tempo de execução que não são necessárias para a compilação.
- Para atributos com comportamento não óbvio (por exemplo, modelos de string
com substituições especiais ou ferramentas invocadas com requisitos
específicos), forneça documentação usando o argumento de palavra-chave
doc
para a declaração do atributo (attr.label_list()
ou semelhante). - As funções de implementação de regras quase sempre precisam ser particulares
(nomeadas com um sublinhado inicial). Um estilo comum é dar à
função de implementação de
myrule
o nome_myrule_impl
. - Transmita informações entre suas regras usando uma interface de provedor bem definida. Declare e documente os campos do provedor.
- Projete sua regra pensando na extensibilidade. Considere que outras regras podem querer interagir com sua regra, acessar seus provedores e reutilizar as ações criadas.
- Siga as diretrizes de performance nas suas regras.