Skip to content

Dependencies

A Zig package may depend on another package.

A Zig package may depend on another package.

Dependencies are declared in build.zig.zon. The build script then imports them by name.

A small build.zig.zon file looks like this:

.{
    .name = .demo,
    .version = "0.1.0",
    .dependencies = .{},
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
    },
}

The .paths field says which files belong to the package. Keep it narrow. Do not publish temporary files, build output, editor state, or local scripts by accident.

A dependency is added under .dependencies:

.{
    .name = .demo,
    .version = "0.1.0",
    .dependencies = .{
        .foo = .{
            .url = "https://example.com/foo-1.0.0.tar.gz",
            .hash = "...",
        },
    },
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
    },
}

The name .foo is the package name used by the build script.

Inside build.zig, fetch the dependency:

const foo_dep = b.dependency("foo", .{
    .target = target,
    .optimize = optimize,
});

Then get a module from it:

const foo_mod = foo_dep.module("foo");

Attach it to your executable or library:

exe.root_module.addImport("foo", foo_mod);

Now source code may import it:

const foo = @import("foo");

The import name is chosen by the build script. It does not have to match the package name, but it should unless there is a clear reason.

Dependencies are explicit at each layer.

build.zig.zon says where the package comes from.

build.zig says which dependency is used by which artifact.

The source file says which module it imports.

This avoids hidden global package state. A package does not become visible merely because it exists in a directory. The build graph must connect it.

A dependency can receive options from the parent build:

const foo_dep = b.dependency("foo", .{
    .target = target,
    .optimize = optimize,
});

This is the usual pattern. It builds the dependency for the same target and optimization mode as the main artifact.

A project may depend on a library but not expose it to all modules. For example:

tool.root_module.addImport("foo", foo_mod);

This gives foo only to tool. Other artifacts cannot import it unless they receive the same import.

That is useful. Keep dependencies local when possible.

A package may also expose several modules:

const parser = dep.module("parser");
const runtime = dep.module("runtime");

The names are defined by the dependency’s build script.

Use the package manager for source dependencies. Use system libraries only when the project deliberately depends on the host system.

A system library is linked separately:

exe.linkSystemLibrary("sqlite3");

This means the build depends on a library installed on the target or host system. That is a different kind of dependency from a Zig package.

Package dependencies should be repeatable. The hash in build.zig.zon protects the fetched content. If the content changes, the hash no longer matches.

Do not remove the hash to make the build easier. The hash is part of the guarantee that a build uses the expected source.

A good dependency section is small and boring. Add a dependency when it saves real work or provides a stable interface. Avoid adding a package for code that would be simpler to write directly.

Exercise 15-25. Add one dependency to build.zig.zon.

Exercise 15-26. Import the dependency in build.zig.

Exercise 15-27. Attach the dependency module only to one executable.

Exercise 15-28. Explain the difference between a Zig package dependency and linkSystemLibrary.