build.zig em Zig — O que é e Como Usar
Definição
build.zig é o arquivo de configuração do sistema de build do Zig, escrito na própria linguagem Zig. Ele substitui ferramentas externas como Make, CMake ou Meson, permitindo que toda a lógica de compilação, testes, dependências e geração de artefatos seja expressa em código Zig normal. Isso significa que você tem acesso a toda a capacidade da linguagem (condicionais, loops, funções) para configurar seu build.
O sistema de build é executado pelo comando zig build, que compila e executa o build.zig para determinar o que fazer.
Por que build.zig Importa
- Sem ferramentas externas: O build system é parte do Zig — não precisa instalar Make, CMake, etc.
- Cross-compilation nativa: Configurar builds para diferentes plataformas é trivial.
- Integração C/C++: Pode compilar código C/C++ junto com Zig.
- Reprodutível: O mesmo
build.zigproduz resultados idênticos em qualquer máquina.
Exemplo Prático
build.zig Básico
const std = @import("std");
pub fn build(b: *std.Build) void {
// Opções de target e otimização
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Definir o 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());
const run_step = b.step("run", "Executar o aplicativo");
run_step.dependOn(&run_cmd.step);
// Step para 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", "Executar testes unitários");
test_step.dependOn(&run_testes.step);
}
build.zig com Dependência Externa
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Dependência declarada no build.zig.zon
const zap = b.dependency("zap", .{
.target = target,
.optimize = optimize,
});
const exe = b.addExecutable(.{
.name = "servidor",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// Adicionar módulo da dependência
exe.root_module.addImport("zap", zap.module("zap"));
b.installArtifact(exe);
}
build.zig com Biblioteca C
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "app-com-c",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// Linkar com biblioteca C do sistema
exe.linkSystemLibrary("sqlite3");
exe.linkLibC();
// Adicionar caminhos de include
exe.addIncludePath(b.path("include"));
b.installArtifact(exe);
}
Comandos Comuns
zig build # Compilar o projeto
zig build run # Compilar e executar
zig build test # Executar testes
zig build -Dtarget=x86_64-linux-gnu # Cross-compilation
zig build -Doptimize=ReleaseFast # Build otimizado
Gerando Código com o Build System
Uma capacidade avançada do build.zig é executar passos de geração de código — como gerar enums a partir de schemas, rodar ferramentas de code generation ou emitir arquivos de configuração:
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Executar um gerador de código como parte do build
const gerador = b.addExecutable(.{
.name = "gerador",
.root_source_file = b.path("tools/gerador.zig"),
.target = b.host, // roda no host, não no target
.optimize = .ReleaseFast,
});
const rodar_gerador = b.addRunArtifact(gerador);
rodar_gerador.addArg("src/gerado.zig");
const exe = b.addExecutable(.{
.name = "app",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// O executável depende do passo de geração
exe.step.dependOn(&rodar_gerador.step);
b.installArtifact(exe);
}
Comparação com Outras Ferramentas de Build
| Ferramenta | Linguagem de config | Tipagem | Dependência externa |
|---|---|---|---|
| build.zig | Zig | Forte, estática | Não |
| CMake | DSL CMake | Fraca | Sim |
| Makefile | Make DSL | Nenhuma | Sim |
| Cargo | TOML + scripts Rust | Parcial | Não |
| Gradle | Groovy/Kotlin DSL | Parcial | JVM |
A principal vantagem do build.zig é que toda a lógica de build é código Zig real — com acesso a loops, condicionais, funções e o sistema de tipos completo da linguagem, sem aprender uma DSL separada.
Boas Práticas
- Sempre use
b.standardTargetOptionseb.standardOptimizeOption: Isso expõe flags-Dtargete-Doptimizepara o usuário sem você precisar declará-las manualmente. - Separe steps por função: Use
b.step("lint", "..."),b.step("docs", "...")eb.step("bench", "...")para organizar tarefas além de build e test. - Use
b.hostpara ferramentas de build: Ferramentas que rodam durante o build (geradores de código, formatadores) devem ter targetb.host, não o target do projeto. - Versione o
build.zig.zon: Esse arquivo declara dependências e o hash de cada pacote — faça commit dele junto com obuild.zig.
Armadilhas Comuns
- Esquecer b.installArtifact: Sem instalar o artefato,
zig buildnão produz saída. - Caminhos relativos: Use
b.path()para caminhos de arquivo, nunca strings de caminho absoluto. - Ordem de dependências: Steps devem declarar dependências com
dependOnpara garantir a ordem correta. - Confundir target e host: O build.zig roda no host, mas configura o target. Não confunda os dois.
Termos Relacionados
- build.zig.zon — Manifesto de pacote com dependências
- zig build — Comando para executar o sistema de build
- Cross-Compilation — Compilação para outras plataformas
- Target Triple — Especificação de plataforma-alvo
- Release Modes — Modos de otimização