OK llvm eventually finished building and after dumping all of the info on nix cross compilation I could find on the internet, I managed to get the derivation building for wasm successfully!
I got claude to summarise everything I learned, hopefully useful to someone in the future:
Here’s a tutorial on building Rust packages for WebAssembly using Nix:
Cross-Compiling Rust to WebAssembly with Nix
This guide explains how to set up a Nix build for compiling Rust packages to WebAssembly (specifically targeting wasm32-unknown-unknown
).
Basic Setup
First, create a basic derivation for your WASM package:
{
cargoLock,
rustPlatform,
buildPackages,
lld,
}:
let
target = "wasm32-unknown-unknown";
in
rustPlatform.buildRustPackage rec {
pname = "my-wasm-lib";
version = "0.1.0";
src = ./.; # Or your source location
inherit cargoLock;
# Disable tests since they can't run on WASM
doCheck = false;
# Required dependencies for WASM builds
depsBuildBuild = [
buildPackages.stdenv.cc
lld # Needed for wasm-ld
];
# Configure rustc for WASM
env = {
RUSTFLAGS = ''--cfg=web_sys_unstable_apis -C linker=wasm-ld'';
};
# Copy the WASM file to the output
installPhase = ''
mkdir -p $out/lib
cp target/${target}/release/${pname}.wasm $out/lib/
'';
meta = {
platforms = [ "wasm32-unknown-none" ];
};
}
Adding to Your Flake
In your flake.nix
, you’ll want to configure the cross-compilation target:
{
description = "My WASM Package";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs }: {
packages.x86_64-linux.wasm-build =
nixpkgs.legacyPackages.x86_64-linux.pkgsCross.wasm32-unknown-none.callPackage
./package.nix { };
};
}
Platform-Specific Dependencies
If building on Darwin (macOS), you may need additional dependencies:
# Add to depsBuildBuild:
depsBuildBuild = [
buildPackages.stdenv.cc
lld
] ++ buildPackages.lib.optionals buildPackages.stdenv.isDarwin [
buildPackages.darwin.libiconv
];
Custom Build Profile
If you need a custom build profile for WASM (e.g., for optimization settings), you can specify it:
buildType = "wasm_release"; # Matches profile.[wasm_release] in Cargo.toml
Common Issues and Solutions
-
Linker Issues: If you see errors about unknown linker arguments, make sure you have both lld
in depsBuildBuild
and the correct RUSTFLAGS
variable set.
-
Missing WASM Features: Some crates might need web-specific features enabled. Add them to RUSTFLAGS
:
RUSTFLAGS = "--cfg=web_sys_unstable_apis ...";
-
Build Platform vs Target Platform: Remember that some dependencies might be needed for the build platform (where compilation happens) while others are for the target platform (WASM). Use depsBuildBuild
for build platform dependencies.
Example: Complex Package with Dependencies
Here’s a more complete example for a package using wasm-bindgen:
{
cargoLock,
rustPlatform,
buildPackages,
lld,
}:
let
target = "wasm32-unknown-unknown";
in
rustPlatform.buildRustPackage rec {
pname = "complex-wasm-app";
version = "0.1.0";
src = ./.;
inherit cargoLock;
doCheck = false;
depsBuildBuild = [
buildPackages.stdenv.cc
lld
] ++ buildPackages.lib.optionals buildPackages.stdenv.isDarwin [
buildPackages.darwin.libiconv
];
buildType = "wasm_release";
env = {
RUSTFLAGS = ''--cfg=web_sys_unstable_apis -C linker=wasm-ld'';
};
installPhase = ''
mkdir -p $out/lib
cp target/${target}/${buildType}/*.wasm $out/lib/
'';
meta = {
description = "A complex WASM application";
platforms = [ "wasm32-unknown-none" ];
};
}
This should give you a good foundation for building Rust packages targeting WebAssembly with Nix. Remember to test your builds on both Linux and Darwin if you’re targeting multiple platforms.