Guía de estilo .bzl

Informa un problema Ver código fuente

En esta página, se describen los lineamientos de estilo básicos de 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, por lo tanto, es un lenguaje de programación y un de configuración.

Usarás Starlark para escribir archivos BUILD, macros y reglas de compilación. Las macros y las reglas son, en esencia, metalenguajes: definen cómo se escriben los archivos BUILD. Los archivos BUILD están diseñados para ser simples y repetitivos.

Todo el software se lee con más frecuencia de la que se escribe. Esto se aplica en especial a Starlark, ya que los ingenieros leen los archivos BUILD para comprender las dependencias de sus destinos y los detalles de sus compilaciones. Por lo general, esta lectura se realiza en el paso, en prisa o en paralelo para realizar 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 con rapidez.

Cuando un usuario abre un archivo BUILD, quiere conocer rápidamente la lista de destinos del archivo, revisar la lista de fuentes de esa biblioteca de C++ o quitar una dependencia de ese objeto binario de Java. Cada vez que agregas una capa de abstracción, haces que sea más difícil para un usuario realizar estas tareas.

BUILD Los archivos también se analizan y actualizan mediante diversas herramientas. Es posible que las herramientas no puedan editar tu archivo BUILD si usa abstracciones. Mantener los archivos BUILD simples te permitirá obtener mejores herramientas. A medida que crece la base de código, se hace cada vez más frecuente realizar cambios en muchos archivos BUILD para actualizar una biblioteca o realizar una limpieza.

Consejo general

Mantén tu 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 que la sangría siga la convención de Python.

Dado que Starlark no es Python, no se aplican algunos aspectos del estilo de Python. Por ejemplo, PEP 8 recomienda que las comparaciones con singleton se realicen con is, que no es un operador de Starlark.

Docstring

Documenta archivos y funciones con docstrings. Usa una docstring en la parte superior de cada archivo .bzl y una docstring para cada función pública.

Reglas y aspectos de los documentos

Las reglas y los aspectos, junto con los atributos, los proveedores y sus campos, deben documentarse mediante el argumento doc.

Convención de nombres

  • Las variables y los nombres de funciones usan minúsculas con palabras separadas por guiones bajos ([a-z][a-z0-9_]*), como cc_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 de longitud de línea, ya que las etiquetas pueden ser largas. Cuando sea posible, intenta usar 79 caracteres como máximo por línea (según la guía de estilo de Python, PEP 8). Este lineamiento no debe aplicarse de manera estricta: los editores deben mostrar más de 80 columnas, los cambios automatizados suelen introducir líneas más largas, y los humanos no deben dedicar tiempo a dividir las líneas que ya son legibles.

Argumentos de palabra 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

Elige valores True y False (en lugar de 1 y 0) para los valores booleanos (como cuando se usa un atributo booleano en una regla).

No uses la función print() en el código de producción, ya que solo está diseñada para la depuración y enviará spam a todos los usuarios indirectos y directos de tu archivo .bzl. La única excepción es que puedes enviar un código que use print() si está inhabilitado de forma predeterminada y solo se pueda habilitar si editas la fuente, por ejemplo, si if DEBUG: protege todos los usos de print(), donde DEBUG está codificado en False. Ten en cuenta si estas declaraciones son lo suficientemente útiles para justificar su impacto en la legibilidad.

Macros

Una macro es una función que crea una instancia de una o más reglas durante la fase de carga. En general, usa reglas cuando 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 un análisis de grafos de compilación.

Debido a esto, 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 están al tanto de las macros, por lo que las herramientas basadas en aspectos (IDE y otros) pueden fallar.

Un uso seguro para las macros es para definir objetivos adicionales destinados a hacer referencia directamente en la CLI de Bazel o en los archivos de COMPILACIÓN: en ese caso, solo los usuarios finales de esos destinos deben conocerlos, y los problemas de compilación presentados por las macros nunca están lejos de su uso.

En el caso de las macros que definen objetivos generados (detalles de la implementación de la macro a los que no se hace referencia en la CLI ni de los que no se crean instancias de esa macro), sigue estas prácticas recomendadas:

  • Una macro debe tomar un argumento name y definir un objetivo con ese nombre. Ese objetivo se convierte en el objetivo principal de esa macro.
  • Los destinos generados, es decir, todos los demás destinos definidos por una macro, deben cumplir con lo siguiente:
    • Deben tener el nombre <name> o _<name> con el prefijo. Por ejemplo, mediante name = '%s_bar' % (name).
    • Tienen visibilidad restringida (//visibility:private).
    • Debes tener una etiqueta manual para evitar la expansión en objetivos comodín (:all, ..., :*, etc.).
  • Solo se debe usar name para derivar nombres de destinos definidos por la macro, y no para nada más. Por ejemplo, no uses el nombre para derivar una dependencia o un archivo de entrada que la macro no genera.
  • Todos los objetivos creados en la macro deben vincularse de alguna manera con el objetivo principal.
  • Los nombres de los parámetros en la macro deben ser coherentes. Si se pasa un parámetro como valor del atributo al destino principal, mantén su nombre igual. Si un parámetro de macro tiene el mismo propósito que un atributo de regla común, como deps, nombra como lo harías con 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 la legibilidad en gran medida.

Los ingenieros suelen escribir macros cuando la API de Starlark de reglas relevantes no es suficiente para su caso práctico específico, sin importar si la regla se define dentro de Bazel en código nativo o en Starlark. Si te enfrentas a este problema, pregúntale al autor de la regla si puede ampliar la API para lograr tus objetivos.

Como regla general, mientras más macros se asemejen a las reglas, mejor.

Consulta también macros.

Reglas

  • Las reglas, los aspectos y sus atributos deben usar nombres en minúscula (mayúsculas o minúsculas).
  • Los nombres de reglas son sustantivos que describen el tipo principal de artefacto producido por la regla, desde el punto de vista de sus dependencias (o, para las reglas de hoja, el usuario). Esto no es necesariamente un sufijo de archivo. Por ejemplo, una regla que produce artefactos C++ destinados a usarse como extensiones de Python puede llamarse py_extension. Para la mayoría de los lenguajes, las reglas típicas incluyen las siguientes:
    • *_library: Es una unidad de compilación o un "módulo".
    • *_binary: Es un destino que produce un ejecutable o una unidad de implementación.
    • *_test: Es un objetivo de prueba. Esto puede incluir varias pruebas. Se espera que todas las pruebas de un destino *_test sean variaciones del mismo tema, por ejemplo, que se pruebe una sola biblioteca.
    • *_import: Es un destino que encapsula un artefacto compilado previamente, como un .jar o un .dll que se usa durante la compilación.
  • Use nombres y tipos coherentes para los atributos. Estos son algunos atributos que se suelen aplicar:
    • srcs: label_list, lo que permite archivos: archivos de origen, por lo general, de autor humano.
    • deps: label_list, por lo general, no permite archivos: dependencias de compilación.
    • data: label_list, lo que permite archivos: datos, como datos de prueba, etcétera.
    • runtime_deps: label_list: Dependencias del entorno de ejecución que no se necesitan para la compilación.
  • Para cualquier atributo con comportamiento no evidente (por ejemplo, plantillas de string con sustituciones especiales o herramientas que se invocan con requisitos específicos), proporciona documentación mediante el argumento de palabra clave doc en la declaración del atributo (attr.label_list() o similar).
  • Casi siempre, las funciones de implementación de reglas deben ser funciones privadas (con un guion bajo al principio). Un estilo común es dar a la función de implementación para myrule el nombre _myrule_impl.
  • Pasa la información entre tus reglas mediante una interfaz provider bien definida. Declara y documenta los campos del proveedor.
  • Diseñe su norma teniendo en cuenta la extensibilidad. Ten en cuenta que otras reglas pueden querer interactuar con tu regla, acceder a tus proveedores y reutilizar las acciones que creas.
  • Sigue los lineamientos de rendimiento en tus reglas.