Fixes: Warning: `stdenv.lib` is deprecated and will be removed in the next release. Please use `pkgs.lib` instead. For more information see https://github.com/NixOS/nixpkgs/issues/108938
5.7 KiB
Cross compilation of Haskell projects involves building a version of GHC that outputs code for the target platform, and providing builds of all library dependencies for that platform.
First, understand how to cross-compile a normal package from Nixpkgs. Matthew Bauer's Beginners' guide to cross compilation in Nixpkgs is a useful resource.
Using an example from the guide, this builds GNU Hello for a Raspberry Pi:
nix build -f '<nixpkgs>' pkgsCross.raspberryPi.hello
We will use the same principle in Haskell.nix — replacing the normal
package set pkgs with a cross-compiling package set
pkgsCross.raspberryPi.
Raspberry Pi example
This is an example of using Haskell.nix to build the Bench command-line utility, which is a Haskell program.
{ pkgs ? import <nixpkgs> {} }:
let
haskellNix = import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz);
native = haskellNix { inherit pkgs; };
in
native.haskellPackages.bench.components.exes.bench
Now switch the package set as in the previous example:
{ pkgs ? import <nixpkgs> {} }:
let
haskellNix = import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz);
raspberryPi = haskellNix { pkgs = pkgs.pkgsCross.raspberryPi; };
in
raspberryPi.haskellPackages.bench.components.exes.bench
You should be prepared for a long wait because it first needs to build GHC, before building all the Haskell dependencies of Bench. If all of these dependencies compiled successfully, I would be very surprised!
!!! hint The above example won't build, but you can try and see, if you like. It will fail on clock-0.7.2, which needs a patch to build.
To fix the build problems, you must add extra configuration to the
package set. Your project will have a mkStackPkgSet or
mkCabalProjectPkgSet. It is there where you must add
module options for setting compiler flags, adding patches, and so on.
!!! note
Note that haskell.nix will automatically use qemu to emulate the target
when necessary to run Template Haskell splices.
Static executables with Musl libc
Another application of cross-compiling is to produce fully static binaries for Linux. For information about how to do that with the Nixpkgs Haskell infrastructure (not Haskell.nix), see nh2/static‑haskell‑nix. Vaibhav Sagar's linked blog post is also very informative.
{ pkgs ? import <nixpkgs> {} }:
let
haskellNix = import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz);
musl64 = haskellNix { pkgs = pkgs.pkgsCross.musl64; };
in
musl64.haskellPackages.bench.components.exes.bench
This example will build Bench linked against Musl libc. However the executable will still be dynamically linked. To get fully static executables you must add package overrides to:
- Disable dynamic linking
- Provide static versions of system libraries. (For more details, see Vaibhav's article).
{
packages.bench.components.exes.bench.configureFlags =
lib.optionals stdenv.hostPlatform.isMusl [
"--disable-executable-dynamic"
"--disable-shared"
"--ghc-option=-optl=-pthread"
"--ghc-option=-optl=-static"
"--ghc-option=-optl=-L${gmp6.override { withStatic = true; }}/lib"
"--ghc-option=-optl=-L${zlib.static}/lib"
];
}
!!! note "Licensing"
Note that if copyleft licensing your program is a problem for you,
then you need to statically link with integer-simple rather than
integer-gmp. However, at present, Haskell.nix does not provide
an option for this.
How to cross-compile your project
Set up your project Haskell package set.
# default.nix
{ pkgs ? import <nixpkgs> {}
let
# Import the Haskell.nix library,
haskell = import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {
inherit pkgs;
};
# Instantiate a package set using the generated file.
pkgSet = haskell.mkCabalProjectPkgSet {
plan-pkgs = import ./pkgs.nix;
pkg-def-extras = [];
modules = [
{
# You will need to put build fixes here.
}
];
};
in
pkgSet.config.hsPkgs
Apply that package set to the Nixpkgs cross package sets that you are interested in.
We are going to expand the pkgs.pkgsCross shortcut to be more
explicit.
let
pkgs = import <nixpkgs> {}
in {
shortcut = pkgs.pkgsCross.SYSTEM;
actual = import <nixpkgs> { crossSystem = pkgs.lib.systems.examples.SYSTEM; };
}
In the above example, for any SYSTEM, shortcut and actual are
the same package set.
# release.nix
let
myProject = import ./default.nix;
pkgsNative = import <nixpkgs> {};
pkgsRaspberryPi = import <nixpkgs> {
crossSystem = pkgsNative.lib.systems.examples.raspberryPi;
};
native = myProject { pkgs = pkgsNative; };
crossRaspberryPi = myProject { pkgs = pkgsRaspberryPi; };
in {
my-project-native = native.my-project.components.exes.my-project;
my-project-raspberry-pi = crossRaspberryPi.my-project.components.exes.my-project;
}
Try to build it, and apply fixes to the modules list, until there
are no errors left.