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
