Introdução
CMake é o sistema de build mais usado em projetos C/C++. Embora poderoso, sua linguagem de script é complexa, com sintaxe pouco intuitiva e comportamento que varia entre versões. O build.zig de Zig oferece a mesma funcionalidade em uma linguagem de programação real, com tipagem e tooling adequados.
Este guia converte padrões CMake comuns para build.zig. Para Makefile, veja Migrar de Makefile para build.zig. Para o sistema de build em geral, consulte Zig Build System.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja Como Instalar Zig
- Projeto C/C++ existente com CMakeLists.txt
- Familiaridade básica com Zig
Projeto Básico
CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MeuProjeto C)
set(CMAKE_C_STANDARD 11)
add_executable(meu-programa
src/main.c
src/utils.c
src/parser.c
)
target_include_directories(meu-programa PRIVATE include)
target_compile_options(meu-programa PRIVATE -Wall -Wextra)
target_link_libraries(meu-programa m pthread)
build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "meu-programa",
.target = target,
.optimize = optimize,
});
exe.addCSourceFiles(.{
.files = &.{
"src/main.c",
"src/utils.c",
"src/parser.c",
},
.flags = &.{ "-Wall", "-Wextra", "-std=c11" },
});
exe.addIncludePath(b.path("include"));
exe.linkLibC();
exe.linkSystemLibrary("m");
exe.linkSystemLibrary("pthread");
b.installArtifact(exe);
}
Mapeamento de Comandos
| CMake | build.zig |
|---|---|
project(nome) | .name = "nome" em addExecutable |
add_executable | b.addExecutable(...) |
add_library(STATIC) | b.addStaticLibrary(...) |
add_library(SHARED) | b.addSharedLibrary(...) |
target_include_directories | exe.addIncludePath(...) |
target_link_libraries | exe.linkSystemLibrary(...) |
target_compile_definitions | exe.defineCMacro(...) |
target_compile_options | Via .flags em addCSourceFiles |
find_package | exe.linkSystemLibrary(...) |
option(...) | b.option(...) |
install(TARGETS) | b.installArtifact(...) |
enable_testing() | b.addTest(...) |
set(CMAKE_BUILD_TYPE) | b.standardOptimizeOption(.{}) |
find_package e Dependências
CMake
find_package(OpenSSL REQUIRED)
find_package(ZLIB REQUIRED)
find_package(Threads REQUIRED)
target_link_libraries(meu-programa
OpenSSL::SSL
OpenSSL::Crypto
ZLIB::ZLIB
Threads::Threads
)
build.zig
exe.linkSystemLibrary("ssl");
exe.linkSystemLibrary("crypto");
exe.linkSystemLibrary("z");
exe.linkLibC(); // inclui pthreads via libc
Zig não precisa de módulos Find separados — linkSystemLibrary busca bibliotecas do sistema diretamente via pkg-config ou caminhos padrão.
Opções de Build
CMake
option(ENABLE_LOGGING "Ativar logging" ON)
option(USE_OPENSSL "Usar OpenSSL" OFF)
if(ENABLE_LOGGING)
target_compile_definitions(meu-programa PRIVATE ENABLE_LOGGING)
endif()
if(USE_OPENSSL)
find_package(OpenSSL REQUIRED)
target_link_libraries(meu-programa OpenSSL::SSL)
endif()
build.zig
const enable_logging = b.option(bool, "logging", "Ativar logging") orelse true;
const use_openssl = b.option(bool, "openssl", "Usar OpenSSL") orelse false;
if (enable_logging) {
exe.defineCMacro("ENABLE_LOGGING", null);
}
if (use_openssl) {
exe.linkSystemLibrary("ssl");
exe.linkSystemLibrary("crypto");
}
zig build -Dlogging=true -Dopenssl=true
Subdirectories e Targets Múltiplos
CMake
# CMakeLists.txt raiz
add_subdirectory(libs/utils)
add_subdirectory(libs/parser)
add_subdirectory(src)
# libs/utils/CMakeLists.txt
add_library(utils STATIC utils.c)
target_include_directories(utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# src/CMakeLists.txt
add_executable(programa main.c)
target_link_libraries(programa utils parser)
build.zig
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Biblioteca utils
const utils = b.addStaticLibrary(.{
.name = "utils",
.target = target,
.optimize = optimize,
});
utils.addCSourceFile(.{
.file = b.path("libs/utils/utils.c"),
.flags = &.{"-std=c11"},
});
utils.addIncludePath(b.path("libs/utils"));
// Biblioteca parser
const parser = b.addStaticLibrary(.{
.name = "parser",
.target = target,
.optimize = optimize,
});
parser.addCSourceFile(.{
.file = b.path("libs/parser/parser.c"),
.flags = &.{"-std=c11"},
});
parser.addIncludePath(b.path("libs/parser"));
// Executável principal
const exe = b.addExecutable(.{
.name = "programa",
.target = target,
.optimize = optimize,
});
exe.addCSourceFile(.{
.file = b.path("src/main.c"),
.flags = &.{"-std=c11"},
});
exe.linkLibrary(utils);
exe.linkLibrary(parser);
exe.linkLibC();
b.installArtifact(exe);
}
Testes
CMake
enable_testing()
add_executable(test_utils tests/test_utils.c)
target_link_libraries(test_utils utils cmocka)
add_test(NAME TestUtils COMMAND test_utils)
build.zig
// Para testes Zig nativos
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");
test_step.dependOn(&run_testes.step);
// Para testes C existentes
const test_c = b.addExecutable(.{
.name = "test_utils",
.target = target,
.optimize = optimize,
});
test_c.addCSourceFile(.{
.file = b.path("tests/test_utils.c"),
.flags = &.{"-std=c11"},
});
test_c.linkLibrary(utils);
test_c.linkSystemLibrary("cmocka");
test_c.linkLibC();
const run_test_c = b.addRunArtifact(test_c);
test_step.dependOn(&run_test_c.step);
Veja Testes Unitários.
Instalação
CMake
install(TARGETS programa DESTINATION bin)
install(FILES include/minha_lib.h DESTINATION include)
install(TARGETS utils DESTINATION lib)
build.zig
b.installArtifact(exe);
b.installArtifact(utils);
b.installFile("include/minha_lib.h", "include/minha_lib.h");
Cross-compilation
CMake
# Requer toolchain file externo
# cmake -DCMAKE_TOOLCHAIN_FILE=toolchain-arm.cmake ..
# toolchain-arm.cmake:
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
build.zig
# Uma flag, sem arquivo de toolchain:
zig build -Dtarget=arm-linux-gnueabihf
zig build -Dtarget=aarch64-linux-gnu
zig build -Dtarget=x86_64-windows-gnu
Veja Cross-compilation em Zig.
Vantagens sobre CMake
- Linguagem real: build.zig é Zig, com tipagem, autocompletion e debugging
- Sem versões incompatíveis: CMake 3.x tem comportamento que varia entre versões
- Cross-compilation trivial: Uma flag vs arquivos de toolchain
- Sem dependências: Não precisa instalar CMake, ninja, ou geradores
- Cache transparente: Sem diretório
build/opaco - Reproduzível: Mesmo resultado em qualquer máquina com Zig instalado
Conclusão
A migração de CMake para build.zig simplifica significativamente o processo de build. A maior vantagem é usar uma linguagem de programação real em vez de scripts CMake, com cross-compilation integrada e sem dependências externas.
Para projetos mistos C/Zig, veja Interoperabilidade C-Zig. Para migração de código, consulte Guia de Migração: C para Zig.