Skip to content

twhitehead/notes-nix-cross

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Overview

I was trying to build a Windows test program for detailing a bug in Wine. Nix has fabulous cross compilation support, so I figured this should be easy to do. It is, but trying to figure out how to do it, was not.

The key non-obvious part is that you need to use callPackage as it does some hackery to make the various versions of each package available all under the same package name.

Details

When you are cross compiling

buildInputs = [ foo ];

is foo built for the other system while

nativeBuildInputs = [ foo ];

is foo built for the local system. These are not the same foo.

How can that be? mkDerivation actually uses foo.__spliced.<version>, if it is available, where <version> is picked depends on whether it was placed in buildInputs or nativeBuildInputs. As it is callPackage that augments foo with these versions (see the code in pkgs/top-level/splice.nix), it is absolutely necessary it use callPackages when build cross packages.

With that in mind, hopefully it is understandable that this will work

example = pkgs.pkgsCross.mingw32.callPackage (
  { stdenv, cmake }:
  stdenv.mkDerivation {
    name = "example";
    version = "1.0.0";
    src = ./.;
    nativeBuildInputs = [ cmake ];
  }
) { };

as callPackage is augmenting cmake to include the version built for the local system so nativeBuildInputs can use it, while this

example = with pkgs.pkgsCross.mingw32; stdenv.mkDerivation {
    name = "example";
    version = "1.0.0";
    src = ./.;
    nativeBuildInputs = [ cmake ];
  }
) { };

will not as cmake has not been augemented, so you are going to get the one built for the other system, which will not be able to run on the local system as will be required to do the build.

Hopefully it is also obvious that

example = with pkgs.pkgsCross.mingw32.buildPackages; stdenv.mkDerivation {
    name = "example";
    version = "1.0.0";
    src = ./.;
    nativeBuildInputs = [ cmake ];
  }
) { };

is not correct either. Although it gives you a cmake built for the local system, it also switches to the stdenv that was used to build mingw32. This gives you the the compiler that was used to build mingw32 and not the mingw32 compiler.

If you want to do it without the callPackage magic, then you have to manually specifying where to get each package from. This works

example = with pkgs.pkgsCross.mingw32; stdenv.mkDerivation {
    name = "example";
    version = "1.0.0";
    src = ./.;
    nativeBuildInputs = [ buildPackages.cmake ];
  }
) { };

as that is essentially what the splicing magic is doing behind the scenes.

Nix

This repo includes a hello world program, a trivial CMakeLists.txt file, and a nix flake with the above code to show just how easy it is to cross compile it for Windows once you know the callPackage detail.

Give it a go. Easy to build

nix flake update
nix build .#hello
ls result/bin

and easy to run

nix shell nixpkgs#wine
wine result/bin/hello.exe
exit

About

How to cross compile using nix (the secret is callPackage)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published