Skip to content

Instantly share code, notes, and snippets.

@Cloudef
Last active January 17, 2025 12:07

Revisions

  1. Cloudef revised this gist Oct 30, 2022. 1 changed file with 117 additions and 98 deletions.
    215 changes: 117 additions & 98 deletions zig-stdenv.nix
    Original file line number Diff line number Diff line change
    @@ -22,6 +22,30 @@ with builtins;
    # TODO: provide zig-overlay.nix

    let
    zig-target = let
    unknown-linux-mapper = x: concatStringsSep "-" (remove "unknown" (splitString "-" x));
    to-zig-target = {
    "armv7l-multiplatform" = "arm-linux-gnueabi";
    "armv7l-hf-multiplatform" = "arm-linux-gnueabihf";
    "armv7l-unknown-linux-musleabi" = "arm-linux-musleabi";
    "armv7l-unknown-linux-musleabihf" = "arm-linux-musleabihf";
    "armv7b-multiplatform" = "armeb-linux-gnueabi";
    "armv7b-hf-multiplatform" = "armeb-linux-gnueabihf";
    "armv7b-unknown-linux-musleabi" = "armeb-linux-musleabi";
    "armv7b-unknown-linux-musleabihf" = "armeb-linux-musleabihf";
    "wasm32-freestanding-musl" = "wasm32-freestanding-musl";
    "wasm32-unknown-wasi" = "wasm32-wasi-musl";
    "aarch64-darwin" = "aarch64-macos-gnu";
    "x86_64-darwin" = "x86_64-macos-gnu";
    "aarch64-windows" = "aarch64-windows-gnu";
    "aarch64_be-windows" = "aarch64_be-windows-gnu";
    "arm-windows" = "arm-windows-gnu";
    "armeb-windows" = "armeb-windows-gnu";
    "i386-windows" = "i386-windows-gnu";
    "x86_64-windows" = "x86_64-windows-gnu";
    };
    in to-zig-target."${target}" or unknown-linux-mapper target;

    zig-stdenv = let
    write-zig-wrapper = cmd: pkgs.writeShellScript "zig-${cmd}" ''${zig}/bin/zig ${cmd} "$@"'';
    zig-cc = write-zig-wrapper "cc";
    @@ -57,30 +81,55 @@ let
    libllvm.out = "";
    };

    zig-target = let
    unknown-linux-mapper = x: concatStringsSep "-" (remove "unknown" (splitString "-" x));
    to-zig-target = {
    "armv7l-multiplatform" = "arm-linux-gnueabi";
    "armv7l-hf-multiplatform" = "arm-linux-gnueabihf";
    "armv7l-unknown-linux-musleabi" = "arm-linux-musleabi";
    "armv7l-unknown-linux-musleabihf" = "arm-linux-musleabihf";
    "armv7b-multiplatform" = "armeb-linux-gnueabi";
    "armv7b-hf-multiplatform" = "armeb-linux-gnueabihf";
    "armv7b-unknown-linux-musleabi" = "armeb-linux-musleabi";
    "armv7b-unknown-linux-musleabihf" = "armeb-linux-musleabihf";
    "wasm32-freestanding-musl" = "wasm32-freestanding-musl";
    "wasm32-unknown-wasi" = "wasm32-wasi-musl";
    "aarch64-darwin" = "aarch64-macos-gnu";
    "x86_64-darwin" = "x86_64-macos-gnu";
    "aarch64-windows" = "aarch64-windows-gnu";
    "aarch64_be-windows" = "aarch64_be-windows-gnu";
    "arm-windows" = "arm-windows-gnu";
    "armeb-windows" = "armeb-windows-gnu";
    "i386-windows" = "i386-windows-gnu";
    "x86_64-windows" = "x86_64-windows-gnu";
    };
    in to-zig-target."${target}" or unknown-linux-mapper target;
    # Bunch of overrides to make sure we don't ever start bootstrapping another cross-compiler
    overrides = self: super: let
    to-override-set = x: genAttrs x (x: zig-toolchain);
    llvmPackages = to-override-set [ "clang" "libclang" "lld" "llvm" "libllvm" "compiler-rt" "libunwind" "libstdcxx" "libcxx" "libcxxapi" "openmp" ];
    in (to-override-set [
    "gcc" "libgcc" "glibc" "bintools" "bintoolsNoLibc" "binutils" "binutilsNoLibc"
    "gccCrossStageStatic" "preLibcCrossHeaders" "glibcCross" "muslCross" "libcCross" "threadsCross"
    ]) // {
    inherit llvmPackages;
    inherit (llvmPackages) clang libclang lld llvm libllvm libunwind libstdcxx libcxx libcxxapi openmp;
    gccForLibs = null; # also disables --gcc-toolchain passed to compiler
    };

    cross0 = import pkgs.path {
    crossSystem.config = target;
    localSystem.config = pkgs.buildPlatform.config;
    crossOverlays = [overrides];
    };

    static-stdenv-maybe = x: if static then cross0.makeStatic x else x;

    zig-stdenv0 = static-stdenv-maybe (cross0.overrideCC cross0.stdenv (cross0.wrapCCWith rec {
    inherit (pkgs) gnugrep coreutils;
    cc = zig-toolchain; libc = cc; libcxx = cc;
    bintools = cross0.wrapBintoolsWith { inherit libc gnugrep coreutils; bintools = cc; };
    # XXX: -march is not compatible
    # https://github.com/ziglang/zig/issues/4911
    extraBuildCommands = ''
    substituteInPlace $out/nix-support/add-local-cc-cflags-before.sh --replace "${target}" "${zig-target}"
    sed -i 's/-march[^ ]* *//g' $out/nix-support/cc-cflags-before
    '';
    }));

    # Aaand .. finally our final stdenv :)
    in zig-stdenv0.override {
    inherit overrides;
    # XXX: Zig doesn't support response file. Nixpkgs wants to use this for clang
    # while zig cc is basically clang, it's still not 100% compatible.
    # Probably should report this as a bug to zig upstream though.
    preHook = ''
    ${cross0.stdenv.preHook}
    export NIX_CC_USE_RESPONSE_FILE=0
    export ZIG_LOCAL_CACHE_DIR="$TMPDIR/zig-cache-${target}"
    export ZIG_GLOBAL_CACHE_DIR="$ZIG_LOCAL_CACHE_DIR"
    '';
    };

    # Rust specific fixes, this lambda is exported so it can be used to wrap any rust toolchain
    wrapRustToolchain = rust-toolchain: let
    rust-config = let
    to-rust-target = {
    "aarch64-darwin" = "aarch64-apple-darwin";
    @@ -92,33 +141,33 @@ let
    "i386-windows" = "i386-windows-gnu";
    "x86_64-windows" = "x86_64-windows-gnu";
    };

    # We need to do some fixup as rustc and zig fight each other
    # FIXME: libcompiler_rt.a removal is a ugly hack that I eventually want to get rid of
    # https://github.com/ziglang/zig/issues/5320
    zig-rust-cc = pkgs.writeShellScript "zig-rust-cc" ''
    args=()
    for v in "$@"; do
    [[ "$v" == *self-contained/crt1.o ]] && continue
    [[ "$v" == *self-contained/crti.o ]] && continue
    [[ "$v" == *self-contained/crtn.o ]] && continue
    [[ "$v" == *self-contained/crtend.o ]] && continue
    [[ "$v" == *self-contained/crtbegin.o ]] && continue
    [[ "$v" == *self-contained/libc.a ]] && continue
    [[ "$v" == -lc ]] && continue
    args+=("$v")
    done
    if ! cc "$@"; then
    lib="$(find "$ZIG_LOCAL_CACHE_DIR" -name libcompiler_rt.a -print -quit)"
    rm "$lib"; touch "$lib"
    cc "$@"
    fi
    '';
    in {
    target = rec {
    name = to-rust-target."${target}" or target;
    cargo = stringAsChars (x: if x == "-" then "_" else x) (toUpper name);
    linker = zig-rust-cc;
    # FIXME: libcompiler_rt.a removal for rust is a ugly hack that I eventually want to get rid of
    # https://github.com/ziglang/zig/issues/5320
    linker = pkgs.writeShellScript "cc-target" ''
    args=()
    for v in "$@"; do
    [[ "$v" == *self-contained/crt1.o ]] && continue
    [[ "$v" == *self-contained/crti.o ]] && continue
    [[ "$v" == *self-contained/crtn.o ]] && continue
    [[ "$v" == *self-contained/crtend.o ]] && continue
    [[ "$v" == *self-contained/crtbegin.o ]] && continue
    [[ "$v" == *self-contained/libc.a ]] && continue
    [[ "$v" == -lc ]] && continue
    args+=("$v")
    done
    export ZIG_LOCAL_CACHE_DIR="$TMPDIR/zig-cache-${target}-rust"
    export ZIG_GLOBAL_CACHE_DIR="$ZIG_LOCAL_CACHE_DIR"
    if ! cc "''${args[@]}"; then
    find "$ZIG_LOCAL_CACHE_DIR" -name libcompiler_rt.a | while read -r f; do
    rm -f "$f"; touch "$f"
    done
    cc "''${args[@]}"
    fi
    '';
    flags = if static then "-C target-feature=+crt-static" else "";
    };
    host = rec {
    @@ -134,69 +183,39 @@ let
    };
    };

    # Bunch of overrides to make sure we don't ever start bootstrapping another cross-compiler
    overrides = self: super: let
    to-override-set = x: genAttrs x (x: zig-toolchain);
    llvmPackages = to-override-set [ "clang" "libclang" "lld" "llvm" "libllvm" "compiler-rt" "libunwind" "libstdcxx" "libcxx" "libcxxapi" "openmp" ];
    in (to-override-set [
    "gcc" "libgcc" "glibc" "bintools" "bintoolsNoLibc" "binutils" "binutilsNoLibc"
    "gccCrossStageStatic" "preLibcCrossHeaders" "glibcCross" "muslCross" "libcCross" "threadsCross"
    ]) // {
    inherit llvmPackages;
    inherit (llvmPackages) clang libclang lld llvm libllvm libunwind libstdcxx libcxx libcxxapi openmp;
    gccForLibs = null; # also disables --gcc-toolchain passed to compiler
    };

    cross1 = import pkgs.path {
    crossSystem.config = target;
    localSystem.config = pkgs.buildPlatform.config;
    crossOverlays = [overrides];
    };

    static-stdenv-maybe = x: if static then cross1.makeStatic x else x;

    # Aaand .. finally our stdenv :)
    in static-stdenv-maybe ((cross1.overrideCC cross1.stdenv (cross1.wrapCCWith rec {
    inherit (pkgs) gnugrep coreutils;
    cc = zig-toolchain;
    libc = cc;
    libcxx = cc;
    bintools = cross1.wrapBintoolsWith { inherit libc gnugrep coreutils; bintools = cc; };
    # XXX: -march is not compatible
    # https://github.com/ziglang/zig/issues/4911
    extraBuildCommands = ''
    substituteInPlace $out/nix-support/add-local-cc-cflags-before.sh --replace "${target}" "${zig-target}"
    sed -i 's/-march[^ ]* *//g' $out/nix-support/cc-cflags-before
    '';
    extraTools = with pkgs; [
    # libsepol tries to use gln for whatever reason on darwin
    # XXX: check if there's already fix for issues like these on nixpkgs that should be integrated
    (writeShellScriptBin "gln" ''${pkgs.coreutils}/bin/ln "$@"'')
    ];
    })).override {
    inherit overrides;
    # XXX: Zig doesn't support response file. Nixpkgs wants to use this for clang
    # while zig cc is basically clang, it's still not 100% compatible.
    # Probably should report this as a bug to zig upstream though.
    preHook = ''
    ${cross1.stdenv.preHook}
    export NIX_CC_USE_RESPONSE_FILE=0
    export ZIG_LOCAL_CACHE_DIR="$TMPDIR/zig-cache"
    export ZIG_GLOBAL_CACHE_DIR="$ZIG_LOCAL_CACHE_DIR"
    # XXX: wrap rustc instead?
    wrapped-cargo = pkgs.writeShellScript "wrapped-cargo" ''
    export CARGO_BUILD_TARGET=${rust-config.target.name}
    export CARGO_TARGET_${rust-config.host.cargo}_LINKER=${rust-config.host.linker}
    export CARGO_TARGET_${rust-config.target.cargo}_LINKER=${rust-config.target.linker}
    export CARGO_TARGET_${rust-config.target.cargo}_RUSTFLAGS="${rust-config.target.flags}"
    ${rust-toolchain}/bin/cargo "$@"
    '';
    });
    in pkgs.symlinkJoin {
    name = "${rust-toolchain.name}-wrapped";
    paths = [ rust-toolchain ];
    postBuild = ''
    rm $out/bin/cargo
    ln -s ${wrapped-cargo} $out/bin/cargo
    '';
    };

    cross = (import pkgs.path {
    crossSystem.config = target;
    localSystem.config = pkgs.buildPlatform.config;
    crossOverlays = [(self: super: { stdenv = zig-stdenv; })];
    crossOverlays = [(self: super: {
    stdenv = zig-stdenv;
    rust = wrapRustToolchain super.rust;
    # XXX: libsepol issue on darwin, should be fixed upstrem instead
    libsepol = super.libsepol.overrideAttrs (old: {
    nativeBuildInputs = with pkgs; old.nativeBuildInputs ++ optionals (stdenv.isDarwin) [
    (writeShellScriptBin "gln" ''${pkgs.coreutils}/bin/ln "$@"'')
    ];
    });
    })];
    });
    in {
    inherit target;
    inherit target wrapRustToolchain;
    stdenv = zig-stdenv;
    pkgs = cross;
    }
  2. Cloudef revised this gist Oct 30, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion zig-stdenv.nix
    Original file line number Diff line number Diff line change
    @@ -171,7 +171,7 @@ let
    extraTools = with pkgs; [
    # libsepol tries to use gln for whatever reason on darwin
    # XXX: check if there's already fix for issues like these on nixpkgs that should be integrated
    (writeShellScriptBin "gln" ''${pkgs.coreutils}/bin/ln'')
    (writeShellScriptBin "gln" ''${pkgs.coreutils}/bin/ln "$@"'')
    ];
    })).override {
    inherit overrides;
  3. Cloudef revised this gist Oct 30, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion zig-stdenv.nix
    Original file line number Diff line number Diff line change
    @@ -199,4 +199,4 @@ in {
    inherit target;
    stdenv = zig-stdenv;
    pkgs = cross;
    }
    }
  4. Cloudef revised this gist Oct 30, 2022. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions zig-stdenv.nix
    Original file line number Diff line number Diff line change
    @@ -98,9 +98,7 @@ let
    # https://github.com/ziglang/zig/issues/5320
    zig-rust-cc = pkgs.writeShellScript "zig-rust-cc" ''
    args=()
    skip_next=0
    for v in "$@"; do
    [[ $skip_next == 1 ]] && { skip_next=0; continue; }
    [[ "$v" == *self-contained/crt1.o ]] && continue
    [[ "$v" == *self-contained/crti.o ]] && continue
    [[ "$v" == *self-contained/crtn.o ]] && continue
  5. Cloudef revised this gist Oct 30, 2022. 1 changed file with 6 additions and 4 deletions.
    10 changes: 6 additions & 4 deletions zig-stdenv.nix
    Original file line number Diff line number Diff line change
    @@ -44,9 +44,6 @@ let
    { name = "bin/ranlib"; path = zig-ranlib; }
    { name = "bin/dlltool"; path = zig-dlltool; }
    { name = "bin/lib"; path = zig-lib; }
    # libsepol tries to use gln for whatever reason on darwin
    # XXX: check if there's already fix for issues like these on nixpkgs that should be integrated
    { name = "bin/gln"; path = "${pkgs.coreutils}/bin/ln"; }
    ] ++ map (x: { name = "bin/${x}"; path = "${bintools}/bin/${x}"; }) [
    "addr2line" "c++filt" "elfedit" "gprof" "gprofng" "nlmconv" "nm" "as"
    "objcopy" "objdump" "readelf" "size" "strings" "windmc" "windres" "strip"
    @@ -173,6 +170,11 @@ let
    substituteInPlace $out/nix-support/add-local-cc-cflags-before.sh --replace "${target}" "${zig-target}"
    sed -i 's/-march[^ ]* *//g' $out/nix-support/cc-cflags-before
    '';
    extraTools = with pkgs; [
    # libsepol tries to use gln for whatever reason on darwin
    # XXX: check if there's already fix for issues like these on nixpkgs that should be integrated
    (writeShellScriptBin "gln" ''${pkgs.coreutils}/bin/ln'')
    ];
    })).override {
    inherit overrides;
    # XXX: Zig doesn't support response file. Nixpkgs wants to use this for clang
    @@ -199,4 +201,4 @@ in {
    inherit target;
    stdenv = zig-stdenv;
    pkgs = cross;
    }
    }
  6. Cloudef revised this gist Oct 30, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion zig-stdenv.nix
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@ with builtins;
    # This seems like race condition with the cache?
    # https://github.com/ziglang/zig/issues/13160
    # https://github.com/ziglang/zig/issues/9711
    # - Rust and zig fight for the ownership libcompiler_rt
    # - Rust and zig fight for the ownership of libcompiler_rt
    # https://github.com/ziglang/zig/issues/5320
    #
    # Why zig for cross-compiling?
  7. Cloudef revised this gist Oct 30, 2022. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions zig-stdenv.nix
    Original file line number Diff line number Diff line change
    @@ -167,9 +167,6 @@ let
    libc = cc;
    libcxx = cc;
    bintools = cross1.wrapBintoolsWith { inherit libc gnugrep coreutils; bintools = cc; };
    # XXX: Zig doesn't support response file. Nixpkgs wants to use this for clang
    # while zig cc is basically clang, it's still not 100% compatible.
    # Probably should report this as a bug to zig upstream though.
    # XXX: -march is not compatible
    # https://github.com/ziglang/zig/issues/4911
    extraBuildCommands = ''
    @@ -178,6 +175,9 @@ let
    '';
    })).override {
    inherit overrides;
    # XXX: Zig doesn't support response file. Nixpkgs wants to use this for clang
    # while zig cc is basically clang, it's still not 100% compatible.
    # Probably should report this as a bug to zig upstream though.
    preHook = ''
    ${cross1.stdenv.preHook}
    export NIX_CC_USE_RESPONSE_FILE=0
  8. Cloudef revised this gist Oct 30, 2022. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions zig-stdenv.nix
    Original file line number Diff line number Diff line change
    @@ -167,7 +167,7 @@ let
    libc = cc;
    libcxx = cc;
    bintools = cross1.wrapBintoolsWith { inherit libc gnugrep coreutils; bintools = cc; };
    # XXX: Zig doesn't support response file. Nix wants to use this for clang
    # XXX: Zig doesn't support response file. Nixpkgs wants to use this for clang
    # while zig cc is basically clang, it's still not 100% compatible.
    # Probably should report this as a bug to zig upstream though.
    # XXX: -march is not compatible
    @@ -199,4 +199,4 @@ in {
    inherit target;
    stdenv = zig-stdenv;
    pkgs = cross;
    }
    }
  9. Cloudef revised this gist Oct 30, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion zig-stdenv.nix
    Original file line number Diff line number Diff line change
    @@ -23,7 +23,7 @@ with builtins;

    let
    zig-stdenv = let
    write-zig-wrapper = cmd: pkgs.writeShellScript "${target}-zig-${cmd}" ''${zig}/bin/zig ${cmd} "$@"'';
    write-zig-wrapper = cmd: pkgs.writeShellScript "zig-${cmd}" ''${zig}/bin/zig ${cmd} "$@"'';
    zig-cc = write-zig-wrapper "cc";
    zig-cxx = write-zig-wrapper "c++";
    zig-ar = write-zig-wrapper "ar";
  10. Cloudef revised this gist Oct 30, 2022. 1 changed file with 81 additions and 100 deletions.
    181 changes: 81 additions & 100 deletions zig-stdenv.nix
    Original file line number Diff line number Diff line change
    @@ -17,10 +17,11 @@ with builtins;
    #
    # Zig can cross-compile out of the box to many target without having to bootstrap the whole cross-compiler and various libcs
    # This means builds are very fast
    #
    # TODO: static == true && glibc target should result in evaluation error
    # TODO: provide zig-overlay.nix

    let
    recursiveMerge = import ./recursive-merge.nix { inherit lib; };

    zig-stdenv = let
    write-zig-wrapper = cmd: pkgs.writeShellScript "${target}-zig-${cmd}" ''${zig}/bin/zig ${cmd} "$@"'';
    zig-cc = write-zig-wrapper "cc";
    @@ -43,6 +44,9 @@ let
    { name = "bin/ranlib"; path = zig-ranlib; }
    { name = "bin/dlltool"; path = zig-dlltool; }
    { name = "bin/lib"; path = zig-lib; }
    # libsepol tries to use gln for whatever reason on darwin
    # XXX: check if there's already fix for issues like these on nixpkgs that should be integrated
    { name = "bin/gln"; path = "${pkgs.coreutils}/bin/ln"; }
    ] ++ map (x: { name = "bin/${x}"; path = "${bintools}/bin/${x}"; }) [
    "addr2line" "c++filt" "elfedit" "gprof" "gprofng" "nlmconv" "nm" "as"
    "objcopy" "objdump" "readelf" "size" "strings" "windmc" "windres" "strip"
    @@ -80,6 +84,61 @@ let
    };
    in to-zig-target."${target}" or unknown-linux-mapper target;

    rust-config = let
    to-rust-target = {
    "aarch64-darwin" = "aarch64-apple-darwin";
    "x86_64-darwin" = "x86_64-macos-gnu";
    "aarch64-windows" = "aarch64-windows-gnu";
    "aarch64_be-windows" = "aarch64_be-windows-gnu";
    "arm-windows" = "arm-windows-gnu";
    "armeb-windows" = "armeb-windows-gnu";
    "i386-windows" = "i386-windows-gnu";
    "x86_64-windows" = "x86_64-windows-gnu";
    };

    # We need to do some fixup as rustc and zig fight each other
    # FIXME: libcompiler_rt.a removal is a ugly hack that I eventually want to get rid of
    # https://github.com/ziglang/zig/issues/5320
    zig-rust-cc = pkgs.writeShellScript "zig-rust-cc" ''
    args=()
    skip_next=0
    for v in "$@"; do
    [[ $skip_next == 1 ]] && { skip_next=0; continue; }
    [[ "$v" == *self-contained/crt1.o ]] && continue
    [[ "$v" == *self-contained/crti.o ]] && continue
    [[ "$v" == *self-contained/crtn.o ]] && continue
    [[ "$v" == *self-contained/crtend.o ]] && continue
    [[ "$v" == *self-contained/crtbegin.o ]] && continue
    [[ "$v" == *self-contained/libc.a ]] && continue
    [[ "$v" == -lc ]] && continue
    args+=("$v")
    done
    if ! cc "$@"; then
    lib="$(find "$ZIG_LOCAL_CACHE_DIR" -name libcompiler_rt.a -print -quit)"
    rm "$lib"; touch "$lib"
    cc "$@"
    fi
    '';
    in {
    target = rec {
    name = to-rust-target."${target}" or target;
    cargo = stringAsChars (x: if x == "-" then "_" else x) (toUpper name);
    linker = zig-rust-cc;
    flags = if static then "-C target-feature=+crt-static" else "";
    };
    host = rec {
    name = to-rust-target."${stdenv.buildPlatform.system}" or target;
    cargo = stringAsChars (x: if x == "-" then "_" else x) (toUpper name);
    # XXX: The fact darwin does not set libiconv linker path may be a bug
    # darwin.cctools / codesign_allocate is a known issue
    # ideally we could just set this to stdenv.cc though
    # https://github.com/NixOS/nixpkgs/pull/148282
    linker = if stdenv.isDarwin then pkgs.writeShellScript "cc-host"
    ''PATH="${pkgs.darwin.cctools}/bin:$PATH" ${stdenv.cc}/bin/cc "$@" -L${pkgs.libiconv}/lib''
    else stdenv.cc;
    };
    };

    # Bunch of overrides to make sure we don't ever start bootstrapping another cross-compiler
    overrides = self: super: let
    to-override-set = x: genAttrs x (x: zig-toolchain);
    @@ -99,123 +158,45 @@ let
    crossOverlays = [overrides];
    };

    cross2 = import pkgs.path {
    crossSystem.config = target;
    localSystem.config = pkgs.buildPlatform.config;
    crossOverlays = [overrides (self: super: {
    stdenv = cross1.stdenvNoCC;
    })];
    };

    static-stdenv-maybe = x: if static then cross2.makeStatic x else x;
    static-stdenv-maybe = x: if static then cross1.makeStatic x else x;

    # Aaand .. finally our stdenv :)
    # If we did not do the 2-stage import here with cross1 and cross2 we would bring gcc with the first stdenv ...
    # There might be better way to go about this, but this works for now.
    in static-stdenv-maybe ((cross2.overrideCC cross1.stdenv (cross2.wrapCCWith rec {
    in static-stdenv-maybe ((cross1.overrideCC cross1.stdenv (cross1.wrapCCWith rec {
    inherit (pkgs) gnugrep coreutils;
    cc = zig-toolchain;
    libc = cc;
    libcxx = cc;
    gnugrep = pkgs.gnugrep;
    coreutils = pkgs.coreutils;
    bintools = cross2.wrapBintoolsWith {
    inherit libc gnugrep coreutils;
    bintools = cc;
    };
    bintools = cross1.wrapBintoolsWith { inherit libc gnugrep coreutils; bintools = cc; };
    # XXX: Zig doesn't support response file. Nix wants to use this for clang
    # while zig cc is basically clang, it's still not 100% compatible.
    # Probably should report this as a bug to zig upstream though.
    # XXX: -march is not compatible
    # https://github.com/ziglang/zig/issues/4911
    extraBuildCommands = ''
    echo "NIX_CC_USE_RESPONSE_FILE=0" >> $out/nix-support/utils.bash
    echo 'export ZIG_LOCAL_CACHE_DIR="$TMPDIR/zig-cache"' >> $out/nix-support/utils.bash
    echo 'export ZIG_GLOBAL_CACHE_DIR="$ZIG_LOCAL_CACHE_DIR"' >> $out/nix-support/utils.bash
    substituteInPlace $out/nix-support/add-local-cc-cflags-before.sh --replace "${target}" "${zig-target}"
    sed -i 's/-march[^ ]* *//g' $out/nix-support/cc-cflags-before
    '';
    })).override {
    overrides = overrides;
    inherit overrides;
    preHook = ''
    ${cross1.stdenv.preHook}
    export NIX_CC_USE_RESPONSE_FILE=0
    export ZIG_LOCAL_CACHE_DIR="$TMPDIR/zig-cache"
    export ZIG_GLOBAL_CACHE_DIR="$ZIG_LOCAL_CACHE_DIR"
    export CARGO_BUILD_TARGET=${rust-config.target.name}
    export CARGO_TARGET_${rust-config.host.cargo}_LINKER=${rust-config.host.linker}
    export CARGO_TARGET_${rust-config.target.cargo}_LINKER=${rust-config.target.linker}
    export CARGO_TARGET_${rust-config.target.cargo}_RUSTFLAGS="${rust-config.target.flags}"
    '';
    });
    in {
    target = target;
    stdenv = zig-stdenv;

    pkgs = (import pkgs.path {
    cross = (import pkgs.path {
    crossSystem.config = target;
    localSystem.config = pkgs.buildPlatform.config;
    crossOverlays = [(self: super: { stdenv = zig-stdenv; })];
    });

    buildRustPackage = args: with pkgs; let
    config = let
    to-rust-target = {
    "aarch64-darwin" = "aarch64-apple-darwin";
    "x86_64-darwin" = "x86_64-macos-gnu";
    "aarch64-windows" = "aarch64-windows-gnu";
    "aarch64_be-windows" = "aarch64_be-windows-gnu";
    "arm-windows" = "arm-windows-gnu";
    "armeb-windows" = "armeb-windows-gnu";
    "i386-windows" = "i386-windows-gnu";
    "x86_64-windows" = "x86_64-windows-gnu";
    };
    in {
    target = rec {
    name = to-rust-target."${target}" or target;
    cargo = stringAsChars (x: if x == "-" then "_" else x) (toUpper name);
    # We need to do some fixup as rustc and zig fight each other
    # FIXME: libcompiler_rt.a removal is a ugly hack that I eventually want to get rid of
    # https://github.com/ziglang/zig/issues/5320
    linker = writeShellScript "cc-target" ''
    args=()
    skip_next=0
    for v in "$@"; do
    [[ $skip_next == 1 ]] && { skip_next=0; continue; }
    [[ "$v" == *self-contained/crt1.o ]] && continue
    [[ "$v" == *self-contained/crti.o ]] && continue
    [[ "$v" == *self-contained/crtn.o ]] && continue
    [[ "$v" == *self-contained/crtend.o ]] && continue
    [[ "$v" == *self-contained/crtbegin.o ]] && continue
    [[ "$v" == *self-contained/libc.a ]] && continue
    [[ "$v" == -lc ]] && continue
    args+=("$v")
    done
    ZIG_LOCAL_CACHE_DIR="$TMPDIR/zig-cache"
    if ! cc "$@"; then
    lib="$(find "$ZIG_LOCAL_CACHE_DIR" -name libcompiler_rt.a -print -quit)"
    rm "$lib"; touch "$lib"
    cc "$@"
    fi
    '';
    flags = if zig-stdenv.hostPlatform.isStatic then "-C target-feature=+crt-static" else "";
    };
    host = rec {
    name = to-rust-target."${stdenv.buildPlatform.system}" or target;
    cargo = stringAsChars (x: if x == "-" then "_" else x) (toUpper name);
    # XXX: The fact darwin does not set libiconv linker path may be a bug
    # darwin.cctools / codesign_allocate is a known issue
    # ideally we could just set this to stdenv.cc though
    # https://github.com/NixOS/nixpkgs/pull/148282
    linker = if stdenv.isDarwin then writeShellScript "cc-host" ''PATH="${darwin.cctools}/bin:$PATH" ${stdenv.cc}/bin/cc "$@" -L${libiconv}/lib'' else stdenv.cc;
    };
    };
    fenix = import (fetchTarball "https://github.com/nix-community/fenix/archive/d87c114ae9d074bcb1d73c2b4debef86dffeddc0.tar.gz") {};
    rust-toolchain = with fenix; combine [ minimal.rustc minimal.cargo targets.${target}.latest.rust-std ];
    naersk = import (fetchFromGitHub {
    owner = "nix-community";
    repo = "naersk";
    rev = "6944160c19cb591eb85bbf9b2f2768a935623ed3";
    sha256 = "sha256-9o2OGQqu4xyLZP9K6kNe1pTHnyPz0Wr3raGYnr9AIgY=";
    }) {
    inherit (pkgs) darwin fetchurl jq lib remarshal rsync runCommandLocal writeText zstd;
    inherit (xorg) lndir;
    cargo = rust-toolchain; rustc = rust-toolchain;
    stdenv = zig-stdenv;
    };
    in naersk.buildPackage (recursiveMerge [ args {
    CARGO_BUILD_TARGET = target;
    "CARGO_TARGET_${config.host.cargo}_LINKER" = config.host.linker;
    "CARGO_TARGET_${config.target.cargo}_LINKER" = config.target.linker;
    "CARGO_TARGET_${config.target.cargo}_RUSTFLAGS" = config.target.flags;
    }]);
    in {
    inherit target;
    stdenv = zig-stdenv;
    pkgs = cross;
    }
  11. Cloudef revised this gist Oct 30, 2022. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions zig-stdenv.nix
    Original file line number Diff line number Diff line change
    @@ -195,6 +195,7 @@ in {
    # XXX: The fact darwin does not set libiconv linker path may be a bug
    # darwin.cctools / codesign_allocate is a known issue
    # ideally we could just set this to stdenv.cc though
    # https://github.com/NixOS/nixpkgs/pull/148282
    linker = if stdenv.isDarwin then writeShellScript "cc-host" ''PATH="${darwin.cctools}/bin:$PATH" ${stdenv.cc}/bin/cc "$@" -L${libiconv}/lib'' else stdenv.cc;
    };
    };
  12. Cloudef created this gist Oct 30, 2022.
    220 changes: 220 additions & 0 deletions zig-stdenv.nix
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,220 @@
    { pkgs ? import <nixpkgs> {}, lib ? pkgs.lib, stdenv ? pkgs.stdenv, zig ? pkgs.zig, static ? true, target }:

    with lib;
    with builtins;

    # Zig based cross-compiling toolchain
    #
    # Major known issues:
    # - Zig seems to randomly fail with parallel builds with 'error: unable to build ... CRT file: BuildingLibCObjectFailed'
    # This seems like race condition with the cache?
    # https://github.com/ziglang/zig/issues/13160
    # https://github.com/ziglang/zig/issues/9711
    # - Rust and zig fight for the ownership libcompiler_rt
    # https://github.com/ziglang/zig/issues/5320
    #
    # Why zig for cross-compiling?
    #
    # Zig can cross-compile out of the box to many target without having to bootstrap the whole cross-compiler and various libcs
    # This means builds are very fast

    let
    recursiveMerge = import ./recursive-merge.nix { inherit lib; };

    zig-stdenv = let
    write-zig-wrapper = cmd: pkgs.writeShellScript "${target}-zig-${cmd}" ''${zig}/bin/zig ${cmd} "$@"'';
    zig-cc = write-zig-wrapper "cc";
    zig-cxx = write-zig-wrapper "c++";
    zig-ar = write-zig-wrapper "ar";
    zig-ranlib = write-zig-wrapper "ranlib";
    zig-dlltool = write-zig-wrapper "dlltool";
    zig-lib = write-zig-wrapper "lib";

    # XXX: Consider LLVM binutils instead?
    bintools = pkgs.bintoolsNoLibc;
    symlinks = [
    { name = "bin/clang"; path = zig-cc; }
    { name = "bin/clang++"; path = zig-cxx; }
    { name = "bin/cc"; path = zig-cc; }
    { name = "bin/c++"; path = zig-cxx; }
    { name = "bin/ld"; path = zig-cc; }
    { name = "bin/gold"; path = zig-cc; }
    { name = "bin/ar"; path = zig-ar; }
    { name = "bin/ranlib"; path = zig-ranlib; }
    { name = "bin/dlltool"; path = zig-dlltool; }
    { name = "bin/lib"; path = zig-lib; }
    ] ++ map (x: { name = "bin/${x}"; path = "${bintools}/bin/${x}"; }) [
    "addr2line" "c++filt" "elfedit" "gprof" "gprofng" "nlmconv" "nm" "as"
    "objcopy" "objdump" "readelf" "size" "strings" "windmc" "windres" "strip"
    ];

    zig-toolchain = pkgs.linkFarm "zig-toolchain" (symlinks
    ++ map (x: let y = last (splitString "/" x.name); in { name = "bin/${target}-${y}"; path = y; }) symlinks) // {
    inherit (pkgs.llvmPackages.libclang) version;
    pname = "zig-toolchain";
    isClang = true;
    libllvm.out = "";
    };

    zig-target = let
    unknown-linux-mapper = x: concatStringsSep "-" (remove "unknown" (splitString "-" x));
    to-zig-target = {
    "armv7l-multiplatform" = "arm-linux-gnueabi";
    "armv7l-hf-multiplatform" = "arm-linux-gnueabihf";
    "armv7l-unknown-linux-musleabi" = "arm-linux-musleabi";
    "armv7l-unknown-linux-musleabihf" = "arm-linux-musleabihf";
    "armv7b-multiplatform" = "armeb-linux-gnueabi";
    "armv7b-hf-multiplatform" = "armeb-linux-gnueabihf";
    "armv7b-unknown-linux-musleabi" = "armeb-linux-musleabi";
    "armv7b-unknown-linux-musleabihf" = "armeb-linux-musleabihf";
    "wasm32-freestanding-musl" = "wasm32-freestanding-musl";
    "wasm32-unknown-wasi" = "wasm32-wasi-musl";
    "aarch64-darwin" = "aarch64-macos-gnu";
    "x86_64-darwin" = "x86_64-macos-gnu";
    "aarch64-windows" = "aarch64-windows-gnu";
    "aarch64_be-windows" = "aarch64_be-windows-gnu";
    "arm-windows" = "arm-windows-gnu";
    "armeb-windows" = "armeb-windows-gnu";
    "i386-windows" = "i386-windows-gnu";
    "x86_64-windows" = "x86_64-windows-gnu";
    };
    in to-zig-target."${target}" or unknown-linux-mapper target;

    # Bunch of overrides to make sure we don't ever start bootstrapping another cross-compiler
    overrides = self: super: let
    to-override-set = x: genAttrs x (x: zig-toolchain);
    llvmPackages = to-override-set [ "clang" "libclang" "lld" "llvm" "libllvm" "compiler-rt" "libunwind" "libstdcxx" "libcxx" "libcxxapi" "openmp" ];
    in (to-override-set [
    "gcc" "libgcc" "glibc" "bintools" "bintoolsNoLibc" "binutils" "binutilsNoLibc"
    "gccCrossStageStatic" "preLibcCrossHeaders" "glibcCross" "muslCross" "libcCross" "threadsCross"
    ]) // {
    inherit llvmPackages;
    inherit (llvmPackages) clang libclang lld llvm libllvm libunwind libstdcxx libcxx libcxxapi openmp;
    gccForLibs = null; # also disables --gcc-toolchain passed to compiler
    };

    cross1 = import pkgs.path {
    crossSystem.config = target;
    localSystem.config = pkgs.buildPlatform.config;
    crossOverlays = [overrides];
    };

    cross2 = import pkgs.path {
    crossSystem.config = target;
    localSystem.config = pkgs.buildPlatform.config;
    crossOverlays = [overrides (self: super: {
    stdenv = cross1.stdenvNoCC;
    })];
    };

    static-stdenv-maybe = x: if static then cross2.makeStatic x else x;

    # Aaand .. finally our stdenv :)
    # If we did not do the 2-stage import here with cross1 and cross2 we would bring gcc with the first stdenv ...
    # There might be better way to go about this, but this works for now.
    in static-stdenv-maybe ((cross2.overrideCC cross1.stdenv (cross2.wrapCCWith rec {
    cc = zig-toolchain;
    libc = cc;
    libcxx = cc;
    gnugrep = pkgs.gnugrep;
    coreutils = pkgs.coreutils;
    bintools = cross2.wrapBintoolsWith {
    inherit libc gnugrep coreutils;
    bintools = cc;
    };
    # XXX: Zig doesn't support response file. Nix wants to use this for clang
    # while zig cc is basically clang, it's still not 100% compatible.
    # Probably should report this as a bug to zig upstream though.
    # XXX: -march is not compatible
    # https://github.com/ziglang/zig/issues/4911
    extraBuildCommands = ''
    echo "NIX_CC_USE_RESPONSE_FILE=0" >> $out/nix-support/utils.bash
    echo 'export ZIG_LOCAL_CACHE_DIR="$TMPDIR/zig-cache"' >> $out/nix-support/utils.bash
    echo 'export ZIG_GLOBAL_CACHE_DIR="$ZIG_LOCAL_CACHE_DIR"' >> $out/nix-support/utils.bash
    substituteInPlace $out/nix-support/add-local-cc-cflags-before.sh --replace "${target}" "${zig-target}"
    sed -i 's/-march[^ ]* *//g' $out/nix-support/cc-cflags-before
    '';
    })).override {
    overrides = overrides;
    });
    in {
    target = target;
    stdenv = zig-stdenv;

    pkgs = (import pkgs.path {
    crossSystem.config = target;
    localSystem.config = pkgs.buildPlatform.config;
    crossOverlays = [(self: super: { stdenv = zig-stdenv; })];
    });

    buildRustPackage = args: with pkgs; let
    config = let
    to-rust-target = {
    "aarch64-darwin" = "aarch64-apple-darwin";
    "x86_64-darwin" = "x86_64-macos-gnu";
    "aarch64-windows" = "aarch64-windows-gnu";
    "aarch64_be-windows" = "aarch64_be-windows-gnu";
    "arm-windows" = "arm-windows-gnu";
    "armeb-windows" = "armeb-windows-gnu";
    "i386-windows" = "i386-windows-gnu";
    "x86_64-windows" = "x86_64-windows-gnu";
    };
    in {
    target = rec {
    name = to-rust-target."${target}" or target;
    cargo = stringAsChars (x: if x == "-" then "_" else x) (toUpper name);
    # We need to do some fixup as rustc and zig fight each other
    # FIXME: libcompiler_rt.a removal is a ugly hack that I eventually want to get rid of
    # https://github.com/ziglang/zig/issues/5320
    linker = writeShellScript "cc-target" ''
    args=()
    skip_next=0
    for v in "$@"; do
    [[ $skip_next == 1 ]] && { skip_next=0; continue; }
    [[ "$v" == *self-contained/crt1.o ]] && continue
    [[ "$v" == *self-contained/crti.o ]] && continue
    [[ "$v" == *self-contained/crtn.o ]] && continue
    [[ "$v" == *self-contained/crtend.o ]] && continue
    [[ "$v" == *self-contained/crtbegin.o ]] && continue
    [[ "$v" == *self-contained/libc.a ]] && continue
    [[ "$v" == -lc ]] && continue
    args+=("$v")
    done
    ZIG_LOCAL_CACHE_DIR="$TMPDIR/zig-cache"
    if ! cc "$@"; then
    lib="$(find "$ZIG_LOCAL_CACHE_DIR" -name libcompiler_rt.a -print -quit)"
    rm "$lib"; touch "$lib"
    cc "$@"
    fi
    '';
    flags = if zig-stdenv.hostPlatform.isStatic then "-C target-feature=+crt-static" else "";
    };
    host = rec {
    name = to-rust-target."${stdenv.buildPlatform.system}" or target;
    cargo = stringAsChars (x: if x == "-" then "_" else x) (toUpper name);
    # XXX: The fact darwin does not set libiconv linker path may be a bug
    # darwin.cctools / codesign_allocate is a known issue
    # ideally we could just set this to stdenv.cc though
    linker = if stdenv.isDarwin then writeShellScript "cc-host" ''PATH="${darwin.cctools}/bin:$PATH" ${stdenv.cc}/bin/cc "$@" -L${libiconv}/lib'' else stdenv.cc;
    };
    };
    fenix = import (fetchTarball "https://github.com/nix-community/fenix/archive/d87c114ae9d074bcb1d73c2b4debef86dffeddc0.tar.gz") {};
    rust-toolchain = with fenix; combine [ minimal.rustc minimal.cargo targets.${target}.latest.rust-std ];
    naersk = import (fetchFromGitHub {
    owner = "nix-community";
    repo = "naersk";
    rev = "6944160c19cb591eb85bbf9b2f2768a935623ed3";
    sha256 = "sha256-9o2OGQqu4xyLZP9K6kNe1pTHnyPz0Wr3raGYnr9AIgY=";
    }) {
    inherit (pkgs) darwin fetchurl jq lib remarshal rsync runCommandLocal writeText zstd;
    inherit (xorg) lndir;
    cargo = rust-toolchain; rustc = rust-toolchain;
    stdenv = zig-stdenv;
    };
    in naersk.buildPackage (recursiveMerge [ args {
    CARGO_BUILD_TARGET = target;
    "CARGO_TARGET_${config.host.cargo}_LINKER" = config.host.linker;
    "CARGO_TARGET_${config.target.cargo}_LINKER" = config.target.linker;
    "CARGO_TARGET_${config.target.cargo}_RUSTFLAGS" = config.target.flags;
    }]);
    }