Guia de estilo do build

Reportar um problema Ver a fonte Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

A formatação de arquivos BUILD segue a mesma abordagem do Go, em que uma ferramenta padronizada cuida da maioria dos problemas de formatação. O Buildifier é uma ferramenta que analisa e emite o código-fonte em um estilo padrão. Portanto, todos os arquivos BUILD são formatados da mesma maneira automática, o que elimina problemas de formatação durante as revisões de código. Além disso, facilita a compreensão, edição e geração de arquivos BUILD pelas ferramentas.

A formatação do arquivo BUILD precisa corresponder à saída de buildifier.

Exemplo de formatação

# Test code implementing the Foo controller.
package(default_testonly = True)

py_test(
    name = "foo_test",
    srcs = glob(["*.py"]),
    data = [
        "//data/production/foo:startfoo",
        "//foo",
        "//third_party/java/jdk:jdk-k8",
    ],
    flaky = True,
    deps = [
        ":check_bar_lib",
        ":foo_data_check",
        ":pick_foo_port",
        "//pyglib",
        "//testing/pybase",
    ],
)

Estrutura do arquivo

Recomendação: use a seguinte ordem (todos os elementos são opcionais):

  • Descrição do pacote (um comentário)

  • Todas as instruções load()

  • A função package().

  • Chamadas para regras e macros

O Buildifier faz uma distinção entre um comentário independente e um comentário anexado a um elemento. Se um comentário não estiver anexado a um elemento específico, use uma linha vazia depois dele. Essa distinção é importante ao fazer mudanças automáticas, por exemplo, para manter ou remover um comentário ao excluir uma regra.

# Standalone comment (such as to make a section in a file)

# Comment for the cc_library below
cc_library(name = "cc")

Referências a destinos no pacote atual

Os arquivos precisam ser referenciados pelos caminhos relativos ao diretório do pacote (sem usar referências de nível superior, como ..). Os arquivos gerados precisam ter o prefixo ":" para indicar que não são fontes. Os arquivos de origem não podem ser prefixados com :. As regras precisam ter o prefixo :. Por exemplo, supondo que x.cc seja um arquivo de origem:

cc_library(
    name = "lib",
    srcs = ["x.cc"],
    hdrs = [":gen_header"],
)

genrule(
    name = "gen_header",
    srcs = [],
    outs = ["x.h"],
    cmd = "echo 'int x();' > $@",
)

Nomeação de destinos

Os nomes dos destinos precisam ser descritivos. Se um destino contiver um arquivo de origem, ele geralmente terá um nome derivado dessa origem. Por exemplo, um cc_library para chat.cc pode ser chamado de chat, ou um java_library para DirectMessage.java pode ser chamado de direct_message.

O destino homônimo de um pacote (o destino com o mesmo nome do diretório que o contém) precisa fornecer a funcionalidade descrita pelo nome do diretório. Se esse destino não existir, não crie um novo com o mesmo nome.

Prefira usar o nome curto ao se referir a um destino epônimo (//x em vez de //x:x). Se você estiver no mesmo pacote, prefira a referência local (:x em vez de //x).

Evite usar nomes de destino "reservados" que têm um significado especial. Isso inclui all, __pkg__ e __subpackages__, que têm semântica especial e podem causar confusão e comportamentos inesperados quando usados.

Na ausência de uma convenção de equipe predominante, estas são algumas recomendações não vinculativas que são amplamente usadas no Google:

  • Em geral, use "snake_case"
    • Para um java_library com um src, isso significa usar um nome que não seja o mesmo que o nome do arquivo sem a extensão.
    • Para regras Java *_binary e *_test, use "Upper CamelCase". Isso permite que o nome do destino corresponda a um dos srcs. Para java_test, isso permite que o atributo test_class seja inferido do nome do destino.
  • Se houver várias variantes de uma determinada meta, adicione um sufixo para desambiguar (por exemplo, :foo_dev, :foo_prod ou :bar_x86, :bar_x64)
  • O sufixo _test é direcionado a nomes que tenham _test, _unittest, Test ou Tests
  • Evite sufixos sem sentido, como _lib ou _library, a menos que seja necessário para evitar conflitos entre uma meta _library e o _binary correspondente.
  • Para destinos relacionados a protos:
    • Os destinos proto_library precisam ter nomes que terminam em _proto
    • As regras específicas de *_proto_library precisam corresponder ao proto subjacente, mas substitua _proto por um sufixo específico do idioma, como:
      • cc_proto_library: _cc_proto
      • java_proto_library: _java_proto
      • java_lite_proto_library: _java_proto_lite

Visibilidade

A visibilidade precisa ser definida da forma mais restrita possível, mas ainda permitindo o acesso por testes e dependências inversas. Use __pkg__ e __subpackages__ conforme necessário.

Evite definir o pacote default_visibility como //visibility:public. //visibility:public precisa ser definido individualmente apenas para destinos na API pública do projeto. Podem ser bibliotecas projetadas para serem usadas por projetos externos ou binários que podem ser usados no processo de build de um projeto externo.

Dependências

As dependências precisam ser restritas às diretas (dependências necessárias pelas fontes listadas na regra). Não liste dependências transitivas.

As dependências locais do pacote precisam ser listadas primeiro e referenciadas de uma maneira compatível com a seção Referências a destinos no pacote atual acima (não pelo nome absoluto do pacote).

Prefira listar as dependências diretamente, como uma única lista. Colocar as dependências "comuns" de vários destinos em uma variável reduz a capacidade de manutenção, impossibilita que as ferramentas mudem as dependências de um destino e pode gerar dependências não utilizadas.

Globs

Indique "sem destinos" com []. Não use um glob que não corresponda a nada: ele é mais propenso a erros e menos óbvio do que uma lista vazia.

Recursivo

Não use globs recursivos para corresponder a arquivos de origem (por exemplo, glob(["**/*.java"])).

Os globs recursivos dificultam o raciocínio sobre arquivos BUILD porque ignoram subdiretórios que contêm arquivos BUILD.

Os globs recursivos geralmente são menos eficientes do que ter um arquivo BUILD por diretório com um gráfico de dependência definido entre eles, já que isso permite um melhor armazenamento em cache remoto e paralelismo.

É recomendável criar um arquivo BUILD em cada diretório e definir um gráfico de dependência entre eles.

Não recursivo

Os globs não recursivos geralmente são aceitáveis.

Outras convenções

  • Use letras maiúsculas e sublinhados para declarar constantes (como GLOBAL_CONSTANT) e letras minúsculas e sublinhados para declarar variáveis (como my_variable).

  • Os rótulos nunca devem ser divididos, mesmo que tenham mais de 79 caracteres. Os rótulos precisam ser literais de string sempre que possível. Justificativa: facilita a localização e a substituição. Além disso, ele melhora a legibilidade.

  • O valor do atributo de nome precisa ser uma string de constante literal, exceto em macros. Razão: as ferramentas externas usam o atributo de nome para se referir a uma regra. Eles precisam encontrar regras sem precisar interpretar o código.

  • Ao definir atributos do tipo booleano, use valores booleanos, não valores inteiros. Por motivos de compatibilidade, as regras ainda convertem inteiros em valores booleanos quando necessário, mas isso não é recomendado. Razão: flaky = 1 pode ser interpretado incorretamente como "deflake this target by rerunning it once". flaky = True diz de forma inequívoca "este teste é instável".

Diferenças em relação ao guia de estilo Python

Embora a compatibilidade com o Guia de estilo Python (em inglês) seja uma meta, há algumas diferenças:

  • Não há limite de comprimento de linha. Comentários e strings longos geralmente são divididos em 79 colunas, mas isso não é obrigatório. Essa prática não deve ser aplicada em revisões de código nem em scripts de pré-envio. Razão: os rótulos podem ser longos e exceder esse limite. É comum que os arquivos BUILD sejam gerados ou editados por ferramentas, o que não combina com um limite de comprimento de linha.

  • Não há suporte para concatenação de strings implícitas. Use o operador +. Razão: os arquivos BUILD contêm muitas listas de strings. É fácil esquecer uma vírgula, o que leva a um resultado completamente diferente. Isso criou muitos bugs no passado. Confira também esta discussão. (em inglês)

  • Use espaços em torno do sinal = para argumentos de palavras-chave nas regras. Razão: os argumentos nomeados são muito mais frequentes do que no Python e sempre estão em uma linha separada. Os espaços melhoram a legibilidade. Essa convenção existe há muito tempo, e não vale a pena modificar todos os arquivos BUILD existentes.

  • Por padrão, use aspas duplas para strings. Razão: isso não está especificado no guia de estilo Python, mas ele recomenda manter consistência. Então, decidimos usar apenas strings com aspas duplas. Muitas linguagens usam aspas duplas para literais de string.

  • Use uma única linha em branco entre duas definições de nível superior. Razão: a estrutura de um arquivo BUILD não é como um arquivo Python típico. Ele tem apenas instruções de nível superior. Usar uma única linha em branco encurta os arquivos BUILD.