Plugins del sistema de build en Zig
Zig ofrece un sistema de construcción (build system) muy flexible que permite a los desarrolladores automatizar la compilación, la generación de binarios y la gestión de dependencias. Un plugin en este contexto es un módulo de Zig que se integra dentro del proceso de construcción para añadir funcionalidades personalizadas, como la generación de código, la configuración de opciones de compilación o la creación de artefactos específicos.
¿Qué es el sistema de build de Zig?
En Zig, el archivo build.zig actúa como script de configuración. Este archivo utiliza el paquete std.build para definir objetivos (targets), enlaces (linkers) y tareas (steps). El flujo típico sigue estos pasos:
- Definir un
Buildconstd.build.Builder. - Crear un
ExeoLibconbuilder.addExecutableobuilder.addLibrary. - Agregar archivos fuente con
exe.addSourceFileolib.addSourceFile. - Especificar opciones de compilación y de enlazado.
- Invocar
builder.build()para generar el binario.
Plugins: ¿Qué aportan?
Un plugin permite:
- Extender el comportamiento del build system sin modificar el núcleo.
- Reutilizar código de construcción en múltiples proyectos.
- Encapsular tareas complejas (por ejemplo, generación de código de protobuffers).
- Proveer una interfaz de configuración clara y reutilizable.
Estructura básica de un plugin
Un plugin típico es un paquete Zig que exporta una función llamada build. Esta función recibe una referencia al builder y devuelve un *std.build.Step que representa la tarea añadida.
Ejemplo 1: Plugin que imprime la versión de Zig
// plugin_zig_version.zig
const std = @import("std");
/// Exporta la función de inicialización del plugin.
/// El parámetro build es el builder del proyecto que lo llama.
pub export fn build(build: *std.build.Builder) !void {
// Creamos un Step personalizado que solo ejecuta un comando.
const step = build.step("print_zig_version", "Imprime la versión de Zig");
// El comando que se ejecutará.
step.dependOn(&build.exec("zig", "build", "version"));
}
Detalle línea por línea:
const std = @import("std");importa el paquete estándar.- La firma
pub export fn build(build: *std.build.Builder)es obligatoria; el build system lo invocará automáticamente. build.stepcrea un nuevo paso de construcción con nombre y descripción.step.dependOnañade una dependencia a un paso existente; aquí usamosbuild.execpara ejecutar un comando externo.
Ejemplo 2: Plugin que genera un archivo fuente a partir de un esquema
// plugin_generate_source.zig
const std = @import("std");
pub export fn build(build: *std.build.Builder) !void {
// Ruta donde se guardará el archivo generado
const outPath = build.path("src/generated.zig");
// Paso de generación: ejecutamos un script de Python
const generateStep = build.step("generate_schema", "Genera código Zig a partir de esquema");
generateStep.dependOn(&build.exec("python", "generate_schema.py", "--out", outPath));
// Añadimos el archivo generado al objetivo
const exe = build.addExecutable("my_app", "src/main.zig");
exe.addSourceFile(outPath);
// Dependencia: la generación debe completarse antes de compilar
exe.root_module.root_source_file = &outPath;
exe.step.dependOn(&generateStep);
}
Detalle línea por línea:
- Se crea un
outPathconbuild.pathque será usado por el paso de generación. - El paso
generateStepejecuta un script externo (python generate_schema.py) que escribe el archivogenerated.zig. - El ejecutable
my_appincluye el archivo generado medianteaddSourceFile. - Se establece una dependencia explícita:
exe.step.dependOn(&generateStep), garantizando que la generación ocurra antes de la compilación.
Cómo usar un plugin en un proyecto
Para emplear un plugin, el proyecto debe declarar el paquete y llamar a su función build desde su propio build.zig.
Estructura de carpetas recomendada
project/
├─ build.zig
├─ src/
│ ├─ main.zig
│ └─ generated.zig (generado por plugin)
└─ plugins/
├─ plugin_zig_version/
│ └─ plugin_zig_version.zig
└─ plugin_generate_source/
└─ plugin_generate_source.zig
Archivo build.zig del proyecto
// build.zig
const std = @import("std");
// Importamos los plugins (suponiendo que están en plugins/)
const ZigVersionPlugin = @import("plugins/plugin_zig_version/plugin_zig_version.zig");
const GenerateSourcePlugin = @import("plugins/plugin_generate_source/plugin_generate_source.zig");
pub fn build(b: *std.build.Builder) void {
// Llamamos a cada plugin
ZigVersionPlugin.build(b);
GenerateSourcePlugin.build(b);
// Configuraciones adicionales del proyecto...
}
El flujo de ejecución:
- El build system llama a
builddel proyecto. - Dentro de esta función se invocan las funciones
buildde los plugins. - Los plugins crean sus propios pasos y añaden depend
