Cheatsheet: Build System em Zig

Cheatsheet: Build System em Zig

O build system do Zig é escrito em Zig puro — o arquivo build.zig é um programa Zig normal que descreve como seu projeto deve ser compilado. Isso significa que você tem toda a expressividade da linguagem para configurar builds complexos, sem precisar de ferramentas externas como CMake, Makefiles ou scripts shell.

build.zig Básico

const std = @import("std");

pub fn build(b: *std.Build) void {
    // Opções padrão de target e otimização
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // Compilar executável
    const exe = b.addExecutable(.{
        .name = "meu-app",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    // Instalar o artefato
    b.installArtifact(exe);

    // Step para rodar o executável
    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    const run_step = b.step("run", "Executar o aplicativo");
    run_step.dependOn(&run_cmd.step);

    // Step de testes
    const testes = b.addTest(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    const run_testes = b.addRunArtifact(testes);
    const test_step = b.step("test", "Rodar testes unitários");
    test_step.dependOn(&run_testes.step);
}

Estrutura Padrão de Projeto

meu-projeto/
├── build.zig          # Configuração de build
├── build.zig.zon      # Manifesto de dependências
├── src/
│   ├── main.zig       # Ponto de entrada
│   ├── lib.zig        # Biblioteca
│   └── utils.zig      # Módulos auxiliares
└── tests/
    └── test_main.zig  # Testes adicionais

Comandos de Build

# Compilar o projeto (modo debug)
zig build

# Compilar e rodar
zig build run

# Rodar testes
zig build test

# Compilar em modo release
zig build -Doptimize=ReleaseSafe
zig build -Doptimize=ReleaseFast
zig build -Doptimize=ReleaseSmall

# Cross-compile para outro target
zig build -Dtarget=x86_64-linux-gnu
zig build -Dtarget=aarch64-macos
zig build -Dtarget=x86_64-windows-msvc

# Listar todos os steps disponíveis
zig build --help

# Verbose — ver comandos executados
zig build --verbose

Modos de Otimização

ModoFlagDescrição
Debug(padrão)Sem otimização, asserts ativados, stack traces
ReleaseSafe-Doptimize=ReleaseSafeOtimizado, com verificações de segurança
ReleaseFast-Doptimize=ReleaseFastMáxima performance, sem verificações
ReleaseSmall-Doptimize=ReleaseSmallMenor binário possível

Compilar Biblioteca

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

    // Biblioteca estática
    const lib = b.addStaticLibrary(.{
        .name = "minha-lib",
        .root_source_file = b.path("src/lib.zig"),
        .target = target,
        .optimize = optimize,
    });
    b.installArtifact(lib);

    // Biblioteca dinâmica (shared)
    const shared = b.addSharedLibrary(.{
        .name = "minha-lib",
        .root_source_file = b.path("src/lib.zig"),
        .target = target,
        .optimize = optimize,
    });
    b.installArtifact(shared);
}

Dependências Externas (build.zig.zon)

Manifesto de dependências

// build.zig.zon
.{
    .name = "meu-projeto",
    .version = "0.1.0",
    .dependencies = .{
        .zap = .{
            .url = "https://github.com/zigzap/zap/archive/v0.1.0.tar.gz",
            .hash = "1220abcdef1234567890...",
        },
        .mach = .{
            .url = "https://github.com/hexops/mach/archive/main.tar.gz",
            .hash = "1220fedcba0987654321...",
        },
    },
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
    },
}

Usando dependências no build.zig

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

    // Obter dependência declarada no .zon
    const zap_dep = b.dependency("zap", .{
        .target = target,
        .optimize = optimize,
    });

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

    // Adicionar módulo da dependência
    exe.root_module.addImport("zap", zap_dep.module("zap"));

    b.installArtifact(exe);
}

Buscar hash de dependência

zig fetch https://github.com/exemplo/lib/archive/v1.0.tar.gz
# Retorna o hash para usar no .zon

Linkar com Bibliotecas C

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

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

    // Linkar com libc
    exe.linkLibC();

    // Linkar com biblioteca do sistema
    exe.linkSystemLibrary("SDL2");
    exe.linkSystemLibrary("openssl");

    // Adicionar caminhos de include e lib
    exe.addIncludePath(b.path("vendor/include"));
    exe.addLibraryPath(b.path("vendor/lib"));

    // Compilar arquivos C junto
    exe.addCSourceFiles(.{
        .files = &.{
            "src/wrapper.c",
            "src/utils.c",
        },
        .flags = &.{
            "-Wall",
            "-Wextra",
            "-std=c11",
        },
    });

    b.installArtifact(exe);
}

Opções de Build Customizadas

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

    // Opção booleana
    const habilitar_logs = b.option(bool, "logs", "Habilitar logging verbose") orelse false;

    // Opção string
    const porta = b.option(u16, "porta", "Porta do servidor") orelse 8080;

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

    // Passar opções como constantes comptime
    const opcoes = b.addOptions();
    opcoes.addOption(bool, "habilitar_logs", habilitar_logs);
    opcoes.addOption(u16, "porta", porta);
    exe.root_module.addOptions("config", opcoes);

    b.installArtifact(exe);
}

No código fonte:

const config = @import("config");

pub fn main() !void {
    if (config.habilitar_logs) {
        std.log.info("Logs habilitados", .{});
    }
    const porta = config.porta;
    // ...
}

Uso:

zig build -Dlogs=true -Dporta=3000

Steps Customizados

pub fn build(b: *std.Build) void {
    // Step que roda um comando do sistema
    const gerar_docs = b.addSystemCommand(&.{
        "python3", "scripts/gerar_docs.py",
    });

    const docs_step = b.step("docs", "Gerar documentação");
    docs_step.dependOn(&gerar_docs.step);

    // Step que depende de outro
    const deploy = b.addSystemCommand(&.{
        "rsync", "-avz", "zig-out/", "servidor:/opt/app/",
    });
    deploy.step.dependOn(b.getInstallStep()); // primeiro compila

    const deploy_step = b.step("deploy", "Deploy para servidor");
    deploy_step.dependOn(&deploy.step);
}

Compilação sem build.zig

Para projetos simples, você pode compilar diretamente:

# Compilar arquivo único
zig build-exe src/main.zig

# Com otimização
zig build-exe src/main.zig -OReleaseFast

# Cross-compile
zig build-exe src/main.zig -target x86_64-linux-gnu

# Compilar biblioteca
zig build-lib src/lib.zig

# Compilar objeto
zig build-obj src/modulo.zig

# Compilar e rodar diretamente
zig run src/main.zig

Erros Comuns

# ERRO: "FileNotFound" no root_source_file
# Verifique se o caminho está correto: b.path("src/main.zig")

# ERRO: hash inválido no .zon
# Execute: zig fetch <URL> para obter o hash correto

# ERRO: "DependencyNotFound"
# Verifique se o nome no b.dependency() bate com o .zon

# ERRO: Biblioteca C não encontrada
# Instale os pacotes -dev: apt install libsdl2-dev

Veja Também

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.