# cross-compilation building for platforms other than the one you're on. zig makes this unusually easy, but there are still patterns to know. ## basic cross-compilation zig can target any platform from any platform: ```zig const target = b.standardTargetOptions(.{}); // from -Dtarget=... ``` user runs: `zig build -Dtarget=aarch64-linux-gnu` that's it for pure zig. c dependencies complicate things. ## cpu targeting bun explicitly sets cpu models per platform: ```zig pub fn getCpuModel(os: OperatingSystem, arch: Arch) ?Target.Query.CpuModel { if (os == .linux and arch == .aarch64) { return .{ .explicit = &Target.aarch64.cpu.cortex_a35 }; } if (os == .mac and arch == .aarch64) { return .{ .explicit = &Target.aarch64.cpu.apple_m1 }; } // x86_64 defaults to haswell for avx2 return null; } ``` and offers a "baseline" mode for maximum compatibility: ```zig if (opts.baseline) { // target nehalem (~2008) instead of haswell (~2013) return .{ .explicit = &Target.x86_64.cpu.nehalem }; } ``` baseline builds run on older hardware but miss avx2 optimizations. ## glibc version linux binaries link against glibc. if you build against glibc 2.34, it won't run on systems with glibc 2.17. ```zig pub fn getOSGlibCVersion(os: OperatingSystem) ?Version { return switch (os) { .linux => .{ .major = 2, .minor = 26, .patch = 0 }, else => null, }; } ``` bun targets glibc 2.26 (from ~2017) for broad compatibility. ## macos universal binaries ghostty builds for both x86_64 and aarch64, then combines with lipo: ```zig // build for both architectures const x86_lib = try buildLib(b, deps.retarget(b, x86_64_macos)); const arm_lib = try buildLib(b, deps.retarget(b, aarch64_macos)); // combine into universal binary const lipo_step = LipoStep.create(b, .{ .input_a = x86_lib.getEmittedBin(), .input_b = arm_lib.getEmittedBin(), .out_name = "libghostty.a", }); ``` the `deps.retarget()` pattern creates a copy of SharedDeps pointing at a different target. ## xcframework for apple platforms for ios apps, you need an xcframework containing: - macos universal (x86_64 + arm64) - ios arm64 - ios simulator (arm64 + x86_64) ```zig const macos = try buildMacOSUniversal(b, deps); const ios = try buildLib(b, deps.retarget(b, .{ .os_tag = .ios, .cpu_arch = .aarch64 })); const ios_sim = try buildLib(b, deps.retarget(b, .{ .os_tag = .ios, .abi = .simulator })); // xcodebuild -create-xcframework ... ``` ## minimum os versions centralize version requirements: ```zig pub fn osVersionMin(os: std.Target.Os.Tag) std.Target.Os.SemVer { return switch (os) { .macos => .{ .major = 13, .minor = 0, .patch = 0 }, .ios => .{ .major = 17, .minor = 0, .patch = 0 }, else => .{ .major = 0, .minor = 0, .patch = 0 }, }; } ``` apply when creating targets: ```zig const target = b.resolveTargetQuery(.{ .os_tag = .macos, .os_version_min = Config.osVersionMin(.macos), }); ``` ## environment detection helpful warnings for common mistakes: ```zig fn checkNixShell(exe: *std.Build.Step.Compile, cfg: *const Config) !void { std.fs.accessAbsolute("/etc/NIXOS", .{}) catch return; // not nixos if (cfg.env.get("IN_NIX_SHELL") != null) return; // in nix shell, good try exe.step.addError( "Building on NixOS outside nix shell. " ++ "Use: nix develop -c zig build", .{}, ); } ``` sources: - [ghostty/src/build/GhosttyLib.zig](https://github.com/ghostty-org/ghostty/blob/main/src/build/GhosttyLib.zig) - [bun/build.zig](https://github.com/oven-sh/bun/blob/main/build.zig)