¿Qué es un zig build step dependOn?

¿Qué es un zig build step dependOn?

En Zig, la fase de construcción se organiza mediante un sistema de steps (pasos). Cada paso representa una tarea concreta: compilar un archivo, enlazar un binario, generar un recurso, etc. Para que estas tareas se ejecuten en el orden correcto, Zig ofrece la función dependOn, la cual declara explícitamente dependencias entre los pasos.

¿Qué es un Build Step?

Un build step en Zig es una unidad de trabajo que el sistema de construcción ejecuta. Se representa mediante la estructura Build.Step y puede ser cualquier cosa: compilar un archivo .zig, enlazar un ejecutable, copiar archivos de recursos, etc. Cuando se crea un paso, Zig automáticamente lo registra en el árbol de dependencias, pero a veces necesitamos establecer una relación más explícita.

La función dependOn

dependOn es un método que se invoca en un paso para indicar que dicho paso debe esperar a que otro paso finalice antes de comenzar. La sintaxis es sencilla:

stepA.dependOn(&stepB);

Esto garantiza que stepB se ejecutará antes que stepA. Si el paso dependiente falla, el paso que lo llama también fallará.

¿Por qué usar dependOn?

  • Control explícito del orden de ejecución.
  • Evitar errores de compilación por dependencias no resueltas.
  • Optimizar el tiempo de construcción al paralelizar pasos sin dependencias.

Sintaxis y ejemplos

Ejemplo 1: Compilar un ejecutable que depende de una librería

// build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    // 1. Crear un paso para compilar la librería
    const lib = b.addStaticLibrary("myLib", "src/lib.zig");
    lib.setBuildMode(.ReleaseSafe);

    // 2. Crear un paso para compilar el binario principal
    const exe = b.addExecutable("myApp", "src/main.zig");
    exe.setBuildMode(.ReleaseSafe);
    
    // 3. Añadir la librería como dependencia del ejecutable
    // Aquí es donde usamos dependOn
    exe.dependOn(&lib.step);
    
    // 4. Indicar que el binario final debe ser instalado
    exe.install();
}

**Explicación línea por línea**:

  • const std = @import("std"); – Importa la biblioteca estándar de Zig, necesaria para acceder al tipo Build y sus métodos.
  • pub fn build(b: *std.Build) void { – Entrada estándar del fichero de construcción.
  • const lib = b.addStaticLibrary(...); – Crea un paso que compilará una biblioteca estática llamada myLib desde src/lib.zig.
  • lib.setBuildMode(.ReleaseSafe); – Define el modo de compilación.
  • const exe = b.addExecutable(...); – Crea un paso que compilará un ejecutable llamado myApp.
  • exe.dependOn(&lib.step); – Declara que el paso de la biblioteca debe completarse antes de que el ejecutable comience a compilar.
  • exe.install(); – Indica que el binario debe copiarse al directorio install tras la construcción.
  • } – Cierre de la función build.

Ejemplo 2: Generar código antes de compilar

// build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    // Paso 1: Generar un fichero .zig a partir de JSON
    const genStep = b.addSystemCommand("python");
    genStep.addArgs("generate_code.py");

    // Paso 2: Compilar el módulo generado
    const mod = b.addModule("generated", .{ .root_source_file = genStep.result_file });
    
    // Paso 3: Compilar el programa principal
    const exe = b.addExecutable("app", "src/main.zig");
    exe.addModule("generated", mod);
    
    // Paso 4: Declarar dependencias explícitas
    exe.dependOn(&genStep.step);
    
    // Paso 5: Instalar
    exe.install();
}

**Detalles de cada línea**:

  • const genStep = b.addSystemCommand("python"); – Crea un paso que ejecutará el intérprete de Python.
  • genStep.addArgs("generate_code.py"); – Añade el script Python que generará el fichero generated.zig.
  • const mod = b.addModule("generated", .{ .root_source_file = genStep.result_file }); – Crea un módulo Zig que se basa en el archivo generado por el paso anterior.
  • exe.addModule("generated", mod); – Incluye el módulo generado en el ejecutable.
  • exe.dependOn(&genStep.step); – Garantiza que el código generado esté disponible antes de compilar el ejecutable.
  • exe.install(); – Instala el binario después de la compilación.

Ejemplo 3: Dependencias entre tests

// build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    // Paso A: Compilar la librería de pruebas
    const testLib = b.addStaticLibrary("testLib", "src/test_lib.zig");

    // Paso B: Compilar el binario de prueba
    const testExe = b.addTest("src/tests.zig");
    
    // Paso C: Asegurarse de que la librería de prueba se compile primero
    testExe.dependOn(&testLib.step);
    
    // Paso D: Ejecutar la prueba
    const runTest = b.addRunArtifact(testExe);
    
    // Paso E: Agregar al objetivo default
    b.defaultStep.dependOn(&runTest.step);
}

**Notas**:

  • El método addTest crea un paso que compila un módulo de prueba.
  • Al declarar testExe.dependOn(&testLib.step), Zig se asegura de que la librería de prueba esté lista antes de compilar el binario de prueba.
  • El paso runTest ejecuta el binario de prueba, y se añade como dependencia del paso por defecto.