Skip to main content
One of Zig’s most powerful features is first-class cross-compilation support. You can compile for any supported target from any host system without needing to install cross-compilation toolchains.

Cross-Compilation Basics

Zig includes cross-compilation support out of the box:
  • No separate toolchain installation required
  • Built-in support for all targets
  • Consistent behavior across host platforms
  • Integrated libc for all targets

Compiling for Different Targets

Command Line

# Compile for Linux x86_64
zig build-exe main.zig -target x86_64-linux-gnu

# Compile for Windows x86_64
zig build-exe main.zig -target x86_64-windows-gnu

# Compile for macOS ARM64
zig build-exe main.zig -target aarch64-macos

# Compile for WebAssembly
zig build-exe main.zig -target wasm32-wasi

# Compile for embedded ARM
zig build-exe main.zig -target arm-freestanding-eabi

In build.zig

const std = @import("std");

pub fn build(b: *std.Build) void {
    // Accept target from command line or use default
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

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

    b.installArtifact(exe);
}
Build with:
zig build -Dtarget=aarch64-linux-musl -Doptimize=ReleaseSmall

Target Triple Format

Target triples specify: <arch>-<os>-<abi>

Architecture Examples

x86_64      64-bit x86
x86         32-bit x86
aarch64     64-bit ARM (Apple Silicon, ARM servers)
arm         32-bit ARM
riscv64     64-bit RISC-V
riscv32     32-bit RISC-V
wasm32      WebAssembly 32-bit
wasm64      WebAssembly 64-bit
powerpc64   64-bit PowerPC
mips64      64-bit MIPS
sparc64     64-bit SPARC

OS Examples

linux       Linux
windows     Windows
macos       macOS
ios         iOS
freebsd     FreeBSD
openbsd     OpenBSD
netbsd      NetBSD
wasi        WebAssembly System Interface
freestanding No operating system (bare metal)
uefi        UEFI firmware

ABI Examples

gnu         GNU C Library
musl        musl C Library
msvc        Microsoft Visual C++
eabi        Embedded ABI
eabihf      Embedded ABI Hard Float
android     Android
none        No ABI

Common Cross-Compilation Targets

Linux Targets

# x86_64 with glibc
zig build -Dtarget=x86_64-linux-gnu

# x86_64 with musl (static linking friendly)
zig build -Dtarget=x86_64-linux-musl

# ARM64 Linux
zig build -Dtarget=aarch64-linux-musl

# 32-bit ARM Linux
zig build -Dtarget=arm-linux-musleabihf

# RISC-V 64-bit
zig build -Dtarget=riscv64-linux-musl

Windows Targets

# 64-bit Windows
zig build -Dtarget=x86_64-windows-gnu

# 32-bit Windows
zig build -Dtarget=x86-windows-gnu

# ARM64 Windows
zig build -Dtarget=aarch64-windows-gnu

macOS Targets

# ARM64 macOS (Apple Silicon)
zig build -Dtarget=aarch64-macos

# x86_64 macOS (Intel)
zig build -Dtarget=x86_64-macos

WebAssembly

# WASI (WebAssembly System Interface)
zig build -Dtarget=wasm32-wasi

# Emscripten
zig build -Dtarget=wasm32-emscripten

# Freestanding WebAssembly
zig build -Dtarget=wasm32-freestanding

Embedded / Bare Metal

# ARM Cortex-M
zig build -Dtarget=thumb-freestanding-eabi

# RISC-V embedded
zig build -Dtarget=riscv32-freestanding-eabi

# AVR (Arduino)
zig build -Dtarget=avr-freestanding-eabi

CPU Features

Specify CPU features for optimization:
# Target specific CPU
zig build-exe main.zig -target x86_64-linux -mcpu=native

# Target baseline CPU with specific features
zig build-exe main.zig -target x86_64-linux -mcpu=baseline+avx2+fma

# Disable specific features
zig build-exe main.zig -target arm-freestanding -mcpu=baseline-neon

In build.zig

lib/std/Build.zig:628-634
const target = b.resolveTargetQuery(std.Target.Query.parse(.{
    .arch_os_abi = "wasm32-wasi",
    // Baseline CPU minus extended_const, plus specific features
    .cpu_features = "baseline-extended_const+nontrapping_bulk_memory_len0",
}) catch unreachable);

Static vs Dynamic Linking

Static Linking

Static executables have no runtime dependencies:
const exe = b.addExecutable(.{
    .name = "static-app",
    .root_module = b.createModule(.{
        .root_source_file = b.path("src/main.zig"),
        .target = b.resolveTargetQuery(.{
            .cpu_arch = .x86_64,
            .os_tag = .linux,
            .abi = .musl,  // musl is ideal for static linking
        }),
        .optimize = .ReleaseSmall,
    }),
    .linkage = .static,
});

Dynamic Linking

const lib = b.addLibrary(.{
    .name = "mylib",
    .linkage = .dynamic,
    .root_module = b.createModule(.{
        .root_source_file = b.path("src/lib.zig"),
        .target = target,
        .optimize = optimize,
    }),
});

Cross-Compiling with C Dependencies

Linking System Libraries

When cross-compiling with system library dependencies:
lib/std/Build.zig:336-344
if (target.result.os.tag == .windows) {
    // Windows-specific libraries
    exe.root_module.linkSystemLibrary("ws2_32", .{});
    exe.root_module.linkSystemLibrary("version", .{});
    exe.root_module.linkSystemLibrary("uuid", .{});
    exe.root_module.linkSystemLibrary("ole32", .{});
}

Using Zig’s Bundled libc

exe.root_module.link_libc = true;
Zig includes libc headers and implementations for all targets.

C++ Standard Library

exe.root_module.link_libcpp = true;

Platform-Specific Code

Handle platform differences in your code:
const builtin = @import("builtin");

pub fn main() void {
    switch (builtin.os.tag) {
        .windows => {
            // Windows-specific code
        },
        .linux => {
            // Linux-specific code
        },
        .macos => {
            // macOS-specific code
        },
        .wasi => {
            // WebAssembly-specific code
        },
        else => {
            // Fallback for other platforms
        },
    }

    // CPU architecture checks
    switch (builtin.cpu.arch) {
        .x86_64 => {
            // x86_64-specific optimizations
        },
        .aarch64 => {
            // ARM64-specific optimizations
        },
        else => {},
    }
}

Build Script Examples

Multi-Target Release Builds

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

    const targets = [_]std.Target.Query{
        .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl },
        .{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu },
        .{ .cpu_arch = .x86_64, .os_tag = .macos },
        .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl },
        .{ .cpu_arch = .aarch64, .os_tag = .macos },
        .{ .cpu_arch = .wasm32, .os_tag = .wasi },
    };

    for (targets) |t| {
        const resolved = b.resolveTargetQuery(t);
        const target_output = b.fmt("{s}-{s}", .{
            @tagName(t.cpu_arch.?),
            @tagName(t.os_tag.?),
        });

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

        const install_artifact = b.addInstallArtifact(exe, .{
            .dest_dir = .{
                .override = .{
                    .custom = b.fmt("bin/{s}", .{target_output}),
                },
            },
        });

        b.getInstallStep().dependOn(&install_artifact.step);
    }
}

Conditional Cross-Compilation

const only_c = b.option(
    bool,
    "only-c",
    "Translate to C code, with only the C backend enabled"
) orelse false;

const target = b.standardTargetOptions(.{
    .default_target = .{
        .ofmt = if (only_c) .c else null,
    },
});

Testing Cross-Compiled Binaries

QEMU Integration

Zig can automatically use QEMU to run cross-compiled tests:
lib/std/Build.zig:74-75
/// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
enable_qemu: bool = false,
Enable in your build:
zig build test -Dtarget=aarch64-linux -Denable-qemu

Wine Integration

For Windows binaries on Linux/macOS:
lib/std/Build.zig:80-81
/// Use system Wine installation to run cross compiled Windows build artifacts.
enable_wine: bool = false,
zig build test -Dtarget=x86_64-windows -Denable-wine

Rosetta Integration

On Apple Silicon, run x86_64 macOS binaries:
lib/std/Build.zig:76-77
/// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
enable_rosetta: bool = false,

Target Utilities

Checking Target Capabilities

src/target.zig:13-18
pub fn cannotDynamicLink(target: *const std.Target) bool {
    return switch (target.os.tag) {
        .freestanding => true,
        else => target.cpu.arch.isSpirV(),
    };
}

Required libc

src/target.zig:20-25
/// On Darwin, we always link libSystem which contains libc.
/// Similarly on FreeBSD and NetBSD we always link system libc
/// since this is the stable syscall interface.
pub fn osRequiresLibC(target: *const std.Target) bool {
    return target.requiresLibC();
}

PIC Requirements

src/target.zig:48-54
/// This function returns whether non-pic code is completely invalid on the given target.
pub fn requiresPIC(target: *const std.Target, linking_libc: bool) bool {
    return target.abi.isAndroid() or
        target.os.tag == .windows or target.os.tag == .uefi or
        osRequiresLibC(target) or
        (linking_libc and target.isGnuLibC());
}

Single-Threaded Defaults

src/target.zig:78-88
pub fn defaultSingleThreaded(target: *const std.Target) bool {
    switch (target.cpu.arch) {
        .wasm32, .wasm64 => return true,
        else => {},
    }
    switch (target.os.tag) {
        .haiku => return true,
        else => {},
    }
    return false;
}

Best Practices

1
Use musl for Linux
2
For maximum compatibility and static linking:
3
zig build -Dtarget=x86_64-linux-musl -Doptimize=ReleaseSmall
4
Test on Target Platforms
5
While Zig makes cross-compilation easy, always test on the actual target platform when possible.
6
Handle Platform Differences
7
Use compile-time branching for platform-specific code:
8
const is_windows = @import("builtin").os.tag == .windows;
const path_separator = if (is_windows) '\\\\' else '/';
9
Optimize for Size for Embedded
10
For embedded targets:
11
zig build -Dtarget=thumb-freestanding-eabi -Doptimize=ReleaseSmall

Common Issues and Solutions

Missing libc Headers

Zig bundles libc headers for all targets. If you encounter missing headers, ensure you’re using exe.root_module.link_libc = true.

Undefined Symbols

When linking C libraries, ensure you’re linking all required dependencies:
exe.root_module.linkSystemLibrary("m", .{});  // Math library
exe.root_module.linkSystemLibrary("pthread", .{});  // POSIX threads

Static Linking Issues

Some libraries don’t support static linking. Use musl ABI for better static linking support:
zig build -Dtarget=x86_64-linux-musl

Backend Support

Different backends support different targets:
src/target.zig:147-234
/// The set of targets that LLVM has non-experimental support for.
pub fn hasLlvmSupport(target: *const std.Target, ofmt: std.Target.ObjectFormat) bool {
    switch (ofmt) {
        // LLVM does not support these object formats:
        .c,
        .plan9,
        => return false,

        .coff,
        .elf,
        .hex,
        .macho,
        .spirv,
        .raw,
        .wasm,
        => {},
    }

    return switch (target.cpu.arch) {
        .arm,
        .armeb,
        .aarch64,
        .aarch64_be,
        .arc,
        .avr,
        .bpfel,
        .bpfeb,
        .hexagon,
        .loongarch32,
        .loongarch64,
        .m68k,
        .mips,
        .mipsel,
        .mips64,
        .mips64el,
        .msp430,
        .powerpc,
        .powerpcle,
        .powerpc64,
        .powerpc64le,
        .amdgcn,
        .riscv32,
        .riscv32be,
        .riscv64,
        .riscv64be,
        .sparc,
        .sparc64,
        .spirv32,
        .spirv64,
        .s390x,
        .thumb,
        .thumbeb,
        .x86,
        .x86_64,
        .xcore,
        .nvptx,
        .nvptx64,
        .lanai,
        .wasm32,
        .wasm64,
        .ve,
        => true,
        // ...
    };
}

See Also

Build docs developers (and LLMs) love