Cross Compile Rust for Windows

Hi,

I’m trying to set up a nix-shell that can compile rust programs for windows. I mostly followed this guide but with the addition of the x86_64-pc-windows-gnu cargo target. When I try to use cargo to build for this target I get the following error:

error: linker `x86_64-w64-mingw32-gcc` not found
  |
  = note: No such file or directory (os error 2)

error: aborting due to previous error

To try and fix this I added pkgs.pkgsCross.mingwW64.gcc to my shell.nix’s buildInputs. After a long compilation attempt my nix-shell command fails with the below:

configure: error: could not determine how to read list of mounted file systems
builder for '/nix/store/zmd8lg97kbq3k1f8qn8503ikzbmw3c4c-coreutils-8.31-x86_64-w64-mingw32.drv' failed with exit code 1
cannot build derivation '/nix/store/1avnd47l9pz81lz09zf2awg343a2fwxy-gcc-debug-wrapper-8.3.0.drv': 1 dependencies couldn't be built
error: build of '/nix/store/1avnd47l9pz81lz09zf2awg343a2fwxy-gcc-debug-wrapper-8.3.0.drv' failed

What is the best way to make a mingw linker available for cargo to build windows binaries?

5 Likes

Is this a sensible question?

Maybe to reduce the scope of my question, is there a way to install a mingw compatible compiler on Nix? If I can do that maybe I can figure out how to get a similar thing working with the rust overlay.

1 Like

The nix-shell file looks like this:

with import <nixpkgs> {
  crossSystem = {
    config = "x86_64-w64-mingw32";
  };
};

mkShell {
  # buildInputs = [ zlib ];
}

Install the toolchain with rustup:

rustup toolchain install stable-x86_64-pc-windows-gnu

Put in .cargo/config the following lines:

[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"

Sorry, I haven’t updated this in a while.

My equivalent nix-shell (using Niv, and the Mozilla overlay) is:

let 
  sources = import ./nix/sources.nix;
  rust = import ./nix/rust.nix{inherit sources;};
  pkgs = (import <nixpkgs>{ crossSystem = {config = "x86_64-w64-mingw32";}; });
in 
  pkgs.mkShell {
    nativeBuildInputs = [ rust ];
  }

And a rust.nix:

{sources ? import ./sources.nix}:
let
  pkgs = import sources.nixpkgs{ overlays = [ (import sources.nixpkgs-mozilla) ]; };
  channel = "nightly";
  date = "2020-10-14";
  targets = [ "x86_64-pc-windows-gnu" ];
  chan = (pkgs.rustChannelOfTargets channel date targets);
in chan

My .cargo/config is identical to yours, and after entering the shell I cans see that the corresponding compiler does exist in the path:


which x86_64-w64-mingw32-gcc /nix/store/ir564pg2s96nmrc3xlvfh065wk9hblj6-x86_64-w64-mingw32-stage-final-gcc-debug-wrapper-8.3.0/bin/x86_64-w64-mingw32-gcc

This gets me a shell with a mingw compiler and a rust toolchain. I can build native Linux executables fine. However, when I try to cross-compile for x86_64-pc-windows-gnu I get a linker error related to pthreads:


= note: /nix/store/n8rscm777z53g1cg6knwwphik3aprx6j-x86_64-w64-mingw32-binutils-2.31.1/bin/x86_64-w64-mingw32-ld: cannot find -l:libpthread.a collect2: error: ld returned 1 exit status

error: aborting due to previous error; 2 warnings emitted


This pthreads issue is where I’ve been stuck for the past couple days. It’s also not clear to me why cargo seems to still be linking with ld instead of gcc despite .cargo/config. Any ideas?

1 Like

Make sure to include pkgs-mingw.windows.mingw_w64_pthreads in your buildInputs (NOT nativeBuildInputs). That finally got the pthreads thing to work for me.

1 Like

I am having this same problem.
When i add mingw_w64_pthreads:

 note: /nix/store/na3khzhlqiq89pf75hmys8icr3qxgy13-x86_64-w64-mingw32-binutils-2.31.1/bin/x86_64-w64-mingw32-ld: cannot find -l:libpthread.a
          collect2: error: ld returned 1 exit status

When i also add statndard windows.pthreads:
(it goes on and on, this is just the end.)

          /nix/store/na3khzhlqiq89pf75hmys8icr3qxgy13-x86_64-w64-mingw32-binutils-2.31.1/bin/x86_64-w64-mingw32-ld: /home/eli/Desktop/Code/Rust/vdcp-spoof/target/x86_64-pc-windows-gnu/release/deps/libring-ba44c9cabb44a5a9.rlib(ring-ba44c9cabb44a5a9.ring.dtsgsr3y-cgu.0.rcgu.o):ring.dtsgsr3y-cgu.:(.text+0x34fb): undefined reference to `LIMBS_less_than'
          /nix/store/na3khzhlqiq89pf75hmys8icr3qxgy13-x86_64-w64-mingw32-binutils-2.31.1/bin/x86_64-w64-mingw32-ld: /home/eli/Desktop/Code/Rust/vdcp-spoof/target/x86_64-pc-windows-gnu/release/deps/libring-ba44c9cabb44a5a9.rlib(ring-ba44c9cabb44a5a9.ring.dtsgsr3y-cgu.0.rcgu.o):ring.dtsgsr3y-cgu.:(.text+0x352c): undefined reference to `GFp_bn_from_montgomery_in_place'
          collect2: error: ld returned 1 exit status

Any ideas?

I had the itch to cross compile for Windows as well. None of the solutions above worked for me when I first came across this page. I didn’t like how nixpkgs’ cross compilation by setting crossSystem would build a different compiler for every target, and I could never get it working. I ended up discovering a way that worked using naersk, and wrote some example configurations. There’s a lot of comments in them which will hopefully address any questions about why I have particular settings. Add examples and templates for cross compilation. by gcoakes · Pull Request #161 · nix-community/naersk · GitHub I hope this helps anyone else.

3 Likes

Thanks for the help everyone! The info here got me successfully cross-compiling for Windows. I want to note that since 2021 pkgs-mingw.windows.mingw_w64_pthreads seems to have moved to windows.mingw_w64_pthreads.

Edit: Well I was able to build a binary, but wine says it’s in the wrong format so I might not actually have it working.

To summarize all of the steps that I added to my existing cross-compilation setup to support windows:

Initialize nixpkgs with the proper cross system:

pkgs = import nixpkgs {
  localSystem = "x86_64-linux";
  crossSystem.config = "x86_64-w64-mingw32"; # Note the `config` part!
};

Set the Rust toolchain build target to x86_64-pc-windows-gnu. I use rust-overlay so I set targets when I initialize my toolchain to get support for that target. You can do that with an override if you want to:

rustToolChain = (pkgs.pkgsBuildHost.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml).override { targets = ["x86_64-pc-windows-gnu"] };

When building with a tool that uses Cargo, like crane, set the build target by setting CARGO_BUILD_TARGET = "x86_64-pc-windows-gnu"

Set the linker to x86_64-w64-mingw32-gcc. I set my linker like this for all of my cross-compilation targets:

envCase = triple: lib.strings.toUpper (builtins.replaceStrings [ "-" ] [ "_" ] triple);

"CARGO_TARGET_${envCase buildTarget}_LINKER" = "${stdenv.cc.targetPrefix}cc";

Get the necessary pthreads lib:

buildInputs = lib.optionals hostPlatform.isWindows [
  pkgs.windows.mingw_w64_pthreads
];