Esta página aborda diretrizes básicas de estilo para Starlark e também inclui informações sobre macros e regras.
Starlark é uma linguagem que define como o software é criado. Por isso, ela é uma linguagem de programação e de configuração.
Você vai usar o Starlark para gravar arquivos BUILD
, macros e regras de build. Macros e regras são essencialmente metalinguagens: 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 o Starlark, já que os engenheiros leem arquivos BUILD
para entender as dependências dos destinos e os detalhes dos builds. Essa leitura geralmente acontece de passagem, com pressa ou em paralelo à realização de alguma outra tarefa. Por isso, a simplicidade e a legibilidade são muito importantes para que os usuários possam analisar e entender 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 fontes dessa biblioteca C++ ou remover uma dependência desse binário Java. Cada vez que você adiciona uma camada de abstração, fica mais difícil para um usuário realizar essas tarefas.
Os arquivos BUILD
também são analisados e atualizados por várias ferramentas diferentes. As ferramentas talvez não consigam editar seu arquivo BUILD
se ele usar abstrações. Manter seus arquivos BUILD
simples permite que você tenha ferramentas melhores. À medida que uma base de código cresce, torna-se cada vez mais frequente fazer mudanças em vários arquivos BUILD
para atualizar uma biblioteca ou fazer uma limpeza.
Recomendações gerais
- Use o Buildifier como um formatador e linter.
- 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 o recuo, seguindo a convenção do Python.
Como Starlark não é Python, alguns aspectos do estilo Python não se aplicam. Por exemplo, a PEP 8 aconselha que
comparações com singletons sejam feitas com is
, que não é um operador em
Starlark.
Docstring
Documente arquivos e funções usando docstrings.
Use uma docstring na parte de cima de cada arquivo .bzl
e uma docstring para cada função
pública.
Regras e aspectos do documento
As regras e os aspectos, junto com os atributos deles, bem como os provedores e os campos deles, precisam ser documentados usando o argumento doc
.
Convenção de nomenclatura
- Variáveis e nomes de 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 particulares não possam ser usados de outros arquivos. Variáveis locais não podem usar o prefixo de sublinhado.
Comprimento da linha
Assim como nos arquivos BUILD
, não há um limite de comprimento de linha, já que os rótulos podem ser longos.
Quando possível, tente usar no máximo 79 caracteres por linha (seguindo o guia de estilo do Python, PEP 8). Esta diretriz não deve ser aplicada de forma estrita: os editores precisam mostrar mais de 80 colunas, as mudanças automatizadas geralmente introduzem linhas mais longas, e os humanos não devem gastar tempo dividindo linhas que já estão legíveis.
Argumentos de palavra-chave
Em argumentos de palavra-chave, é preferível usar espaços em torno 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 se destina apenas à depuração e vai enviar spam para todos os usuários diretos e indiretos do seu arquivo .bzl
. A única exceção é que você pode enviar 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 como False
. Considere se essas declarações são úteis o suficiente para justificar o impacto na legibilidade.
Macros
Uma macro é uma função que cria 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 visto pelo usuário não é o mesmo usado pelo Bazel durante o build. As macros são expandidas antes que o Bazel faça qualquer análise de gráfico de build.
Por isso, quando algo dá errado, o usuário precisa entender a implementação da sua macro para resolver problemas de build. Além disso, os resultados do bazel
query
podem ser difíceis de interpretar porque os destinos mostrados vêm da expansão de macros. Por fim, os aspectos não conhecem macros, então as ferramentas que dependem deles (IDEs e outras) podem falhar.
Um uso seguro para macros é definir destinos adicionais que serão referenciados diretamente na CLI do Bazel ou em arquivos BUILD. Nesse caso, apenas os usuários finais desses destinos precisam saber sobre eles, e os problemas de build introduzidos por macros nunca estão longe do uso delas.
Para macros que definem destinos gerados (detalhes de implementação da macro que não devem ser referenciados na CLI nem dependidos por destinos não instanciados por essa macro), siga estas práticas recomendadas:
- Uma macro precisa receber um argumento
name
e definir uma meta com esse nome. Esse destino se torna o destino principal da macro. - Os destinos gerados, ou seja, todos os outros destinos definidos por uma macro, precisam:
- Ter os nomes prefixados por
<name>
ou_<name>
. Por exemplo, usandoname = '%s_bar' % (name)
. - Ter visibilidade restrita (
//visibility:private
) e - Use uma tag
manual
para evitar a expansão em destinos com caracteres curinga (:all
,...
,:*
etc.).
- Ter os nomes prefixados por
- O
name
só pode ser usado para derivar nomes de destinos definidos pela macro, e não para mais nada. 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. - Todos os destinos criados na macro precisam estar vinculados de alguma forma ao destino principal.
- Por convenção,
name
precisa ser o primeiro argumento ao definir uma macro. - Mantenha a consistência nos nomes dos parâmetros na macro. Se um parâmetro for transmitido
como um valor de atributo para o destino principal, mantenha o nome dele. Se um parâmetro de macro
tiver a mesma finalidade que um atributo de regra comum, como
deps
, nomeie como 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 costumam escrever macros quando a API Starlark das regras relevantes é insuficiente para o caso de uso específico, independente de a regra ser definida no Bazel em código nativo ou em Starlark. Se você estiver enfrentando esse problema, pergunte ao autor da regra se ele pode estender a API para alcançar seus objetivos.
Via de regra, quanto mais as macros se parecerem com as 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 das regras são substantivos que descrevem o principal tipo de artefato produzido pela
regra, do ponto de vista das dependências (ou, para regras de folha, do
usuário). Isso não é necessariamente um sufixo de arquivo. Por exemplo, uma regra que
produz artefatos C++ destinados a serem usados como extensões 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. Espere que todos os testes em uma meta*_test
sejam 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 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 permitindo 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 ambiente de execução que não são necessárias para 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 para
myrule
o nome_myrule_impl
. - Transmita informações entre as regras usando uma interface de provedor bem definida. Declare e documente os campos do provedor.
- Crie sua regra pensando na extensibilidade. Considere que outras regras podem querer interagir com a sua, acessar seus provedores e reutilizar as ações que você cria.
- Siga as diretrizes de performance nas suas regras.