En esta página, se abarcan los lineamientos básicos de estilo para Starlark y también se incluye información sobre macros y reglas.
Starlark es un lenguaje que define cómo se compila el software y, como tal, es tanto un lenguaje de programación como un lenguaje de configuración.
Usarás Starlark para escribir archivos BUILD
, macros y reglas de compilación. Las macros y las reglas son, básicamente, metalenguajes, ya que definen cómo se escriben los archivos BUILD
.
Los archivos BUILD
deben ser simples y repetitivos.
Todo el software se lee con más frecuencia de lo que se escribe. Esto es especialmente cierto en el caso de Starlark, ya que los ingenieros leen los archivos BUILD
para comprender las dependencias de sus destinos y los detalles de sus compilaciones. A menudo, esta lectura se realiza de pasada, a las apuradas o en paralelo con la realización de alguna otra tarea. Por lo tanto, la simplicidad y la legibilidad son muy importantes para que los usuarios puedan analizar y comprender los archivos BUILD
rápidamente.
Cuando un usuario abre un archivo BUILD
, quiere saber rápidamente la lista de destinos en el archivo, revisar la lista de fuentes de esa biblioteca de C++ o quitar una dependencia de ese archivo binario de Java. Cada vez que agregas una capa de abstracción, dificulta que el usuario realice estas tareas.
Muchas herramientas diferentes también analizan y actualizan los archivos BUILD
. Es posible que las herramientas no puedan editar tu archivo BUILD
si usa abstracciones. Mantener tus archivos BUILD
simples te permitirá obtener mejores herramientas. A medida que crece una base de código, se vuelve cada vez más frecuente realizar cambios en muchos archivos BUILD
para actualizar una biblioteca o realizar una limpieza.
Consejo general
- Usa Buildifier como formateador y verificador de código.
- Sigue los lineamientos para las pruebas.
Estilo
Estilo de Python
Si tienes dudas, sigue la guía de estilo de PEP 8 siempre que sea posible. En particular, usa cuatro espacios en lugar de dos para la sangría y sigue la convención de Python.
Dado que Starlark no es Python, no se aplican algunos aspectos del estilo de Python. Por ejemplo, la PEP 8 recomienda que las comparaciones con singletones se realicen con is
, que no es un operador en Starlark.
Docstring
Documenta archivos y funciones con docstrings.
Usa una cadena de documentación en la parte superior de cada archivo .bzl
y una cadena de documentación para cada función pública.
Documenta las reglas y los aspectos
Las reglas y los aspectos, junto con sus atributos, así como los proveedores y sus campos, se deben documentar con el argumento doc
.
Convención de nombres
- Los nombres de variables y funciones usan letras minúsculas con palabras separadas por guiones bajos (
[a-z][a-z0-9_]*
), comocc_library
. - Los valores privados de nivel superior comienzan con un guion bajo. Bazel exige que no se puedan usar valores privados de otros archivos. Las variables locales no deben usar el prefijo de guion bajo.
Longitud de la línea
Al igual que en los archivos BUILD
, no hay un límite estricto para la longitud de la línea, ya que las etiquetas pueden ser largas.
Cuando sea posible, intenta usar un máximo de 79 caracteres por línea (siguiendo la guía de estilo de Python, PEP 8). Esta guía no se debe aplicar de forma estricta: los editores deben mostrar más de 80 columnas, los cambios automatizados introducirán líneas más largas con frecuencia y los humanos no deben dedicar tiempo a dividir líneas que ya son legibles.
Argumentos de palabras clave
En los argumentos de palabras clave, se prefieren los espacios alrededor del signo igual:
def fct(name, srcs):
filtered_srcs = my_filter(source = srcs)
native.cc_library(
name = name,
srcs = filtered_srcs,
testonly = True,
)
Valores booleanos
Prefiere los valores True
y False
(en lugar de 1
y 0
) para los valores booleanos (por ejemplo, cuando se usa un atributo booleano en una regla).
Usar la impresión solo para la depuración
No uses la función print()
en el código de producción. Solo está diseñada para la depuración y enviará spam a todos los usuarios directos e indirectos de tu archivo .bzl
. La única excepción es que puedes enviar código que use print()
si está inhabilitado de forma predeterminada y solo se puede habilitar editando el código fuente (por ejemplo, si todos los usos de print()
están protegidos por if DEBUG:
, donde DEBUG
está codificado como False
). Ten en cuenta si estas afirmaciones son lo suficientemente útiles como para justificar su impacto en la legibilidad.
Macros
Una macro es una función que crea una o más reglas durante la fase de carga. En general, usa reglas siempre que sea posible en lugar de macros. El gráfico de compilación que ve el usuario no es el mismo que usa Bazel durante la compilación. Las macros se expanden antes de que Bazel realice cualquier análisis del gráfico de compilación.
Por lo tanto, cuando algo salga mal, el usuario deberá comprender la implementación de tu macro para solucionar problemas de compilación. Además, los resultados de bazel
query
pueden ser difíciles de interpretar porque los objetivos que se muestran en los resultados provienen de la expansión de macros. Por último, los aspectos no tienen conocimiento de las macros, por lo que las herramientas que dependen de los aspectos (IDE y otros) podrían fallar.
Un uso seguro de las macros es definir destinos adicionales que se pueden consultar directamente en la CLI de Bazel o en los archivos BUILD. En ese caso, solo los usuarios finales de esos destinos deben conocerlos, y cualquier problema de compilación que introduzcan las macros nunca está lejos de su uso.
Para las macros que definen destinos generados (detalles de implementación de la macro a los que no se debe hacer referencia en la CLI ni de los que no deben depender los destinos no creados por esa macro), sigue estas prácticas recomendadas:
- Una macro debe tomar un argumento
name
y definir un destino con ese nombre. Ese objetivo se convierte en el objetivo principal de la macro. - Los destinos generados, es decir, todos los demás destinos definidos por una macro, deben cumplir con los siguientes requisitos:
- Sus nombres deben tener el prefijo
<name>
o_<name>
. Por ejemplo, conname = '%s_bar' % (name)
. - Tener visibilidad restringida (
//visibility:private
) - Tener una etiqueta
manual
para evitar la expansión en la segmentación por comodines (:all
,...
,:*
, etcétera)
- Sus nombres deben tener el prefijo
- El
name
solo se debe usar para derivar nombres de destinos definidos por la macro, y no para ningún otro propósito. Por ejemplo, no uses el nombre para derivar un archivo de dependencia o de entrada que no se genere con la propia macro. - Todos los objetivos creados en la macro deben estar vinculados de alguna manera al objetivo principal.
- Mantén la coherencia en los nombres de los parámetros de la macro. Si se pasa un parámetro como valor de atributo al destino principal, mantén el mismo nombre. Si un parámetro de macro cumple el mismo propósito que un atributo de regla común, como
deps
, asígnale el mismo nombre que el atributo (consulta a continuación). - Cuando llames a una macro, usa solo argumentos de palabras clave. Esto es coherente con las reglas y mejora en gran medida la legibilidad.
Los ingenieros suelen escribir macros cuando la API de Starlark de las reglas pertinentes no es suficiente para su caso de uso específico, independientemente de si la regla se define dentro de Bazel en código nativo o en Starlark. Si tienes este problema, pregúntale al autor de la regla si puede extender la API para que logres tus objetivos.
Como regla general, cuanto más se parezcan las macros a las reglas, mejor será el resultado.
Consulta también macros.
Reglas
- Las reglas, los aspectos y sus atributos deben usar nombres en minúsculas (“snake case”).
- Los nombres de las reglas son sustantivos que describen el tipo principal de artefacto que produce la regla, desde el punto de vista de sus dependencias (o, en el caso de las reglas hoja, del usuario). No necesariamente es un sufijo de archivo. Por ejemplo, una regla que produce artefactos de C++ destinados a usarse como extensiones de Python podría llamarse
py_extension
. En la mayoría de los idiomas, las reglas típicas incluyen lo siguiente:*_library
: Es una unidad de compilación o "módulo".*_binary
: Es un destino que produce un archivo ejecutable o una unidad de implementación.*_test
: Es un destino de prueba. Esto puede incluir varias pruebas. Se espera que todas las pruebas en un destino*_test
sean variaciones sobre el mismo tema, por ejemplo, probar una sola biblioteca.*_import
: Es un destino que encapsula un artefacto precompilado, como un.jar
o un.dll
que se usa durante la compilación.
- Usa nombres y tipos coherentes para los atributos. Algunos atributos de aplicación general son los siguientes:
srcs
:label_list
, que permite archivos: archivos fuente, por lo general, creados por personas.deps
:label_list
, por lo general, no permite archivos: dependencias de compilación.data
:label_list
, que permite archivos: archivos de datos, como datos de prueba, etcéteraruntime_deps
:label_list
: Son las dependencias del tiempo de ejecución que no se necesitan para la compilación.
- Para cualquier atributo con un comportamiento no obvio (por ejemplo, plantillas de cadenas con sustituciones especiales o herramientas que se invocan con requisitos específicos), proporciona documentación con el argumento de palabra clave
doc
en la declaración del atributo (attr.label_list()
o similar). - Las funciones de implementación de reglas casi siempre deben ser privadas (con un guion bajo inicial en el nombre). Un estilo común es darle a la función de implementación para
myrule
el nombre_myrule_impl
. - Pasa información entre tus reglas con una interfaz de proveedor bien definida. Declara y documenta los campos del proveedor.
- Diseña tu regla teniendo en cuenta la extensibilidad. Ten en cuenta que es posible que otras reglas quieran interactuar con la tuya, acceder a tus proveedores y reutilizar las acciones que crees.
- Sigue los lineamientos de rendimiento en tus reglas.