Skip to main content
rust.compile enables cross-compilation of Rust projects to different platforms using cargo-zigbuild, which leverages Zig’s cross-compilation capabilities.

Overview

Cross-compile Rust packages to any platform supported by Zig:
packages.myapp-windows = lib.rust.compile {
  package = pkgs.rustPlatform.buildRustPackage {
    pname = "myapp";
    version = "1.0.0";
    src = ./.;
    cargoLock.lockFile = ./Cargo.lock;
  };
  target = "x86_64-pc-windows-gnu";
};

Function Signature

rust.compile :: { package, target?, ... } -> Derivation
package
derivation
required
A Rust package built with rustPlatform.buildRustPackage.
target
string
default:"stdenv.buildPlatform.rust.rustcTarget"
Target platform triple. Examples:
  • "x86_64-unknown-linux-gnu"
  • "aarch64-unknown-linux-gnu"
  • "x86_64-pc-windows-gnu"
  • "x86_64-apple-darwin"
  • "aarch64-apple-darwin"

Return Value

Returns a modified derivation with:
  • Cross-compiled binary in $out/bin/<name>
  • .exe extension for Windows targets
  • meta.mainProgram set correctly
  • auditable = false (cargo-auditable removed for compatibility)
  • doCheck = false (tests disabled for cross-compilation)

Examples

Basic Cross-Compilation

let
  myapp = pkgs.rustPlatform.buildRustPackage {
    pname = "myapp";
    version = "1.0.0";
    src = ./.;
    cargoLock.lockFile = ./Cargo.lock;
  };
in
{
  packages = {
    # Native build
    default = myapp;

    # Cross-compile to Windows
    windows = lib.rust.compile {
      package = myapp;
      target = "x86_64-pc-windows-gnu";
    };
  };
}

Multi-Platform Release

flake.nix
{
  outputs = { self, nixpkgs, nur }:
    nur.lib.mkFlake { } (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
        lib = nur.lib.${system};

        myapp = pkgs.rustPlatform.buildRustPackage {
          pname = "myapp";
          version = "1.0.0";
          src = ./.;
          cargoLock.lockFile = ./Cargo.lock;
        };
      in
      {
        packages = {
          default = myapp;

          # Linux
          linux-x86_64 = lib.rust.compile {
            package = myapp;
            target = "x86_64-unknown-linux-gnu";
          };

          linux-aarch64 = lib.rust.compile {
            package = myapp;
            target = "aarch64-unknown-linux-gnu";
          };

          # macOS
          macos-x86_64 = lib.rust.compile {
            package = myapp;
            target = "x86_64-apple-darwin";
          };

          macos-aarch64 = lib.rust.compile {
            package = myapp;
            target = "aarch64-apple-darwin";
          };

          # Windows
          windows-x86_64 = lib.rust.compile {
            package = myapp;
            target = "x86_64-pc-windows-gnu";
          };
        };
      }
    );
}

Using with Override

Since compile uses lib.makeOverridable:
let
  myapp = pkgs.rustPlatform.buildRustPackage { /* ... */ };

  # Create a base compile function
  compileFor = lib.rust.compile { package = myapp; };
in
{
  packages = {
    # Override for different targets
    windows = compileFor.override { target = "x86_64-pc-windows-gnu"; };
    macos = compileFor.override { target = "aarch64-apple-darwin"; };
  };
}

Release Automation

{
  apps = lib.mkApps {
    release = {
      script = ''
        set -euo pipefail

        VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')
        echo "Building release v$VERSION for all platforms..."

        # Build all targets
        nix build .#linux-x86_64
        nix build .#linux-aarch64
        nix build .#macos-x86_64
        nix build .#macos-aarch64
        nix build .#windows-x86_64

        # Create release archives
        mkdir -p dist
        tar czf "dist/myapp-v$VERSION-linux-x86_64.tar.gz" -C result/bin .
        tar czf "dist/myapp-v$VERSION-macos-aarch64.tar.gz" -C result/bin .
        zip "dist/myapp-v$VERSION-windows-x86_64.zip" -j result/bin/*.exe

        echo "Release artifacts in dist/"
        ls -lh dist/
      '';
      deps = with pkgs; [ cargo-metadata jq nix gnutar gzip zip ];
    };
  };
}

Supported Targets

Any target supported by both Rust and Zig can be used. Common targets:

Linux

  • x86_64-unknown-linux-gnu
  • aarch64-unknown-linux-gnu
  • x86_64-unknown-linux-musl (static linking)
  • aarch64-unknown-linux-musl

macOS

  • x86_64-apple-darwin (Intel)
  • aarch64-apple-darwin (Apple Silicon)

Windows

  • x86_64-pc-windows-gnu
  • i686-pc-windows-gnu

Other

  • wasm32-unknown-unknown (WebAssembly)
  • wasm32-wasi
For a full list, see Rust platform support.

Implementation Details

cargo-zigbuild Integration

The implementation uses cargo-zigbuild instead of regular cargo build for cross-compilation:
if [[ "${target}" == "${stdenv.hostPlatform.rust.rustcTarget}" ]]; then
  # Native build - use regular cargo
  cargo build --release --target-dir "$build_dir"
else
  # Cross-compilation - use cargo-zigbuild
  cargo zigbuild --release --target-dir "$build_dir" --target "${target}"
fi
This automatically handles:
  • Cross-compilation toolchain setup
  • Linker configuration
  • C library linking

cargo-auditable Removal

cargo-auditable is filtered out from nativeBuildInputs because it’s not compatible with cargo-zigbuild:
nativeBuildInputs = [
  cargo-zigbuild
  jq
]
++ builtins.filter (
  drv: builtins.match ".*auditable.*" (lib.getName drv) == null
) prev.nativeBuildInputs;

Binary Name Detection

The binary name is extracted from Cargo.toml using cargo metadata:
package_name=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].name')
release=$(find $build_dir -type f -executable -name "${package_name}*")
Windows binaries automatically get .exe extension:
bin = if (builtins.match ".*windows.*" target != null) then "${name}.exe" else name;

Source Location

Implemented in: libs/rust/compile.nix:8

cargo-zigbuild Advantages

Featurecargo-zigbuildTraditional cross-compilation
Setup complexityLow (automatic)High (manual toolchain)
C library supportBuilt-inRequires cross-compilers
Platform coverageExtensiveLimited
Build speedFastVaries
ReproducibilityHighMedium

Troubleshooting

Missing C Dependencies

If your Rust project depends on C libraries (via FFI), you may need to provide them:
lib.rust.compile {
  package = myapp.overrideAttrs (old: {
    buildInputs = old.buildInputs ++ [
      pkgs.openssl
      pkgs.postgresql
    ];
  });
  target = "x86_64-pc-windows-gnu";
}
Some targets may have linking issues. Try using the -musl variant for static linking:
target = "x86_64-unknown-linux-musl"; # Static binary

Binary Not Found

If the binary isn’t found after build, check Cargo.toml for the correct binary name:
[[bin]]
name = "my-custom-name"
path = "src/main.rs"

Best Practices

  1. Test on target platforms: Always test cross-compiled binaries on actual target systems
  2. Use musl for Linux: Static binaries are more portable
  3. Pin Rust version: Use rust-bin overlay or similar for reproducible builds
  4. Minimize C dependencies: Pure Rust code cross-compiles more reliably
  5. Check target tier: Tier 1 targets have better support than Tier 2/3

Complete Example

Cargo.toml

[package]
name = "myapp"
version = "1.0.0"
edition = "2021"

[dependencies]
clap = { version = "4.4", features = ["derive"] }
tokio = { version = "1.35", features = ["full"] }

flake.nix

{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    nur.url = "github:your-org/nur-nix";
  };

  outputs = { self, nixpkgs, nur }:
    nur.lib.mkFlake { } (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
        lib = nur.lib.${system};

        myapp = pkgs.rustPlatform.buildRustPackage {
          pname = "myapp";
          version = "1.0.0";
          src = ./.;
          cargoLock.lockFile = ./Cargo.lock;
        };
      in
      {
        packages = {
          default = myapp;
          linux = lib.rust.compile { package = myapp; };
          windows = lib.rust.compile {
            package = myapp;
            target = "x86_64-pc-windows-gnu";
          };
          macos = lib.rust.compile {
            package = myapp;
            target = "aarch64-apple-darwin";
          };
        };

        devShells.default = pkgs.mkShell {
          packages = with pkgs; [
            cargo
            rustc
            cargo-zigbuild
            rust-analyzer
          ];
        };
      }
    );
}

Go Utilities

Cross-compile Go applications

Deno Utilities

Compile TypeScript/JavaScript to binaries

Build docs developers (and LLMs) love