En esta página, se explican los aspectos básicos del uso de macros y se incluyen casos de uso típicos, la depuración y las convenciones.
Una macro es una función a la que se llama desde el archivo BUILD
que puede crear una instancia de reglas.
Las macros se usan principalmente para el encapsulamiento y la reutilización de código de reglas existentes.
y otras macros. Al final del
fase de carga, las macros ya no existen
y Bazel solo ve el conjunto concreto de reglas con instancias.
Uso
El caso de uso típico de una macro es cuando se desea reutilizar una regla.
Por ejemplo, genrule en un archivo BUILD
genera un archivo con
//:generator
con un argumento some_arg
codificado en el comando:
genrule(
name = "file",
outs = ["file.txt"],
cmd = "$(location //:generator) some_arg > $@",
tools = ["//:generator"],
)
Si quieres generar más archivos con diferentes argumentos, puedes
extraeré este código en una función macro. Llamemos a la macro file_generator
, que
tiene los parámetros name
y arg
. Reemplaza la genrule por lo siguiente:
load("//path:generator.bzl", "file_generator")
file_generator(
name = "file",
arg = "some_arg",
)
file_generator(
name = "file-two",
arg = "some_arg_two",
)
file_generator(
name = "file-three",
arg = "some_arg_three",
)
Aquí, cargarás el símbolo file_generator
de un archivo .bzl
ubicado
en el paquete //path
. Al colocar las definiciones de las macrofunciones en un
.bzl
, mantienes tus archivos BUILD
limpios y declarativos; el .bzl
se pueden cargar desde cualquier paquete en el espacio de trabajo.
Por último, en path/generator.bzl
, escribe la definición de la macro en
encapsula y parametriza la definición original de genrule:
def file_generator(name, arg, visibility=None):
native.genrule(
name = name,
outs = [name + ".txt"],
cmd = "$(location //:generator) %s > $@" % arg,
tools = ["//:generator"],
visibility = visibility,
)
También puedes usar macros para encadenar reglas. En este ejemplo, se muestran imágenes genrules, en que una genrule usa los resultados de una genrule anterior como entradas:
def chained_genrules(name, visibility=None):
native.genrule(
name = name + "-one",
outs = [name + ".one"],
cmd = "$(location :tool-one) $@",
tools = [":tool-one"],
visibility = ["//visibility:private"],
)
native.genrule(
name = name + "-two",
srcs = [name + ".one"],
outs = [name + ".two"],
cmd = "$(location :tool-two) $< $@",
tools = [":tool-two"],
visibility = visibility,
)
En este ejemplo, solo se asigna un valor de visibilidad a la segunda genrule. Esto permite de macros para ocultar los resultados de las reglas intermedias de modo que no se dependa de por otros destinos del lugar de trabajo.
Macros desplegables
Cuando quieras investigar lo que hace una macro, usa el comando query
con
--output=build
para ver el formulario expandido:
$ bazel query --output=build :file
# /absolute/path/test/ext.bzl:42:3
genrule(
name = "file",
tools = ["//:generator"],
outs = ["//test:file.txt"],
cmd = "$(location //:generator) some_arg > $@",
)
Crea una instancia de reglas nativas
Las reglas nativas (reglas que no necesitan una sentencia load()
) pueden
desde el módulo native:
def my_macro(name, visibility=None):
native.cc_library(
name = name,
srcs = ["main.cc"],
visibility = visibility,
)
Si necesitas saber el nombre del paquete (por ejemplo, qué archivo BUILD
llama a
usa la función native.package_name().
Ten en cuenta que native
solo se puede usar en archivos .bzl
y no en WORKSPACE
o
BUILD
.
Resolución de etiqueta en macros
Dado que las macros se evalúan en la fase de carga,
las cadenas de etiquetas, como "//foo:bar"
, que ocurren en una macro se interpretan
en relación con el archivo BUILD
en el que se usa la macro, y no relativa a
el archivo .bzl
en el que se define. Por lo general, este comportamiento no es recomendable
para macros diseñadas para usarse en otros repositorios, por ejemplo, porque
son parte de un conjunto de reglas de Starlark publicado.
Para obtener el mismo comportamiento que con las reglas de Starlark, une las cadenas de etiquetas con el
Label
:
# @my_ruleset//rules:defs.bzl
def my_cc_wrapper(name, deps = [], **kwargs):
native.cc_library(
name = name,
deps = deps + select({
# Due to the use of Label, this label is resolved within @my_ruleset,
# regardless of its site of use.
Label("//config:needs_foo"): [
# Due to the use of Label, this label will resolve to the correct target
# even if the canonical name of @dep_of_my_ruleset should be different
# in the main workspace, such as due to repo mappings.
Label("@dep_of_my_ruleset//tools:foo"),
],
"//conditions:default": [],
}),
**kwargs,
)
Depuración
bazel query --output=build //my/path:all
te mostrará cómo el archivoBUILD
se encarga de la evaluación. Se expanden todas las macros, globs y bucles. Conocido limitación: Por el momento, las expresionesselect
no se muestran en el resultado.Puedes filtrar el resultado en función de
generator_function
(qué función generó las reglas) ogenerator_name
(el atributo de nombre de la macro):bash $ bazel query --output=build 'attr(generator_function, my_macro, //my/path:all)'
Para averiguar dónde se genera exactamente la regla
foo
en un archivoBUILD
, debes hacer lo siguiente: puedes probar el siguiente truco. Inserta esta línea cerca de la parte superior deBUILD
. archivo:cc_library(name = "foo")
. Ejecuta Bazel. Recibirás una excepción cuando se crea la reglafoo
(debido a un conflicto de nombres), que te mostrará las seguimiento de pila completa.También puedes usar print para la depuración. Muestra el mensaje como una línea de registro
DEBUG
durante la fase de carga Excepto en raras casos, quita las llamadasprint
o hazlas condicionales bajo una El parámetrodebugging
que se establece de forma predeterminada enFalse
antes de enviar el código a el depósito.
Errores
Si quieres arrojar un error, usa la función fail.
Explica claramente al usuario qué salió mal y cómo corregir su archivo BUILD
.
No es posible detectar un error.
def my_macro(name, deps, visibility=None):
if len(deps) < 2:
fail("Expected at least two values in deps")
# ...
Convenciones
Todas las funciones públicas (funciones que no comienzan con guion bajo) que las reglas de creación de instancias deben tener un argumento
name
. Este argumento no debe ser es opcional (no proporcione un valor predeterminado).Las funciones públicas deben usar una docstring después de Python convenciones.
En los archivos
BUILD
, el argumentoname
de las macros debe ser una palabra clave. (no un argumento posicional).El atributo
name
de las reglas generadas por una macro debe incluir el nombre. como prefijo. Por ejemplo,macro(name = "foo")
puede generar uncc_library
foo
y una regla generalfoo_gen
.En la mayoría de los casos, los parámetros opcionales deben tener un valor predeterminado de
None
.None
se puede pasar directamente a reglas nativas, que lo tratan de la misma manera que si no habías pasado ningún argumento. Por lo tanto, no es necesario reemplazarlo con0
,False
o[]
para este fin. En cambio, la macro debe diferir a las reglas que crea, ya que sus valores predeterminados pueden ser complejos o pueden cambiar tiempo. Además, un parámetro que se establece explícitamente en su valor predeterminado es diferente de una que nunca se establece (o que se establece enNone
) cuando se accede a través del lenguaje de consulta o los componentes internos del sistema de compilación.Las macros deben tener un argumento
visibility
opcional.