Guía de estilo de BUILD

Informar un problema Ver fuente Noche}

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. Buildifier es una herramienta que analiza y emite el código fuente con un estilo estándar. Por lo tanto, cada archivo BUILD tiene el mismo formato automatizado, lo que hace que el formateo no sea un problema durante las revisiones de código. También facilita que las herramientas comprendan, editen y generen archivos BUILD.

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 (cada elemento es opcional):

  • Descripción del paquete (un comentario)

  • Todos los estados de cuenta load()

  • La función package()

  • Llamadas a reglas y macros

Buildifier hace una distinción 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 conservar 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 los destinos en el paquete actual

Para hacer referencia a los archivos, utiliza sus rutas de acceso en relación con el directorio del paquete (sin usar nunca referencias hacia arriba, 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 tener 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();' > $@",
)

Nomenclatura de objetivos

Los nombres de los destinos deben ser descriptivos. Si un destino contiene un archivo de origen, el destino, por lo general, 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 tal objetivo, no crees un objetivo epónimo.

Es preferible usar el nombre corto para hacer referencia a un objetivo eónimo (//x en lugar de //x:x). Si estás en el mismo paquete, es preferible usar la referencia local (:x en lugar de //x).

Evita 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 de equipo predominante, 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 que debes usar un nombre que no sea el mismo que el nombre de archivo sin la extensión.
    • Para las reglas *_binary y *_test de Java, usa "Upper CamelCase". Esto permite que el nombre del destino coincida con uno de los src. En el caso de java_test, esto permite que se infiera el atributo test_class desde el nombre del destino.
  • Si hay múltiples variantes de un destino en particular, agrega un sufijo para desambiguar (por ejemplo, :foo_dev, :foo_prod o :bar_x86, :bar_x64)
  • Sufija los objetivos _test con _test, _unittest, Test o Tests
  • Evita los sufijos sin sentido, como _lib o _library (a menos que sean necesarios para evitar conflictos entre un objetivo _library y su _binary correspondiente).
  • Para destinos relacionados con proto:
    • proto_library destinos deben tener nombres que terminen en _proto
    • Las reglas *_proto_library específicas de los lenguajes deben coincidir con el proto subyacente, pero reemplazar _proto por un sufijo específico del lenguaje, como el siguiente:
      • cc_proto_library: _cc_proto
      • java_proto_library: _java_proto
      • java_lite_proto_library: _java_proto_lite

Visibilidad

El alcance de la visibilidad debe ser lo más restringido posible y, al mismo tiempo, permitir el acceso de las pruebas y las dependencias inversas. Usa __pkg__ y __subpackages__ según corresponda.

No configures el paquete default_visibility como //visibility:public. //visibility:public debe configurarse de forma individual solo para los destinos en la API pública del proyecto. Podrían ser bibliotecas diseñadas para depender de proyectos externos o objetos binarios que podrían usarse en un proceso de compilación de un proyecto externo.

Dependencias

Las dependencias deben restringirse a dependencias directas (dependencias que necesitan las fuentes enumeradas en la regla). No incluyas dependencias transitivas.

Las dependencias locales del paquete deben aparecer primero y se debe hacer referencia a ellas de una manera compatible con la sección Referencias a los destinos en el paquete actual anterior (no por su nombre de paquete absoluto).

Prefieres enumerar las dependencias directamente, como una sola lista. Poner las dependencias "comunes" de varios objetivos en una variable reduce la capacidad de mantenimiento, hace que las herramientas imposibiliten el cambio de las dependencias de un objetivo y puede provocar que se usen dependencias.

Globs

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

Recursiva

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

Los globs recursivos hacen que los archivos BUILD sean difíciles de deducir porque omiten los subdirectorios que contienen archivos BUILD.

Los globs recursivos suelen ser menos eficientes que tener un archivo BUILD por directorio con un gráfico de dependencia definido entre ellos, ya que esto habilita un mejor paralelismo y almacenamiento en caché remoto.

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

No recursivo

Por lo general, se aceptan los globs no recursivos.

Otras convenciones

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

  • Las etiquetas nunca se deben dividir, incluso si tienen más de 79 caracteres. Las etiquetas deben ser literales de cadena siempre que sea posible. Razón: Hace que sea fácil buscar y reemplazar. También mejora la legibilidad.

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

  • Cuando establezca atributos de tipo booleano, use valores booleanos, no valores enteros. Por motivos heredados, las reglas siguen convirtiendo números enteros en booleanos según sea necesario, pero no es recomendable. Razón: Es posible que flaky = 1 se interprete de forma errónea y que diga "deflake este destino volviendo a ejecutarlo una vez". flaky = True indica inequívocamente "esta prueba es inestable".

Diferencias con la guía de estilo de Python

Si bien la compatibilidad con la guía de estilo de Python es un objetivo, existen algunas diferencias:

  • No hay un límite estricto de longitud de línea. Los comentarios largos y las cadenas largas se suelen dividir en 79 columnas, pero no es obligatorio. No debe aplicarse en revisiones de código ni secuencias de comandos del envío previo. Razón: Las etiquetas pueden ser largas y superar este límite. Es común que las herramientas generen o editen los archivos BUILD, lo que no se ajusta bien al límite de longitud de línea.

  • No se admite la concatenación de cadenas implícitas. Usa el operador +. Razón: Los archivos BUILD contienen muchas listas de cadenas. Es fácil olvidar una coma, lo que lleva a un resultado completamente diferente. Esto creó muchos errores en el pasado. 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 cadenas. Razón: Esto no se especifica en la guía de estilo de Python, pero recomienda coherencia. Así que decidimos usar solo cadenas entre comillas dobles. Muchos lenguajes usan comillas dobles para literales de cadenas.

  • 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 sentencias de nivel superior. Si usas una sola línea en blanco, los archivos BUILD serán más cortos.