Zig Packaging: Módulos e Dependências — Guia Completo

O sistema de pacotes do Zig é uma das features mais recentes e poderosas da linguagem. Introduzido na versão 0.11.0 e aprimorado nas versões subsequentes, o Zig Package Manager permite que você compartilhe código, gerencie dependências e crie módulos reutilizáveis de forma elegante — tudo sem precisar de ferramentas externas como npm, pip, cargo ou vcpkg.

Neste tutorial completo, você vai aprender tudo sobre o build.zig.zon (o arquivo de manifesto de pacotes), como adicionar dependências de repositórios remotos ou locais, criar seus próprios módulos e publicar pacotes para a comunidade.

Pré-requisito: Ter o Zig instalado e conhecer o básico do Zig Build System. Se você ainda não está familiarizado com o build.zig, recomendamos começar por lá.

O que é o Zig Package Manager?

Evolução do Gerenciamento de Pacotes no Zig

Antes do sistema de pacotes nativo, desenvolvedores Zig dependiam de:

  • Git submodules — frágeis, difíceis de atualizar
  • Vendoring — copiar código manualmente, sem versionamento
  • System package managers — não portáveis entre plataformas
  • Conan/vcpkg — ferramentas externas, complexas para projetos simples

O Zig Package Manager resolve todos esses problemas com uma solução integrada, simples e portátil.

Componentes do Sistema de Pacotes

ComponenteDescrição
build.zig.zonManifesto do pacote (metadados + dependências)
zig fetchComando para baixar e adicionar dependências
zig build --fetchBaixa todas as dependências declaradas
b.dependency()API no build.zig para acessar dependências
b.createModule()Cria módulos reutilizáveis
Cache global.cache/zig/ armazena dependências baixadas

Arquitetura: Como Funciona

Seu Projeto
├── build.zig          # Configuração do build
├── build.zig.zon      # Manifesto (nome, versão, deps)
└── src/
    └── main.zig

Dependências (em ~/.cache/zig/)
├── p/                   # Pacotes baixados
   ├── 1220abc.../      # Hash único do pacote
      ├── build.zig
      └── src/
   └── 1220def.../
└── tmp/                 # Downloads temporários

O sistema usa content-addressable storage — cada pacote é identificado pelo hash do seu conteúdo, garantindo integridade e reproducibilidade.

O Arquivo build.zig.zon — Manifesto do Pacote

O build.zig.zon (Zig Object Notation) é o coração do sistema de pacotes. É um arquivo de configuração declarativo que define metadados do seu projeto e suas dependências.

Estrutura Básica

.{
    .name = .@"meu-projeto",
    .version = "0.1.0",
    .fingerprint = 0x1234abcd,  // Gerado automaticamente
    .minimum_zig_version = "0.15.0",
    .dependencies = .{},
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
        "LICENSE",
        "README.md",
    },
}

Campos do build.zig.zon

CampoTipoObrigatórioDescrição
.nameenumNome do pacote (prefixado com @)
.versionstringVersão semântica (semver)
.fingerprintintegerHash único do pacote (gerado por zig build)
.minimum_zig_versionstringVersão mínima do Zig necessária
.dependenciesstructDependências do projeto
.pathsarrayArquivos/pastas incluídos no pacote

O Campo .name

O nome do pacote usa um enum prefixado para evitar colisões:

.{
    // ✅ Correto — usa prefixo @
    .name = .@"minha-lib",
    
    // ✅ Correto — sem hífen, sem problema
    .name = .minhalib,
    
    // ❌ Incorreto — hífen sem prefixo
    // .name = .minha-lib,  // Erro de sintaxe!
}

Boas práticas para nomes:

  • Use nomes descritivos e únicos
  • Prefira minha-lib sobre lib genérico
  • Verifique se o nome já existe em repositórios populares

O Campo .fingerprint

O fingerprint é um hash único gerado automaticamente pelo Zig:

.{
    .name = .@"meu-projeto",
    .version = "0.1.0",
    .fingerprint = 0x9b80,  // Gerado por `zig build`
    // ...
}

⚠️ Nunca altere manualmente o fingerprint. Ele garante que pacotes com nomes similares não colidam.

O Campo .paths

Define quais arquivos são incluídos quando o pacote é distribuído:

.{
    .paths = .{
        // Arquivos essenciais
        "build.zig",
        "build.zig.zon",
        
        // Código-fonte
        "src",
        
        // Documentação
        "README.md",
        "LICENSE",
        "CHANGELOG.md",
        
        // Assets (opcional)
        "assets",
    },
}

O que NÃO incluir:

.paths = .{
    // ❌ Não incluir arquivos gerados
    // ".zig-cache",      // Cache de compilação
    // "zig-out",         // Artefatos de build
    
    // ❌ Não incluir arquivos de desenvolvimento
    // ".git",            // Repositório git
    // ".github",         // CI configs
    // "tests/fixtures",  // Arquivos de teste grandes
    
    // ❌ Não incluir secrets
    // ".env",            // Variáveis de ambiente
    // "secrets.json",    // Chaves privadas
}

Exemplo Completo: Manifesto de Biblioteca

.{
    .name = .@"zig-json-parser",
    .version = "1.2.0",
    .fingerprint = 0x8f3a2b1c,
    .minimum_zig_version = "0.15.0",
    
    .dependencies = .{
        // Dependências serão adicionadas aqui
    },
    
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
        "README.md",
        "LICENSE",
        "CHANGELOG.md",
    },
}

Adicionando Dependências

Método 1: Usando zig fetch --save

A forma mais simples e recomendada de adicionar uma dependência:

# Adicionar de uma URL
zig fetch --save https://github.com/user/repo/archive/refs/tags/v1.0.0.tar.gz

# Adicionar de um repositório Git
zig fetch --save git+https://github.com/user/repo#v1.0.0

# Adicionar do GitHub (sintaxe simplificada)
zig fetch --save git+https://github.com/hendriknielaender/zBench

O que acontece:

  1. O Zig baixa o pacote
  2. Calcula o hash de integridade
  3. Atualiza automaticamente o build.zig.zon
  4. Armazena no cache global (~/.cache/zig/)

Exemplo de build.zig.zon após zig fetch:

.{
    .name = .@"meu-projeto",
    .version = "0.1.0",
    .dependencies = .{
        .zbench = .{
            .url = "git+https://github.com/hendriknielaender/zBench#v0.9.2",
            .hash = "1220c4c79d6a7c7713b6c7391c96276b9b4f72b3b8d4f6c4e3e1a2b3c4d5e6f7a8b9c",
        },
    },
    .paths = .{ "build.zig", "build.zig.zon", "src" },
}

Método 2: Edição Manual do build.zig.zon

Para maior controle, você pode editar manualmente:

.{
    .dependencies = .{
        // Dependência de tarball
        .minha_dep = .{
            .url = "https://example.com/lib-1.0.0.tar.gz",
            .hash = "1220abc123...",  // Hash obrigatório!
        },
        
        // Dependência Git com tag específica
        .outra_dep = .{
            .url = "git+https://github.com/user/repo#v2.0.0",
            .hash = "1220def456...",
        },
        
        // Dependência Git com commit específico
        .dep_commit = .{
            .url = "git+https://github.com/user/repo#abc123def",
            .hash = "1220abc789...",
        },
        
        // Dependência Git (main branch — não recomendado para produção)
        .dep_latest = .{
            .url = "git+https://github.com/user/repo",
            .hash = "1220fff000...",
        },
    },
}

⚠️ Aviso: O hash é obrigatório para garantir integridade. Para obtê-lo, execute zig build --fetch após adicionar a URL.

Método 3: Dependências Locais (Path)

Para desenvolvimento local ou monorepos:

.{
    .dependencies = .{
        // Dependência em caminho relativo
        .minha_lib_local = .{
            .path = "../minha-lib",
        },
        
        // Dependência em caminho absoluto
        .outra_lib = .{
            .path = "/home/user/projetos/outra-lib",
        },
    },
}

Cenários comuns para dependências locais:

  1. Monorepo: Vários pacotes no mesmo repositório
  2. Desenvolvimento simultâneo: Editando lib e app ao mesmo tempo
  3. Forks locais: Modificando uma dependência
  4. Projetos privados: Código que não pode ser publicado

Configurando Dependências no build.zig

Declarar no build.zig.zon não é suficiente — você também precisa configurar no build.zig:

const std = @import("std");

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

    // === Configurar dependências ===
    
    // Obtém a dependência declarada no build.zig.zon
    const zbench = b.dependency("zbench", .{
        .target = target,
        .optimize = optimize,
    });

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

    // === Adicionar módulos da dependência ===
    
    // Adiciona o módulo "zbench" da dependência
    exe.root_module.addImport("zbench", zbench.module("zbench"));

    b.installArtifact(exe);
    
    // ... resto do build.zig
}

Fluxo de dados:

build.zig.zon          build.zig                Código Zig
     │                      │                         │
     │  Declara dep        │  Obtém com              │  Usa com
     │  "zbench"           │  b.dependency()         │  @import()
     ▼                      ▼                         ▼
┌─────────┐           ┌─────────┐             ┌─────────────┐
│ .zbench │──────────▶│ zbench  │────────────▶│ @import()   │
│  = {...}│           │ module  │             │ ("zbench")  │
└─────────┘           └─────────┘             └─────────────┘

Exemplo Prático: Adicionando uma Biblioteca de Logging

Vamos adicionar a biblioteca zul (Zig Utility Library) passo a passo:

Passo 1: Buscar a dependência

zig fetch --save git+https://github.com/vsrinivas/zul

Passo 2: Verificar o build.zig.zon atualizado

.{
    .name = .@"meu-app",
    .version = "0.1.0",
    .dependencies = .{
        .zul = .{
            .url = "git+https://github.com/vsrinivas/zul#v0.3.2",
            .hash = "1220a1b2c3d4e5f6...",
        },
    },
    .paths = .{ "build.zig", "build.zig.zon", "src" },
}

Passo 3: Configurar no build.zig

const std = @import("std");

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

    // Obtém a dependência zul
    const zul = b.dependency("zul", .{
        .target = target,
        .optimize = optimize,
    });

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

    // Adiciona o módulo "zul"
    exe.root_module.addImport("zul", zul.module("zul"));

    b.installArtifact(exe);
    
    // Steps run e test...
    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());
    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);
}

Passo 4: Usar no código Zig

const std = @import("std");
const zul = @import("zul");

pub fn main() !void {
    // Usa a biblioteca zul
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Exemplo: parsing de UUID
    const uuid = try zul.UUID.parse("550e8400-e29b-41d4-a716-446655440000");
    std.debug.print("UUID: {s}\n", .{uuid});
}

Gerenciamento de Versões

Versionamento Semântico (Semver)

O Zig usa semver para versionamento de pacotes:

MAJOR.MINOR.PATCH

1.2.3
│   │   │
│   │   └── Correções de bugs (backward compatible)
│   │
│   └────── Novas features (backward compatible)
└────────── Breaking changes

Exemplos:

.version = "0.1.0",   // Desenvolvimento inicial
.version = "1.0.0",   // Primeiro release estável
.version = "1.2.0",   // Nova feature backward compatible
.version = "2.0.0",   // Breaking changes

Atualizando Dependências

Para atualizar uma dependência para uma nova versão:

# 1. Remover a dependência antiga do build.zig.zon (opcional)
# 2. Adicionar a nova versão
zig fetch --save git+https://github.com/user/repo#v2.0.0

# 3. Rebuild para usar a nova versão
zig build

Comparação com outras ferramentas:

FerramentaComando de Update
Zigzig fetch --save <url>
Cargocargo update
npmnpm update
pippip install --upgrade

Pinning de Versões

Para garantir builds reproduzíveis, sempre use versões específicas:

// ✅ Bom — versão específica
.url = "git+https://github.com/user/repo#v1.2.3",

// ✅ Bom — commit específico (ainda mais seguro)
.url = "git+https://github.com/user/repo#abc123def",

// ⚠️ Cuidado — pode quebrar a qualquer momento
.url = "git+https://github.com/user/repo#main",

// ❌ Ruim — sem controle de versão
.url = "https://example.com/latest.tar.gz",

Resolvendo Conflitos de Versão

Quando duas dependências requerem versões incompatíveis:

// Pacote A requer lib X v1.x
// Pacote B requer lib X v2.x (breaking changes)

Soluções:

  1. Atualizar Pacote A para versão compatível com X v2.x
  2. Fork e adaptar uma das dependências
  3. Remover uma dependência e usar alternativa
  4. Contribuir upstream para resolver o conflito

O Zig não tem resolução automática de conflitos — você deve gerenciá-los manualmente.

Criando Módulos Reutilizáveis

O que é um Módulo Zig?

Um módulo é uma unidade de código compilável que pode ser:

  • Importada em outros projetos via @import()
  • Compartilhada como dependência
  • Testada isoladamente

Estrutura de uma Biblioteca (Library)

minha-lib/
├── build.zig           # Configuração do build
├── build.zig.zon       # Manifesto do pacote
├── src/
│   └── root.zig        # Ponto de entrada da lib
├── README.md
└── LICENSE

Criando uma Biblioteca do Zero

Passo 1: Inicializar o projeto

mkdir minha-lib && cd minha-lib
zig init

Isso cria a estrutura básica incluindo src/root.zig.

Passo 2: Definir a API pública

No src/root.zig, exporte apenas o que deve ser público:

// src/root.zig
const std = @import("std");

// === API Pública ===

/// Estrutura de dados principal
pub const Config = struct {
    name: []const u8,
    value: i32,
};

/// Função pública
pub fn init(name: []const u8, value: i32) Config {
    return .{
        .name = name,
        .value = value,
    };
}

/// Processa a configuração
pub fn process(config: Config) !void {
    std.debug.print("Processando {s}: {d}\n", .{
        config.name, config.value,
    });
}

// === Interno (não exportado) ===

// Funções privadas começam sem 'pub'
fn helper() void {
    // implementação interna
}

// Testes inline
const testing = std.testing;

test "Config init" {
    const cfg = init("teste", 42);
    try testing.expectEqualStrings("teste", cfg.name);
    try testing.expectEqual(42, cfg.value);
}

Passo 3: Configurar o build.zig

const std = @import("std");

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

    // === Biblioteca ===
    const lib = b.addLibrary(.{
        .name = "minha-lib",
        .linkage = .static,  // ou .dynamic
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/root.zig"),
            .target = target,
            .optimize = optimize,
        }),
    });

    b.installArtifact(lib);

    // === Testes ===
    const lib_unit_tests = b.addTest(.{
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/root.zig"),
            .target = target,
            .optimize = optimize,
        }),
    });

    const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
    const test_step = b.step("test", "Run unit tests");
    test_step.dependOn(&run_lib_unit_tests.step);
}

Passo 4: Configurar o build.zig.zon

.{
    .name = .@"minha-lib",
    .version = "0.1.0",
    .fingerprint = 0x1234abcd,
    .minimum_zig_version = "0.15.0",
    .dependencies = .{},
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
        "README.md",
        "LICENSE",
    },
}

Múltiplos Módulos em um Pacote

Um pacote pode expor vários módulos:

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

    // Módulo principal
    const lib = b.addLibrary(.{
        .name = "minha-lib",
        .linkage = .static,
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/root.zig"),
            .target = target,
            .optimize = optimize,
        }),
    });

    // Módulo adicional: utils
    const utils_mod = b.createModule(.{
        .root_source_file = b.path("src/utils.zig"),
        .target = target,
        .optimize = optimize,
    });
    lib.root_module.addImport("utils", utils_mod);

    // Módulo adicional: crypto
    const crypto_mod = b.createModule(.{
        .root_source_file = b.path("src/crypto.zig"),
        .target = target,
        .optimize = optimize,
    });
    lib.root_module.addImport("crypto", crypto_mod);

    b.installArtifact(lib);
}

Uso no código cliente:

const minha_lib = @import("minha-lib");
const utils = @import("utils");
const crypto = @import("crypto");

Organizando um Pacote Complexo

Para bibliotecas maiores, organize assim:

minha-lib/
├── build.zig
├── build.zig.zon
├── src/
   ├── root.zig          # Re-exporta tudo
   ├── core/
      ├── mod.zig       # Módulo core
      ├── types.zig
      └── error.zig
   ├── io/
      ├── mod.zig       # Módulo io
      ├── reader.zig
      └── writer.zig
   └── utils/
       ├── mod.zig       # Módulo utils
       └── string.zig
├── tests/
   └── integration.zig
├── examples/
   └── basic.zig
├── README.md
└── LICENSE

src/root.zig:

// Re-exporta submódulos
pub const core = @import("core/mod.zig");
pub const io = @import("io/mod.zig");
pub const utils = @import("utils/mod.zig");

// Re-exporta tipos comuns
pub const Config = core.types.Config;
pub const Error = core.error.Error;

Dependências Remotas vs Locais

Quando Usar Cada Tipo

TipoUse QuandoExemplo
Remota (URL/Git)Dependência estável, publicadazbench, zul
Local (path)Desenvolvimento simultâneoEditando lib e app juntos
Local (path)Código privado/proprietárioLibs internas da empresa
Local (path)Fork temporárioTestando modificações

Alternando entre Remoto e Local

Durante o desenvolvimento, você pode alternar:

// build.zig.zon — versão para produção
.{
    .dependencies = .{
        .minha_lib = .{
            .url = "git+https://github.com/user/lib#v1.0.0",
            .hash = "1220abc...",
        },
    },
}
// build.zig.zon — versão para desenvolvimento
.{
    .dependencies = .{
        .minha_lib = .{
            .path = "../minha-lib",  // Caminho local
        },
    },
}

Dica: Use branches Git ou comentários para gerenciar:

.{
    .dependencies = .{
        // Produção:
        // .minha_lib = .{ .url = "...", .hash = "..." },
        
        // Desenvolvimento:
        .minha_lib = .{ .path = "../minha-lib" },
    },
}

Monorepo: Múltiplos Pacotes

Estrutura típica de monorepo:

meu-monorepo/
├── packages/
│   ├── core/              # Pacote: core
│   │   ├── build.zig
│   │   ├── build.zig.zon
│   │   └── src/
│   │
│   ├── utils/             # Pacote: utils
│   │   ├── build.zig
│   │   ├── build.zig.zon
│   │   └── src/
│   │
│   └── app/               # Pacote: app (depende de core e utils)
│       ├── build.zig
│       ├── build.zig.zon  # Depende de core e utils via path
│       └── src/
└── README.md

packages/app/build.zig.zon:

.{
    .name = .@"app",
    .version = "0.1.0",
    .dependencies = .{
        .core = .{ .path = "../core" },
        .utils = .{ .path = "../utils" },
    },
    .paths = .{ "build.zig", "build.zig.zon", "src" },
}

Publicando Pacotes

Preparando para Publicação

Antes de publicar, verifique:

# 1. Build funciona?
zig build

# 2. Testes passam?
zig build test

# 3. Documentação atualizada?
# README.md completo
# CHANGELOG.md atualizado

# 4. Versão correta?
# build.zig.zon → .version

# 5. Arquivos necessários incluídos?
# build.zig.zon → .paths

Checklist de Qualidade

  • zig build funciona sem erros
  • zig build test passa todos os testes
  • README.md com:
    • Descrição do projeto
    • Instruções de instalação
    • Exemplos de uso
    • Documentação da API
    • Licença
  • CHANGELOG.md com histórico de versões
  • Código documentado (doc comments com ///)
  • Sem arquivos desnecessários em .paths

Distribuição

O Zig não tem um registro central de pacotes (tipo crates.io ou npm). A distribuição é feita via:

  1. GitHub/GitLab — Tags de release com tarballs
  2. Git — Referências diretas a commits/tags
  3. Servidor próprio — URLs de tarball

Criando um release no GitHub:

# 1. Atualizar versão no build.zig.zon
# 2. Commit
 git add .
 git commit -m "Release v1.0.0"
 
# 3. Tag
 git tag v1.0.0
 git push origin v1.0.0

# 4. Criar release no GitHub (interface web)
# 5. Anexar tarball gerado automaticamente pelo GitHub

URL para zig fetch:

# Usar a URL do tarball do release
zig fetch --save https://github.com/user/repo/archive/refs/tags/v1.0.0.tar.gz

# Ou usar Git diretamente
zig fetch --save git+https://github.com/user/repo#v1.0.0

Documentação com Autodoc

O Zig pode gerar documentação automaticamente:

# Gerar documentação
zig build --prefix docs/

# Ou usando zig directly
zig build-lib -femit-docs src/root.zig

Exemplo de código bem documentado:

/// Representa uma configuração de conexão.
///
/// Exemplo:
/// ```zig
/// const config = Config{
///     .host = "localhost",
///     .port = 8080,
/// };
/// ```
pub const Config = struct {
    /// Endereço do servidor
    host: []const u8,
    
    /// Porta de conexão (padrão: 8080)
    port: u16 = 8080,
    
    /// Timeout em milissegundos
    timeout_ms: u32 = 5000,
};

/// Inicializa uma conexão com a configuração fornecida.
///
/// Parâmetros:
/// - `allocator`: Alocador para recursos da conexão
/// - `config`: Configuração de conexão
///
/// Retorna: `Connection` ou erro
///
/// Erros possíveis:
/// - `error.InvalidHost`: Host inválido
/// - `error.ConnectionRefused`: Conexão recusada
pub fn connect(allocator: std.mem.Allocator, config: Config) !Connection {
    // implementação
}

Melhores Práticas

1. Use Versões Específicas

// ✅ Bom — versão fixa
.url = "git+https://github.com/user/repo#v1.2.3",

// ⚠️ Evite — branch pode mudar
.url = "git+https://github.com/user/repo#main",

2. Mantenha o Cache Limpo

# O cache pode crescer com o tempo
# Limpe periodicamente (ou deixe o Zig gerenciar)
rm -rf ~/.cache/zig/p/

3. Documente Dependências

No README.md, liste as dependências principais:

## Dependências

- [zul](https://github.com/vsrinivas/zul) v0.3+ — Utilitários
- [zbench](https://github.com/hendriknielaender/zBench) v0.9+ — Benchmarks

4. Minimize Dependências

// ✅ Bom — poucas deps bem escolhidas
.dependencies = .{
    .zul = .{ .url = "...", .hash = "..." },
},

// ⚠️ Cuidado — árvore de deps grande
// Cada dependência aumenta tempo de build e risco

5. Teste com CI

# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: goto-bus-stop/setup-zig@v2
        with:
          version: 0.15.0
      - run: zig build
      - run: zig build test

6. Use Módulos para Organização

// ✅ Bom — módulos bem definidos
const json = @import("json");
const http = @import("http");
const db = @import("db");

// ❌ Ruim — tudo em um arquivo gigante
// src/main.zig com 5000 linhas

Troubleshooting

Problema: “error: HashMismatch”

Causa: O hash no build.zig.zon não corresponde ao conteúdo baixado.

Solução:

# Remover a linha .hash do build.zig.zon
# Executar para obter o hash correto
zig build --fetch

# Copiar o hash do erro e atualizar o build.zig.zon

Problema: “error: DependencyNotFound”

Causa: A dependência não foi baixada.

Solução:

# Baixar todas as dependências
zig build --fetch

# Ou force refetch
rm -rf ~/.cache/zig/p/
zig build --fetch

Problema: “error: FileNotFound” ao importar

Causa: O nome do módulo no addImport() não corresponde ao @import().

Verifique:

// build.zig
exe.root_module.addImport("minha_lib", dep.module("minha_lib"));
//                                           ^^^^^^^^^^^^
//                                           nome do módulo

// código.zig
const lib = @import("minha_lib");
//                    ^^^^^^^^^
//                    deve corresponder ao addImport

Problema: Build lento após adicionar deps

Causa: Muitas dependências ou compilação inicial.

Solução:

# Cache é reutilizado após primeira compilação
# Use --summary para ver o que está sendo compilado
zig build --summary all

Problema: Conflito de nomes

Causa: Dois pacotes expõem módulos com o mesmo nome.

Solução: Use aliases no build.zig:

// Renomear na importação
exe.root_module.addImport("lib_a_json", dep_a.module("json"));
exe.root_module.addImport("lib_b_json", dep_b.module("json"));

Exemplo Completo: Projeto com Dependências

Vamos criar um projeto completo que usa múltiplas dependências:

Estrutura:

meu-app/
├── build.zig
├── build.zig.zon
└── src/
    └── main.zig

build.zig.zon:

.{
    .name = .@"meu-app",
    .version = "1.0.0",
    .fingerprint = 0x12345678,
    .minimum_zig_version = "0.15.0",
    .dependencies = .{
        // Biblioteca de utilitários
        .zul = .{
            .url = "git+https://github.com/vsrinivas/zul#v0.3.2",
            .hash = "1220a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b",
        },
        
        // Biblioteca de benchmarking
        .zbench = .{
            .url = "git+https://github.com/hendriknielaender/zBench#v0.9.2",
            .hash = "1220b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c",
        },
    },
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
    },
}

build.zig:

const std = @import("std");

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

    // === Dependências ===
    const zul = b.dependency("zul", .{
        .target = target,
        .optimize = optimize,
    });
    
    const zbench = b.dependency("zbench", .{
        .target = target,
        .optimize = optimize,
    });

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

    // Adicionar módulos
    exe.root_module.addImport("zul", zul.module("zul"));
    exe.root_module.addImport("zbench", zbench.module("zbench"));

    b.installArtifact(exe);

    // === Run step ===
    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", "Run the app");
    run_step.dependOn(&run_cmd.step);

    // === Test step ===
    const unit_tests = b.addTest(.{
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        }),
    });
    unit_tests.root_module.addImport("zul", zul.module("zul"));
    
    const run_unit_tests = b.addRunArtifact(unit_tests);
    const test_step = b.step("test", "Run unit tests");
    test_step.dependOn(&run_unit_tests.step);

    // === Benchmark step ===
    const bench_exe = b.addExecutable(.{
        .name = "benchmark",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/benchmark.zig"),
            .target = target,
            .optimize = .ReleaseFast,
        }),
    });
    bench_exe.root_module.addImport("zbench", zbench.module("zbench"));
    
    const run_bench = b.addRunArtifact(bench_exe);
    const bench_step = b.step("bench", "Run benchmarks");
    bench_step.dependOn(&run_bench.step);
}

src/main.zig:

const std = @import("std");
const zul = @import("zul");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Usando zul para parsing de UUID
    const uuid = try zul.UUID.parse("550e8400-e29b-41d4-a716-446655440000");
    std.debug.print("UUID: {s}\n", .{uuid});

    // Usando zul para StringBuilder
    var builder = zul.StringBuilder.init(allocator);
    defer builder.deinit();
    
    try builder.append("Hello, ");
    try builder.append("Zig Packaging!");
    
    std.debug.print("{s}\n", .{builder.string()});
}

// Testes
const testing = std.testing;

test "UUID parsing" {
    const uuid = try zul.UUID.parse("550e8400-e29b-41d4-a716-446655440000");
    try testing.expectEqual(uuid, uuid);
}

Comandos:

# Build
zig build

# Run
zig build run

# Test
zig build test

# Benchmark
zig build bench

# Ver dependências
zig build --fetch
ls ~/.cache/zig/p/

Resumo

O Zig Package Manager oferece um sistema de gerenciamento de dependências:

AspectoImplementação Zig
Manifestobuild.zig.zon (ZON format)
Adicionar depszig fetch --save <url>
CacheContent-addressable em ~/.cache/zig/
Uso no códigob.dependency() + addImport()
Módulosb.createModule() + addImport()
VersõesSemver + hashes de integridade
PublicaçãoTags Git + releases GitHub

Comparação com outras linguagens:

ZigRustNode.jsPython
Manifestobuild.zig.zonCargo.tomlpackage.jsonpyproject.toml
RegistryNenhum (URLs diretas)crates.ionpm.orgPyPI
LockfileImplícito (hashes)Cargo.lockpackage-lock.jsonpoetry.lock
Offline✅ Sim✅ Sim✅ Sim✅ Sim

O Zig escolhe simplicidade sobre features complexas: sem registro central, sem árvore de dependências complexa, apenas URLs e hashes.

Próximos Passos

Agora que você domina o sistema de pacotes do Zig:

  1. 📦 Zig Build System: Guia Completo — Aprofunde no build.zig
  2. Comptime em Zig — Metaprogramação avançada
  3. 🌐 Zig WebAssembly — Compile para WASM
  4. 🔍 Documentação oficial de Packaging — Referência completa

Explore a lista de pacotes Zig para descobrir bibliotecas úteis.


Tem dúvidas sobre Zig Packaging? Compartilhe com a comunidade Zig Brasil!

Continue aprendendo Zig

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