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 en un estilo estándar. Por lo tanto, cada archivo BUILD
tiene el mismo formato automatizado, lo que hace que el formato 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 (todos los elementos son opcionales):
Descripción del paquete (un comentario)
Todas las sentencias
load()
La función
package()
Llamadas a reglas y macros
El compilador 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 destinos 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 superiores, 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();' > $@",
)
Asignación de nombres a los destinos
Los nombres de los destinos deben ser descriptivos. Si un destino contiene un archivo de origen, por lo 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.
Utiliza el nombre corto para hacer referencia a un destino del mismo nombre (//x
en lugar de //x:x
). Si estás en el mismo paquete, se recomienda usar la referencia local (:x
en lugar de //x
).
Evita usar nombres de segmentación "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.
En caso de que no exista una convención de equipo, estas son algunas recomendaciones no vinculantes que se suelen usar en Google:
- En general, usa "snake_case".
- Para un elemento
java_library
con unsrc
, esto significa usar un nombre que no es el mismo que el nombre del 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 lossrc
. En el caso dejava_test
, esto permite que el atributotest_class
se infiera desde el nombre del destino.
- Para un elemento
- Si hay muchas variantes de un destino en particular, agrega un sufijo para desambiguar (por ejemplo,
:foo_dev
,:foo_prod
o:bar_x86
,:bar_x64
) - Sufijo los destinos
_test
con_test
,_unittest
,Test
oTests
- Evita los sufijos sin sentido, como
_lib
o_library
, a menos que sea necesario para evitar conflictos entre un destino_library
y su_binary
correspondiente. - Para objetivos relacionados con proto:
- Los destinos de
proto_library
deben tener nombres que terminen en_proto
- Las reglas
*_proto_library
específicas de los lenguajes deben coincidir con el protocolo subyacente, pero debes reemplazar_proto
por un sufijo específico del lenguaje, como lo siguiente:cc_proto_library
:_cc_proto
java_proto_library
:_java_proto
java_lite_proto_library
:_java_proto_lite
- Los destinos de
Visibilidad
La visibilidad debe tener el alcance más exacto posible, a la vez que se permite el acceso mediante pruebas y 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 objetivos en la API pública del proyecto. Podrían ser bibliotecas diseñadas para que dependan de objetos binarios o proyectos externos que puedan 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 enumeres dependencias transitivas.
Las dependencias locales del paquete deben mostrarse primero, y se debe hacer referencia a ellas de manera que sean compatibles con la sección anterior Referencias a los destinos en el paquete actual (no por su nombre de paquete absoluto).
Prefieres enumerar las dependencias directamente, como una sola lista. Colocar las dependencias "comunes" de varios objetivos en una variable reduce la capacidad de mantenimiento, hace que sea imposible que las herramientas cambien las dependencias de un objetivo y puede generar dependencias no utilizadas.
Globs
Indica "sin objetivos" con []
. No uses un glob que no coincida con nada, ya que es más propenso a errores y menos obvio 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 entender, ya que omiten los subdirectorios que contienen archivos BUILD
.
Por lo general, los globs recursivos son 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 remotos.
Se recomienda crear un archivo BUILD
en cada directorio y definir un gráfico de dependencias entre ellos.
No recurrente
Por lo general, se aceptan globs no recurrentes.
Otras convenciones
Usa mayúsculas y guiones bajos para declarar constantes (como
GLOBAL_CONSTANT
) y usa minúsculas y guiones bajos para declarar variables (comomy_variable
).Las etiquetas nunca se deben dividir, 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 hacer referencia a una regla. Deben encontrar reglas sin tener que interpretar el código.
Cuando definas atributos de tipo booleano, usa valores booleanos, no números enteros. Por motivos heredados, las reglas aún convierten los números enteros en booleanos según sea necesario, pero no es recomendable. Razón: Podría interpretarse de forma incorrecta
flaky = 1
que dice “Para reducir la inestabilidad de este destino, vuelve a ejecutarlo una vez”.flaky = True
dice de manera inequívoca: "Esta prueba es inestable".
Diferencias con la guía de estilo de 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 cadenas largas a menudo se dividen en 79 columnas, pero esto no es obligatorio. No debe aplicarse en las revisiones de código ni en las secuencias de comandos de 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 funciona bien con el límite de longitud de línea.No se admite la concatenación de strings implícita. Usa el operador
+
. Razón: Los archivosBUILD
contienen muchas listas de strings. Es fácil olvidar una coma, lo que lleva a un resultado completamente diferente. Esto generó 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 archivosBUILD
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. Así que decidimos usar solo cadenas con 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 instrucciones de nivel superior. Si usas una sola línea en blanco, se acortanBUILD
archivos.