Guia de estilo do arquivo .bzl

Informar um problema Acessar código-fonte

Esta página aborda as diretrizes básicas de estilo do Starlark e inclui informações sobre macros e regras.

Starlark é uma linguagem que define como o software é criado e, por isso, é uma linguagem de programação e de configuração.

Você 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 escritos. Os arquivos BUILD precisam ser simples e repetitivos.

Todo software é lido com mais frequência do que escrito. Isso vale especialmente 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 acontecerá de passagem, com pressa ou em paralelo à realização de outra tarefa. Consequentemente, a simplicidade e a 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, analisar 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, você dificulta a realização dessas tarefas por um usuário.

Os arquivos BUILD também são analisados e atualizados por muitas ferramentas diferentes. Talvez as ferramentas 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 a base de código cresce, torna-se cada vez mais frequente fazer alterações em muitos arquivos BUILD para atualizar uma biblioteca ou fazer uma limpeza.

Recomendações gerais

Estilo

Estilo Python

Em caso de dúvida, siga o Guia de estilo PEP 8 sempre que possível. Use quatro espaços em vez de dois espaços para recuo de acordo com a convenção do Python.

Como o Starlark não é Python, alguns aspectos do estilo Python não se aplicam. Por exemplo, a PEP 8 recomenda que as comparações com Singletons sejam feitas com is, que não é um operador no Starlark.

String de documento

Documente arquivos e funções usando docstrings. Use um docstring na parte superior de cada arquivo .bzl e um docstring para cada função pública.

Documentar regras e aspectos

Regras e aspectos, além dos respectivos atributos, provedores e campos, precisam ser documentados usando o argumento doc.

Convenção de nomenclatura

  • As variáveis e os nomes de funções usam letras minúsculas com palavras separadas por sublinhados ([a-z][a-z0-9_]*), como cc_library.
  • Valores particulares de nível superior começam com um sublinhado. O Bazel impõe que valores particulares não possam ser usados em outros arquivos. Variáveis locais não devem usar o prefixo sublinhado.

Comprimento da linha

Como nos arquivos BUILD, não há um limite rígido 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. Essa diretriz não deve ser aplicada estritamente: os editores precisam exibir mais de 80 colunas, alterações automatizadas frequentemente 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 valores True e False (em vez de 1 e 0) para valores booleanos (como ao usar um atributo booleano em uma regra).

Não use a função print() no código de produção. Ela se destina apenas à depuração e vai enviar spam a todos os usuários diretos e indiretos do arquivo .bzl. A única exceção é que você poderá 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 está fixado no código para False. Esteja atento se essas instruções são úteis o suficiente para justificar o impacto delas 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 visto pelo usuário não é o mesmo usado pelo Bazel durante a versão. As macros são expandidas antes de o Bazel fazer 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 os destinos mostrados nos resultados vêm da expansão macro. Por fim, os aspectos não estão cientes das macros, portanto, as ferramentas que dependem de aspectos (IDEs e outros) podem falhar.

Um uso seguro das macros é definir outros destinos a serem referenciados diretamente na CLI do Bazel ou em arquivos BUILD. Nesse caso, apenas os usuários finais desses destinos precisam conhecê-los, e os problemas de compilação introduzidos pelas macros nunca estão longe de serem usados.

Para macros que definem destinos gerados (detalhes de implementação da macro que não devem ser referidos na CLI ou dependentes de destinos não instanciados por essa macro), siga estas práticas recomendadas:

  • Uma macro precisa usar um argumento name e definir um destino com esse nome. Esse destino se torna o principal alvo dessa macro.
  • Os destinos gerados, ou seja, todos os outros destinos definidos por uma macro, precisam:
    • Ter os nomes com o prefixo <name> ou _<name>. Por exemplo, usando name = '%s_bar' % (name).
    • têm visibilidade restrita (//visibility:private);
    • Tenha uma tag manual para evitar a expansão em destinos com caracteres curinga (:all, ..., :* etc.).
  • O name só é 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 ser acoplados de alguma forma ao destino principal.
  • Mantenha os nomes dos parâmetros na macro consistentes. Se um parâmetro for transmitido como um valor de atributo para o destino principal, mantenha o nome dele. Se um parâmetro da macro tiver a mesma finalidade de um atributo de regra comum, como deps, dê um nome igual ao do atributo (confira abaixo).
  • Ao chamar uma macro, use apenas argumentos de palavra-chave. Isso é consistente com as regras e melhora muito a legibilidade.

Os engenheiros geralmente gravam 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 atingir suas metas.

Como regra geral, quanto mais macros se assemelham às regras, melhor.

Consulte também macros.

Regras

  • Regras, aspectos e os respectivos atributos precisam usar nomes em letras minúsculas.
  • Os nomes de regras são substantivos que descrevem o tipo principal de artefato produzido pela regra, do ponto de vista das dependências (ou, para regras de folha, o usuário). 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. Na maioria das linguagens, as regras comuns 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. Espera-se que todos os testes em um destino *_test sejam variações do mesmo tema, por exemplo, testando 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 que costumam ser aplicáveis incluem:
    • srcs: label_list, permitindo arquivos: arquivos de origem, normalmente de criação humana.
    • 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 compilação.
  • Para todos os atributos com comportamento não óbvio (por exemplo, modelos de string com substituições especiais ou ferramentas invocadas com requisitos específicos), envie a documentação usando o argumento de palavra-chave doc na declaração do atributo (attr.label_list() ou semelhante).
  • As funções de implementação de regras devem ser quase sempre funções privadas (nomeadas com um sublinhado inicial). Um estilo comum é dar à função de implementação para myrule o nome _myrule_impl.
  • Transmita informações entre suas regras usando uma interface de provedor bem definida. Declarar e documentar os campos do provedor.
  • Crie sua regra com a extensibilidade em mente. Considere que outras regras podem interagir com elas, acessar seus provedores e reutilizar as ações que você criar.
  • Siga as diretrizes de desempenho nas suas regras.