Skip to content

Target Triples

One of Zig's main design goals is cross compilation.

One of Zig’s main design goals is cross compilation.

A Zig compiler running on one machine can produce programs for many other operating systems and processors without installing a separate toolchain.

You can build a Linux executable on macOS. You can build for ARM on x86-64. You can build freestanding binaries for systems with no operating system at all.

The target is selected with a target triple.

A target triple describes three things:

<architecture>-<operating system>-<abi>

For example:

x86_64-linux-gnu
aarch64-macos-none
wasm32-freestanding-none
riscv64-linux-musl

The first field is the CPU architecture.

Common architectures include:

ArchitectureMeaning
x86_6464-bit x86
aarch6464-bit ARM
riscv6464-bit RISC-V
wasm32WebAssembly 32-bit

The second field is the operating system.

Operating systemMeaning
linuxLinux
macosApple macOS
windowsMicrosoft Windows
freestandingNo operating system

The third field is the ABI.

ABI means Application Binary Interface. It defines low-level conventions such as calling functions, object layout, and system libraries.

Common ABIs include:

ABIMeaning
gnuglibc-based systems
muslmusl libc
msvcMicrosoft Visual C++ ABI
noneNo libc or ABI runtime

A native build uses the current machine automatically:

zig build-exe hello.zig

This produces an executable for the current operating system and processor.

To build for another target, use -target:

zig build-exe hello.zig -target x86_64-linux-gnu

On a macOS machine this produces a Linux executable.

To build for ARM Linux:

zig build-exe hello.zig -target aarch64-linux-musl

To build for WebAssembly:

zig build-exe hello.zig -target wasm32-freestanding-none

You can inspect supported targets with:

zig targets

The output is large. It includes architectures, operating systems, CPUs, features, and ABI combinations known to the compiler.

A small program can inspect its target at compile time.

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

pub fn main() void {
    std.debug.print("arch = {s}\n", .{@tagName(builtin.cpu.arch)});
    std.debug.print("os   = {s}\n", .{@tagName(builtin.os.tag)});
}

On Linux x86-64 the output might be:

arch = x86_64
os   = linux

builtin contains information generated by the compiler for the current target.

builtin.cpu.arch is an enum value describing the CPU architecture.

builtin.os.tag is an enum value describing the operating system.

@tagName converts an enum tag into a string.

Since target information is available at compile time, programs can select code conditionally:

const builtin = @import("builtin");

pub fn main() void {
    if (builtin.os.tag == .windows) {
        windowsMain();
    } else {
        posixMain();
    }
}

fn windowsMain() void {}

fn posixMain() void {}

Only the selected branch is compiled for the target.

This is important in systems programming. Different systems often require different APIs, different calling conventions, or different executable formats.

Zig treats cross compilation as a normal operation, not a special case.

Exercise 17-1. Run zig targets and inspect the architectures supported by your compiler.

Exercise 17-2. Build the same program for two different operating systems.

Exercise 17-3. Print the target ABI with builtin.abi.

Exercise 17-4. Add target-specific code for Windows and Linux.