Cross-compilation en Zig: compilar para cualquier plataforma desde cualquier máquina

Cross-compilar en C o C++ normalmente significa instalar un toolchain específico para cada plataforma destino. Para compilar desde Linux a macOS necesitas el SDK de Apple y alguna herramienta como osxcross. Para compilar a ARM necesitas una toolchain ARM. Es perfectamente hacible, pero hay que configurarlo todo y mantenerlo. Zig resuelve esto de forma diferente: la cross-compilation está incorporada y funciona sin instalar nada adicional.

Cómo funciona la cross-compilation en Zig

Zig incluye LLVM internamente y lleva consigo las libc para todos los targets principales (musl para Linux, mingw-w64 para Windows, y más). Esto significa que para compilar a un target diferente solo necesitas especificar el target en la línea de comandos o en el build.zig. No hay toolchain adicional que instalar.

# Ver todos los targets disponibles
zig targets | head -50

# Compilar para ARM64 Linux (Raspberry Pi, servidores ARM)
zig build -Dtarget=aarch64-linux-musl

# Compilar para Windows x86_64 desde Linux o macOS
zig build -Dtarget=x86_64-windows-gnu

# Compilar para macOS desde Linux
zig build -Dtarget=aarch64-macos

# Compilar para x86_64 Linux con musl (binario estático)
zig build -Dtarget=x86_64-linux-musl

El formato del target

Los targets en Zig siguen el formato cpu-os-abi. La CPU puede incluir características específicas:

Target

Descripción

x86_64-linux-musl

Linux 64 bits, libc musl estática

x86_64-linux-gnu

Linux 64 bits, glibc dinámica

aarch64-linux-musl

ARM64 Linux (servidores AWS Graviton, Raspberry Pi 4)

x86_64-windows-gnu

Windows 64 bits, mingw-w64

aarch64-macos

macOS Apple Silicon

wasm32-freestanding

WebAssembly sin sistema operativo

riscv64-linux-musl

RISC-V 64 bits Linux

Cross-compilation con zig cc

Para proyectos C existentes, zig cc funciona como compilador C con soporte nativo de cross-compilation:

# Compilar un proyecto C para ARM64 Linux
zig cc -target aarch64-linux-musl programa.c -o programa-arm64

# Binario estático para distribución universal en Linux
zig cc -target x86_64-linux-musl -static programa.c -o programa-static

# Para Windows desde Linux
zig cc -target x86_64-windows-gnu programa.c -o programa.exe

El binario resultante con musl es estático: no depende de librerías del sistema en la máquina destino. Funciona en cualquier distribución Linux con el kernel suficientemente reciente, sin instalar nada adicional.

Configurar el target en build.zig

En un proyecto con build.zig, el target se puede pasar desde la línea de comandos o fijar en el build script:

const std = @import("std");

pub fn build(b: *std.Build) void {
    // El target viene de -Dtarget=... en la línea de comandos
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "mi-programa",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    b.installArtifact(exe);
}
# Compilar para tres plataformas
zig build -Dtarget=x86_64-linux-musl    -Doptimize=ReleaseFast
zig build -Dtarget=aarch64-linux-musl   -Doptimize=ReleaseFast
zig build -Dtarget=x86_64-windows-gnu   -Doptimize=ReleaseFast

Script de release multiplataforma

Un patrón habitual es generar binarios para todas las plataformas en un solo paso desde CI:

#!/bin/bash
# release.sh - generar binarios para todas las plataformas

APP="mi-programa"
VERSION="1.0.0"

TARGETS=(
    "x86_64-linux-musl"
    "aarch64-linux-musl"
    "x86_64-windows-gnu"
    "aarch64-macos"
)

for TARGET in "${TARGETS[@]}"; do
    echo "Compilando para $TARGET..."
    zig build -Dtarget=$TARGET -Doptimize=ReleaseFast
    mkdir -p "dist/${TARGET}"
    cp zig-out/bin/${APP}* dist/${TARGET}/
done

echo "Binarios generados en dist/"

Cross-compilation con proyectos C existentes

Si tienes un proyecto C con su propio sistema de build, puedes usar zig cc como compilador sustituto. La mayoría de proyectos que usan Make o CMake aceptan sobreescribir el compilador con una variable de entorno:

# Para proyectos con Make
CC="zig cc -target aarch64-linux-musl" make

# Para proyectos con CMake
cmake -DCMAKE_C_COMPILER="zig;cc;-target;aarch64-linux-musl" .
make

Comparación con otras soluciones

La alternativa más usada para cross-compilation en C es crosstool-NG o Docker con imágenes de toolchains específicos. En Go, la cross-compilation también es sencilla con GOOS y GOARCH, pero Go lleva su propio runtime. Zig produce binarios sin runtime, lo que da binarios más pequeños y sin dependencias en tiempo de ejecución.

Para Rust, la cross-compilation requiere rustup target add y en muchos casos instalar un linker adicional o usar cargo-cross. Zig lo resuelve de forma más limpia porque el linker es parte del compilador. Puedes ver más sobre las diferencias entre Zig y Go en cuanto a targets en el artículo sobre Go 1.26, y más sobre cómo Zig gestiona los proyectos multiplataforma en el artículo sobre el sistema de build.

Imagen: Pexels / Eylül Ku?dili

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP