Makefiles y CMake files pueden ser complicados de entender y requieren de un buen conocimiento técnico, especialmente cuando intentas compilar tu proyecto C/C++ en diferentes plataformas. Afortunadamente, Zig ofrece una solución: una toolchain completa que incluye un sistema de construcción integrado, zig build. Este sistema no solo simplifica el proceso de build, sino que también elimina dependencias externas y facilita la compilación cruzada. En este artículo, veremos cómo usar zig build para gestionar tus proyectos C/C++.

¿Compilación cruzada?

La compilación cruzada es como traducir tu programa a otro idioma antes de enviarlo. Significa que puedes compilar tu código en tu computadora (por ejemplo, una Mac o Windows) para que funcione en otro tipo de computadora o sistema operativo (como un servidor Linux o un dispositivo ARM). Normalmente, esto es difícil porque necesitas herramientas especiales para cada "traducción". Zig lo hace mucho más fácil porque puede "traducir" a muchos sistemas diferentes con una sola herramienta.

Un ejemplo claro es Uber: necesitaban que su código funcionara en servidores arm64, una arquitectura diferente a la que usaban para desarrollar. En lugar de lidiar con múltiples toolchains y configuraciones complejas, adoptaron Zig para la compilación cruzada. Esto les permitió crear una toolchain hermética y consistente, compilando para ambas arquitecturas (x86_64 y arm64) desde un único punto, simplificando su infraestructura y reduciendo costos. Zig les proporcionó una solución limpia y eficiente para un problema que tradicionalmente es bastante engorroso.

Fuente: Bootstrapping Uber’s Infrastructure on arm64 with Zig

Además, la versatilidad de Zig como sistema de Build ha llevado a que varias bibliotecas de C hayan migrado sus sistemas de Build a Zig, facilitando su uso en otros proyectos y su compilación. Algunos ejemplos notables incluyen:

Construyendo tu Primer Proyecto C++ con Zig

Ahora que hemos visto cómo Zig facilita la compilación cruzada, veamos lo sencillo que es usarlo como tu sistema de construcción para proyectos C/C++. Para empezar, crearemos dos archivos: build.zig y main.cpp.

El archivo build.zig define cómo se construirá nuestro proyecto:

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const mode = b.standardOptimizeOption(.{});

    const module = b.addModule("main", .{
        .target = target,
        .optimize = mode,
        .link_libc = true,
    });

    module.addCSourceFile(.{ // Cambiamos addCSourceFile a addCppSourceFile
        .file = b.path("main.cpp"),
        .flags = &[_][]const u8{"-std=c++17"}, // Usamos un estándar de C++
    });

    const exe = b.addExecutable(.{
        .name = "hello",
        .root_module = module,
    });

    b.installArtifact(exe);
}

Y nuestro simple programa en C++, main.cpp:

#include <iostream>

int main() {
  std::cout << "Hola desde el Sistema de Construcción de Zig!" << std::endl;
  return 0;
}

Para compilar este proyecto, simplemente ejecuta el siguiente comando en tu terminal:

zig build

Este comando compilará el archivo main.cpp y lo enlazará para crear el ejecutable hello. Lo encontrarás en la carpeta zig-out/bin, dentro de tu directorio de trabajo actual. Si ejecutas el ejecutable con ./zig-out/bin/hello, verás el siguiente mensaje:

Hola desde el Sistema de Construcción de Zig!

¡Y eso es todo! Con unos pocos pasos sencillos, has utilizado Zig para construir un programa en C++. Veremos cómo este enfoque declarativo y sin dependencias puede simplificar la gestión de proyectos C/C++ más complejos.

Compilación Cruzada con zig build -Dtarget

Si quisieras compilar este mismo programa para una arquitectura diferente, como aarch64-linux-gnu (ARM64 para Linux), puedes usar la opción -Dtarget al ejecutar zig build. El comando sería:

zig build -Dtarget=aarch64-linux-gnu

La opción -Dtarget le indica a Zig que compile el programa para la arquitectura especificada, permitiéndote generar ejecutables para diferentes plataformas desde la misma máquina, sin necesidad de configuraciones adicionales. Esto es especialmente útil para desarrolladores que necesitan crear versiones de su software para múltiples sistemas operativos o arquitecturas.

Si bien no hay una lista exhaustiva y oficial de todos los posibles valores para -Dtarget que funcione en todas las versiones de Zig, puedes obtener una lista de los objetivos soportados por tu instalación actual de Zig con el siguiente comando:

zig targets

Este comando te mostrará una lista detallada de arquitecturas, sistemas operativos, ABI (Application Binary Interface) y otros detalles relevantes para la compilación.

En general, la sintaxis para -Dtarget es la siguiente:

arquitectura-sistemaoperativo-abi

Algunos ejemplos comunes incluyen:

  • x86_64-linux-gnu: Arquitectura x86 de 64 bits, Linux, ABI GNU (glibc).
  • aarch64-linux-gnu: Arquitectura ARM de 64 bits, Linux, ABI GNU (glibc).
  • x86_64-windows-gnu: Arquitectura x86 de 64 bits, Windows, ABI GNU.
  • x86_64-macos-none: Arquitectura x86 de 64 bits, macOS, sin ABI específica.
  • aarch64-macos-none: Arquitectura ARM de 64 bits (Apple Silicon), macOS, sin ABI específica.
  • wasm32-freestanding-none: WebAssembly de 32 bits, sin sistema operativo, sin ABI específica.

¡Muchas gracias por leer hasta aqui! Próximamente abordaremos la compilación de la libreria AK de SerenityOS y Ladybird en reemplazo a STL de C++.

Categorizado en:

C/C++, Build Systems, Zig

Última Actualización: febrero 17, 2025