Cross Compiling
For building arm software check out the Article NixOS on ARM
If you are looking for building 32bit software, check out Packaging/32bit Applications
Quick example to cross compile a package: Cheatsheet#Cross-compile_packages.
Cross-Compiling a package in nixpkgs
Cross-compilation is well supported in nixpkgs since 18.09. The basic idea is to use pkgsCross.platform instead of pkgs:
nix-build '<nixpkgs>' -A pkgsCross.raspberryPi.openssl How to obtain a shell with a cross compiler
Create a file crossShell.nix as follows:
with import <nixpkgs> { crossSystem = { config = "aarch64-unknown-linux-gnu"; }; }; mkShell { buildInputs = [ zlib ]; # your dependencies here } and then use it to obtain a shell:
nix-shell crossShell.nix
The resulting shell contains a cross toolchain and zlib in this example. Note that contrary to native shells, the compiler and some other tools are prefixed: there is no gcc but a aarch64-unknown-linux-gnu-gcc. Some convenience environment variables expand to the prefixed version of tools: $CC, $LD...
Examples of how to specify your target system can be found in lib/systems/examples.nix. If the exact system you are targeting is available in this file then you can use the existing definition as in the following example:
let pkgs = import <nixpkgs> { crossSystem = (import <nixpkgs/lib>).systems.examples.armv7l-hf-multiplatform; }; in mkShell {} Even shorter:
let pkgs = import <nixpkgs> {}; in pkgs.pkgsCross.armv7l-hf-multiplatform.mkShell {} The examples above do not work as is with build dependencies (nativeBuildInputs). A solution is to use callPackage to enable splicing:
let pkgs = import <nixpkgs> { crossSystem = { config = "aarch64-unknown-linux-gnu"; }; }; in pkgs.callPackage ( {mkShell, pkg-config, zlib}: mkShell { nativeBuildInputs = [ pkg-config ]; # you build dependencies here buildInputs = [ zlib ]; # your dependencies here } ) {} See also
#49526.
Lazy cross-compiling
If you target "aarch64-unknown-linux-gnu", there is a nice way to reduce amount of cross-compiling and side-step journey to fix cross errors. The idea is to fetch non-essential dependencies from binary cache of regular aarch64 binaries.
Say we are building SDL2.
let # this will use aarch64 binaries from binary cache, so no need to build those pkgsArm = import <nixpkgs> { config = {}; overlays = []; system = "aarch64-linux"; }; # these will be your cross packages pkgsCross = import <nixpkgs> { overlays = [(self: super: { # we want to hack on SDL, don't want to hack on those. Some even don't cross-compile inherit (pkgsArm) xorg libpulseaudio libGL guile systemd libxkbcommon ; })]; crossSystem = { config = "aarch64-unknown-linux-gnu"; }; }; in pkgsCross.SDL2.override { # those shouldn't be neither pkgsCross, nor pkgsArm # because those trigger # cannot execute binary file: Exec format error # in this case it was enough to just use buildPackages variants # but in general there may be problems inherit (pkgsCross.buildPackages) wayland wayland-protocols ; }
How to specify dependencies
Depending in which if packages are required at build time or at runtime they need to go to different inputs the derivation.
- If it is used at build-time it's
depsBuildXXX- compiler producing native binaries go to
depsBuildBuild; - compiler producing cross binaries, all setup hooks and programs executed by the builder go to
depsBuildHost:- common examples:
pkg-config, autoreconfHook, makeWrapper, intltool, bison, flex.
- common examples:
- compiler producing native binaries go to
- If it is used at run-time it's
depsHostXXX. [Static linking doesn't effect this, even if it allows us to forget where things came from.]- if it’s an interpreter that will be needed by an installed script, it should go in
depsHostTarget. - otherwise it is probably only needed at build time and can go in
depsBuildHost
- if it’s an interpreter that will be needed by an installed script, it should go in
- If it is a tool and "acts" (e.g. helps build) on build-time stuff, then it's
depsXXXBuild. - If it is a tool and "acts" on run-time stuff, then it's
depsXXXHost. - If it is not a tool, it's
depsXXX(XXX+1)(build + 1 == host, host +1 == target). For backwards compatibility usenativeBuildInputsinstead ofdepsBuildHostandbuildInputsinstead ofdepsHostTarget.
Source: https://github.com/NixOS/nixpkgs/pull/50881#issuecomment-440772499
References
- Nixpkgs manual on cross compiling
- Nixpkgs manual on Specifying dependencies
