Guía de estilo de BUILD

Informa un problema Ver código fuente

El formato de archivo BUILD sigue el mismo enfoque que Go, en el que una herramienta estandarizada se encarga de la mayoría de los problemas de formato. El compilador es una herramienta que analiza y emite el código fuente en un estilo estándar. Por lo tanto, cada archivo BUILD se formatea de la misma manera automatizada, lo que hace que el formato no sea un problema durante las revisiones de código. También permite que las herramientas comprendan, editen y generen archivos BUILD con mayor facilidad.

El formato de archivo BUILD debe coincidir con el resultado de buildifier.

Ejemplo de formato

# 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",
    ],
)

Estructura de archivos

Recomendación: Usa el siguiente orden (todos los elementos son opcionales):

  • Descripción del paquete (un comentario)

  • Todas las declaraciones load()

  • La función package()

  • Llamadas a reglas y macros

El Compilador distingue entre un comentario independiente y un comentario adjunto a un elemento. Si no se adjunta un comentario a un elemento específico, usa una línea vacía después de este. La distinción es importante cuando se realizan cambios automáticos (por ejemplo, para mantener o quitar un comentario cuando se borra una regla).

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

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

Referencias a objetivos en el paquete actual

Se debe hacer referencia a los archivos por sus rutas de acceso en relación con el directorio del paquete (sin usar referencias ascendentes, como ..). Los archivos generados deben tener el prefijo “:” para indicar que no son fuentes. Los archivos de origen no deben tener el prefijo :. Las reglas deben contener el prefijo :. Por ejemplo, supongamos que x.cc es un archivo de origen:

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

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

Nombres de destino

Los nombres de destino deben ser descriptivos. Si un destino contiene un archivo de origen, en general, el destino debe tener un nombre derivado de esa fuente (por ejemplo, un cc_library para chat.cc podría llamarse chat, o un java_library para DirectMessage.java podría llamarse direct_message).

El destino epónimo de un paquete (el destino con el mismo nombre que el directorio que lo contiene) debe proporcionar la funcionalidad que se describe en el nombre del directorio. Si no existe ese objetivo, no crees un objetivo epónimo.

Se recomienda usar el nombre corto cuando se hace referencia a un destino epónimo (//x en lugar de //x:x). Si estás en el mismo paquete, usa la referencia local (:x en lugar de //x).

Evite utilizar nombres de destino "reservados" que tengan un significado especial. Esto incluye all, __pkg__ y __subpackages__; estos nombres tienen una semántica especial y pueden causar confusión y comportamientos inesperados cuando se usan.

Ante la ausencia de una convención predominante para el equipo, estas son algunas recomendaciones no vinculantes que se usan ampliamente en Google:

  • En general, usa "snake_case".
    • Para una java_library con un src, esto significa usar un nombre que no es igual al nombre de archivo sin la extensión.
    • En el caso de las reglas *_binary y *_test de Java, usa “Upper CamelCase”. Esto permite que el nombre del destino coincida con uno de los src. Para java_test, esto permite que el atributo test_class se infiera desde el nombre del destino.
  • Si hay múltiples variantes de un objetivo en particular, agrega un sufijo para eliminar la ambigüedad (como :foo_dev, :foo_prod o :bar_x86, :bar_x64)
  • Sufija _test objetivos con _test, _unittest, Test o Tests
  • Evita los sufijos sin significado, como _lib o _library (a menos que sea necesario para evitar conflictos entre un objetivo _library y su _binary correspondiente).
  • Para destinos relacionados con los protocolos, haz lo siguiente:
    • Los objetivos proto_library deben tener nombres que terminen en _proto
    • Las reglas *_proto_library específicas de los idiomas deben coincidir con el protocolo subyacente, pero reemplazar _proto por un sufijo específico del idioma, por ejemplo:
      • cc_proto_library: _cc_proto
      • java_proto_library: _java_proto
      • java_lite_proto_library: _java_proto_lite

Visibilidad

La visibilidad debe tener el máximo alcance posible, a la vez que se permite el acceso de las pruebas y las dependencias inversas. Usa __pkg__ y __subpackages__ según corresponda.

Evita configurar el paquete default_visibility en //visibility:public. //visibility:public debe configurarse de forma individual solo para los objetivos de la API pública del proyecto. Pueden ser bibliotecas que se diseñaron para depender de proyectos externos o de objetos binarios que puede usar el proceso de compilación de un proyecto externo.

Dependencias

Las dependencias deben restringirse a las dependencias directas (dependencias necesarias por las fuentes enumeradas en la regla). No enumeres las dependencias transitivas.

Las dependencias locales del paquete deben aparecer primero y mencionarse de una manera compatible con las Referencias a los destinos en el paquete actual (no por su nombre de paquete absoluto).

Prefiere enumerar las dependencias directamente como una sola lista. Colocar las dependencias "comunes" de varios destinos en una variable reduce la capacidad de mantenimiento, hace que sea imposible para las herramientas cambiar las dependencias de un destino y puede generar dependencias sin usar.

Globs

Indica "sin objetivos" con []. No uses un glob que no coincida con nada: es más propenso a errores y menos obvio que una lista vacía.

Recurrente

No uses globs recursivos para hacer coincidir los archivos de origen (por ejemplo, glob(["**/*.java"])).

Los globs glob recursivos dificultan la lógica de los archivos BUILD, ya que omiten los subdirectorios que contienen archivos BUILD.

Los globs glob recursivos suelen ser menos eficientes que tener un archivo BUILD por directorio con un grafo de dependencia definido entre ellos, ya que esto permite un mejor almacenamiento en caché y paralelismo de forma remota.

Se recomienda crear un archivo BUILD en cada directorio y definir un grafo de dependencia entre ellos.

No recursivo

Generalmente, se aceptan globs no recurrentes.

Otras convenciones

  • Usa mayúsculas y guiones bajos para declarar constantes (como GLOBAL_CONSTANT), y minúsculas y guiones bajos para declarar variables (como my_variable).

  • Las etiquetas nunca deben dividirse, incluso si tienen más de 79 caracteres. Las etiquetas deben ser literales de string siempre que sea posible. Razón: Facilita la búsqueda y el reemplazo. También mejora la legibilidad.

  • El valor del atributo de nombre debe ser una string constante literal (excepto en macros). Razón: Las herramientas externas usan el atributo de nombre para referirse a una regla. Deben encontrar reglas sin tener que interpretar el código.

  • Cuando configures atributos de tipo booleano, usa valores booleanos, no números enteros. Por motivos heredados, las reglas aún convierten números enteros en booleanos según sea necesario, pero no se recomienda. Razón: flaky = 1 podría interpretarse incorrectamente como decir "deshace este objetivo volviendo a ejecutarlo una vez". flaky = True indica inequívocamente que "esta prueba es inestable".

Guía de estilo de diferencias con Python

Aunque la compatibilidad con la guía de estilo de Python es un objetivo, hay algunas diferencias:

  • No hay un límite estricto de longitud de línea. Los comentarios largos y las strings largas a menudo se dividen en 79 columnas, pero no es obligatorio. No debe aplicarse de manera forzosa en las revisiones de código ni las secuencias de comandos de envío previo. Razón: Las etiquetas pueden ser extensas y superar este límite. Es común que las herramientas generen o editen archivos BUILD, lo que no va bien con un límite de longitud de línea.

  • No se admite la concatenación de strings implícita. Usa el operador +. Razón: Los archivos BUILD contienen muchas listas de strings. Es fácil olvidar una coma, lo que lleva a un resultado completamente diferente. Esto ha creado muchos errores. Consulta también este debate.

  • Usa espacios alrededor del signo = para los argumentos de palabras clave en las reglas. Razón: Los argumentos con nombre son mucho más frecuentes que en Python y siempre están en una línea separada. Los espacios mejoran la legibilidad. Esta convención existe desde hace mucho tiempo y no vale la pena modificar todos los archivos BUILD existentes.

  • De forma predeterminada, usa comillas dobles para las strings. Razón: Esto no se especifica en la guía de estilo de Python, pero recomienda coherencia. Por lo tanto, decidimos usar solo strings entre comillas dobles. Muchos lenguajes usan comillas dobles para los literales de string.

  • Usa una sola línea en blanco entre dos definiciones de nivel superior. Razón: La estructura de un archivo BUILD no es como un archivo típico de Python. Solo tiene declaraciones de nivel superior. El uso de una sola línea en blanco reduce el tamaño de los archivos BUILD.