Sign in

    zig-cross

    Cross-compile MoonBit native binaries with zig

    command-line
    moonx
    wasm
    zig
    cross-compile
    Download zip
    Author
    Version
    0.0.1
    License
    Apache-2.0
    Last updated
    3 hours ago
    Downloads
    1

    Dependencies

    #zig-cross

    zig-cross cross-compiles MoonBit native binaries with zig. It builds for Linux, macOS and Windows from one machine. It runs with moonx, so you do not install it.

    This tool will move to its own repository.

    #Requirements

    • Zig 0.17.0 on PATH: mise use zig@0.17.0.
    • The moon command.
    • Linux or macOS as the build machine. The tool stops on Windows.

    #Usage

    moonx moonrockz/zig-cross@<version> build linux-x64 darwin-arm64 -- apps/myapp moonx moonrockz/zig-cross@<version> exec host -- moon test --target native moonx moonrockz/zig-cross@<version> --help

    build [--target-dir DIR] <target>... [-- <path>...]

    1. It writes the wrappers cc and ar to DIR/zig/<target>. DIR is _build/cross by default.
    2. It runs moon build --target native --release --target-dir DIR/<target> [paths] with MOON_CC and MOON_AR set to the wrappers.
    3. It reads the header of each *.exe under DIR/<target>/native/release/build. A binary with the wrong architecture is a failure.
    4. It prints a table (target, path, size, result). It exits with 1 when one target fails.

    exec <target> -- <command> <arg>... runs a command with MOON_CC and MOON_AR set to the wrappers of one target. Use host to compile the tests of a package with zig.

    #Targets

    NameZig tripleHeader check
    linux-x64x86_64-linux-muslELF, machine 62
    linux-arm64aarch64-linux-muslELF, machine 183
    darwin-arm64aarch64-macosMach-O, cputype 16777228
    darwin-x64x86_64-macosMach-O, cputype 16777223
    windows-x64x86_64-windows-gnuPE, machine 0x8664
    hostnone (the host)none

    #Do not ship a cross-built Windows binary

    A windows-x64 binary that zig cross-builds is wrong for MoonBit code that uses OsString: arguments, environment variables and paths. The core library is prebuilt for the host. Its #cfg(platform="windows") setting is resolved from the host OS, not from the target. A Windows binary built on Linux or macOS therefore reads its arguments wrongly. Build Windows binaries on Windows until moon can set the target platform.

    #What the wrappers change, and why

    Two upstream bugs make a plain MOON_CC="zig cc -target ..." fail.

    • Every wrapper adds -D_CRT_RAND_S. The MoonBit runtime file env.c includes <stdlib.h> before it defines _CRT_RAND_S, so rand_s is not declared on Windows. The flag defines it before the first include.
    • A cross wrapper drops -DMOONBIT_USE_SIMDUTF and the arguments that end in /moonbit_simdutf.o, /simdutf.o and /libbacktrace.a. moon links these prebuilt objects for the machine that runs moon. They have the wrong architecture for the target. The ar wrapper drops the two simdutf objects. The host wrapper drops nothing.
    • The host wrapper adds -lunwind on Linux, for link steps only. A debug build there compiles backtrace.c, which needs the unwinder. gcc links its unwinder by itself, but zig cc does not. macOS needs nothing.

    Command

    pub(all) enum Command {
    Build(target_dir~ : String, targets~ : Array[Target], paths~ : Array[String])
    Exec(target~ : Target, argv~ : Array[String])
    Help
    Version
    } derive(Eq,
    Debug
    )

    Format

    pub(all) enum Format {
    Elf(Int)
    MachO(Int)
    Pe(Int)
    Unknown
    } derive(Eq,
    Debug
    )

    The executable format of a binary, from its header. The payload is the machine field: e_machine for ELF, cputype for Mach-O and Machine for PE.

    Target

    pub(all) enum Target {
    LinuxX64
    LinuxArm64
    DarwinArm64
    DarwinX64
    WindowsX64
    Host
    } derive(Eq,
    Debug
    )

    A target that zig-cross builds for. Host is the machine itself: zig only compiles for it, and the wrappers do not strip anything.

    Target::name

    fn Target::name(self : Target) -> String

    Target::parse

    fn Target::parse(text : String) -> Target?

    ar_wrapper

    fn ar_wrapper(target : Target) -> String

    The ar wrapper for MOON_AR. moon puts the prebuilt simdutf objects into the runtime archive; a cross build must leave them out (see cc_wrapper).

    cc_wrapper

    fn cc_wrapper(target : Target) -> String

    The Host wrapper has one more rule. On Linux, a debug build compiles backtrace.c, which calls _Unwind_Backtrace. gcc links its unwinder (libgcc) by itself, but zig cc does not, so a link step needs zig's bundled -lunwind. macOS has the unwinder in libSystem and needs nothing. The tool can run on wasm, so the wrapper decides at run time with uname -s. A compile step (-c) does not link, so it gets no library.

    The cc wrapper for MOON_CC: a /bin/sh script that runs zig cc.

    A cross build needs two workarounds for upstream bugs in moon and in the MoonBit runtime:

    1. The runtime file env.c includes <stdlib.h> before it defines _CRT_RAND_S. On Windows (mingw), rand_s is then not declared and the compile fails. A -D_CRT_RAND_S on the command line defines it before the first include. The flag does no harm on other targets, so every wrapper has it.
    2. moon passes -DMOONBIT_USE_SIMDUTF and links the prebuilt objects moonbit_simdutf.o, simdutf.o and libbacktrace.a. These objects are built for the machine that runs moon, not for MOON_CC's target, so the link fails or the binary gets the wrong architecture. A cross wrapper removes the flag and the three files. The Host wrapper removes nothing.

    check_format

    fn check_format(target : Target, bytes : Bytes) -> String?

    None when the header in bytes is right for target, else the message for the summary.

    default_target_dir

    let default_target_dir : String

    The default value of --target-dir.

    detect_format

    fn detect_format(bytes : Bytes) -> Format

    Read the format from the first bytes of a file. A short or unknown input gives Unknown.

    expected_format

    fn expected_format(target : Target) -> Format?

    The format that a binary for target must have. Host has none, so it is not checked.

    help_text

    fn help_text() -> String

    parse_args

    fn parse_args(args : Array[String]) -> Result[Command, String]

    Parse the arguments, without argv[0].

    version

    let version : String

    The version of this tool. scripts/versions.mbtx keeps it equal to version in moon.mod.

    version_text

    fn version_text() -> String

    zig_triple

    fn zig_triple(target : Target) -> String?

    The zig target triple. Host has none: zig then compiles for the machine it runs on.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io