mirror of
https://github.com/NixOS/nix.dev.git
synced 2024-10-18 14:32:43 -04:00
Tutorial for cross-compilation
This commit is contained in:
parent
dedcd7b6b3
commit
365c3989b0
|
@ -383,6 +383,8 @@ linkcheck_ignore = [
|
|||
r'https://app.terraform.io',
|
||||
# Seems like README anchors aren't parsable?
|
||||
r'https://github.com/cachix/install-nix-action',
|
||||
# It's dynamic
|
||||
r'https://matrix.to'
|
||||
]
|
||||
|
||||
# Anchors are not present in HTML
|
||||
|
|
|
@ -14,6 +14,7 @@ Welcome to nix.dev
|
|||
- Installing software from source code.
|
||||
- Transparent build caching via binary caches.
|
||||
- Strong support for software auditability.
|
||||
- First-class cross-compilation support.
|
||||
- Remote builds.
|
||||
- Remote deployments.
|
||||
- Atomic upgrades and rollbacks.
|
||||
|
|
257
source/tutorials/cross-compilation.rst
Normal file
257
source/tutorials/cross-compilation.rst
Normal file
|
@ -0,0 +1,257 @@
|
|||
.. _ref-cross-compilation:
|
||||
|
||||
Cross-compilation
|
||||
=================
|
||||
|
||||
Cross-compilation is the act of **compiling code** on the **build platform**
|
||||
to the **host platform**, where the compiled **executable runs**. [#]_
|
||||
|
||||
It's needed when the host platform has limited resources (such as CPU)
|
||||
or when it's not easily accessible for development.
|
||||
|
||||
Nix community has world-class support for cross-compilation,
|
||||
after years of hard work from our community.
|
||||
|
||||
.. [#] Terminology for cross-compilation platforms differs between build systems,
|
||||
Nix community has chosen to follow
|
||||
`autoconf terminology <https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Hosts-and-Cross_002dCompilation.html>`_.
|
||||
|
||||
.. note:: macOS/Darwin is a special case, as not the whole OS is Open Source.
|
||||
It's only possible to cross-compile between ``aarch64-darwin`` and ``x86_64-darwin``.
|
||||
|
||||
|
||||
What's a target platform?
|
||||
-------------------------
|
||||
|
||||
There's actually a third platform named target.
|
||||
|
||||
It matters in cases where you'd like to distribute a compiler binary,
|
||||
as you'd then like to build a compiler on the build platform, compile code on the
|
||||
target plaform and run the final executable on the host platform.
|
||||
|
||||
Since that's rarely needed, we'll treat target platform the same as the build.
|
||||
|
||||
|
||||
Determining the host platform
|
||||
-----------------------------
|
||||
|
||||
The build platform is determined automatically by Nix
|
||||
as it can just guess it during the configure phase.
|
||||
|
||||
The host platform is best determined by running on the host platform:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ bash $(nix-build '<nixpkgs>' -A gnu-config)/config.guess
|
||||
aarch64-unknown-linux-gnu
|
||||
|
||||
In case that's not possible (when the host platform is not easily accessible
|
||||
for development), it has to be constructed manually via the following template:
|
||||
|
||||
.. code::
|
||||
|
||||
<cpu>-<vendor>-<os>-<abi>
|
||||
|
||||
Note that ``<vendor>`` is often ``unknown`` and ``<abi>`` is optional.
|
||||
There's also no unique identifier for a platform, for example ``unknown`` and
|
||||
``pc`` are interchangeable (hence it's called config.guess).
|
||||
|
||||
Some other common examples of platforms:
|
||||
|
||||
- aarch64-apple-darwin14
|
||||
- aarch64-pc-linux-gnu
|
||||
- x86_64-w64-mingw32
|
||||
- aarch64-apple-ios
|
||||
|
||||
|
||||
Choosing the host platform with Nix
|
||||
-----------------------------------
|
||||
|
||||
Nixpkgs comes with a set of predefined host plaform applied to all packages.
|
||||
|
||||
It's possible to list predefined sets via shell completion:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ nix-build '<nixpkgs>' -A pkgsCross.<TAB>
|
||||
pkgsCross.aarch64-android pkgsCross.musl32
|
||||
pkgsCross.aarch64-android-prebuilt pkgsCross.musl64
|
||||
pkgsCross.aarch64be-embedded pkgsCross.muslpi
|
||||
pkgsCross.aarch64-darwin pkgsCross.musl-power
|
||||
pkgsCross.aarch64-embedded pkgsCross.or1k
|
||||
pkgsCross.aarch64-multiplatform pkgsCross.pogoplug4
|
||||
pkgsCross.aarch64-multiplatform-musl pkgsCross.powernv
|
||||
pkgsCross.amd64-netbsd pkgsCross.ppc64
|
||||
pkgsCross.arm-embedded pkgsCross.ppc64-musl
|
||||
pkgsCross.armhf-embedded pkgsCross.ppc-embedded
|
||||
pkgsCross.armv7a-android-prebuilt pkgsCross.ppcle-embedded
|
||||
pkgsCross.armv7l-hf-multiplatform pkgsCross.raspberryPi
|
||||
pkgsCross.avr pkgsCross.remarkable1
|
||||
pkgsCross.ben-nanonote pkgsCross.remarkable2
|
||||
pkgsCross.fuloongminipc pkgsCross.riscv32
|
||||
pkgsCross.ghcjs pkgsCross.riscv32-embedded
|
||||
pkgsCross.gnu32 pkgsCross.riscv64
|
||||
pkgsCross.gnu64 pkgsCross.riscv64-embedded
|
||||
pkgsCross.i686-embedded pkgsCross.scaleway-c1
|
||||
pkgsCross.iphone32 pkgsCross.sheevaplug
|
||||
pkgsCross.iphone32-simulator pkgsCross.vc4
|
||||
pkgsCross.iphone64 pkgsCross.wasi32
|
||||
pkgsCross.iphone64-simulator pkgsCross.x86_64-embedded
|
||||
pkgsCross.mingw32 pkgsCross.x86_64-netbsd
|
||||
pkgsCross.mingwW64 pkgsCross.x86_64-netbsd-llvm
|
||||
pkgsCross.mmix pkgsCross.x86_64-unknown-redox
|
||||
pkgsCross.msp430
|
||||
|
||||
|
||||
From the attribute name it can't always be immediately clear what is the platform.
|
||||
|
||||
It's possible to query the platform name using::
|
||||
|
||||
$ nix-instantiate '<nixpkgs>' -A pkgsCross.aarch64-darwin.hostPlatform.config --eval
|
||||
"aarch64-apple-darwin"
|
||||
|
||||
.. note:: In case the plaforms hasn't been defined yet, feel free to contribute one
|
||||
by `adding it upstream <https://github.com/NixOS/nixpkgs/blob/master/lib/systems/examples.nix>`_.
|
||||
|
||||
|
||||
Cross-compiling for the first time!
|
||||
-----------------------------------
|
||||
|
||||
To cross-compile a package like `hello <https://www.gnu.org/software/hello/>`_,
|
||||
pick the platform target name like ``aarch64-multiplatform`` in our case and run:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ nix-build '<nixpkgs>' -A pkgsCross.aarch64-multiplatform.hello
|
||||
...
|
||||
/nix/store/pzi2h0d60nb4ydcl3nn7cbxxdnibw3sy-hello-aarch64-unknown-linux-gnu-2.10
|
||||
|
||||
`Search for a package <https://search.nixos.org/packages>`_ attribute name to find the
|
||||
one that you're interested in to build.
|
||||
|
||||
|
||||
Real world cross-compiling of a Hello World example
|
||||
---------------------------------------------------
|
||||
|
||||
To show off the power of cross-compilation in Nix, let's build our own Hello World program
|
||||
by cross-compiling it as static executables to ``armv6l-unknown-linux-gnueabihf``
|
||||
and ``x86_64-w64-mingw32`` (Windows) platforms and run the resulting executable
|
||||
with `an emulator <https://en.wikipedia.org/wiki/Emulator>`_.
|
||||
|
||||
.. code:: nix
|
||||
|
||||
{ pkgs ? import <nixpkgs> {}
|
||||
}:
|
||||
|
||||
let
|
||||
# Create a C program that prints Hello World
|
||||
helloWorld = pkgs.writeText "hello.c" ''
|
||||
#include <stdio.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
printf ("Hello, world!\n");
|
||||
return 0;
|
||||
}
|
||||
'';
|
||||
|
||||
# A function that takes host platform packages
|
||||
crossCompileFor = hostPkgs:
|
||||
# Run a simple command with the compiler available
|
||||
hostPkgs.runCommandCC "hello-world-cross-test" {} ''
|
||||
# Wine requires home directory
|
||||
HOME=$PWD
|
||||
|
||||
# Compile our example using the compiler specific to our host platform
|
||||
$CC ${helloWorld} -o hello
|
||||
|
||||
# Run the compiled program using an emulator
|
||||
# Usually Qemu, but on windows it is Wine
|
||||
${hostPkgs.stdenv.hostPlatform.emulator hostPkgs.buildPackages} hello > $out
|
||||
|
||||
# print to stdout program stdout
|
||||
cat $out
|
||||
'';
|
||||
in {
|
||||
# Statically compile our example using the two platform hosts
|
||||
rpi = crossCompileFor pkgs.pkgsCross.raspberryPi;
|
||||
windows = crossCompileFor pkgs.pkgsCross.mingwW64;
|
||||
}
|
||||
|
||||
If we build this example and print both resulting derivations, we should see "Hello, world!" for each:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ cat $(nix-build cross-compile.nix)
|
||||
Hello, world!
|
||||
Hello, world!
|
||||
|
||||
|
||||
Developer environment with a cross-compiler
|
||||
-------------------------------------------
|
||||
|
||||
In :ref:`tutorial for declarative reproducible environments <declarative-reproducible-envs>`,
|
||||
we've looked at how Nix helps us provide tooling and system libraries for our project.
|
||||
|
||||
It's also possible to provide an environment with a compiler configured for cross-compilation!
|
||||
|
||||
Given we have a ``shell.nix``:
|
||||
|
||||
.. code:: nix
|
||||
|
||||
{ nixpkgs ? fetchTarball "https://github.com/NixOS/nixpkgs/archive/bba3474a5798b5a3a87e10102d1a55f19ec3fca5.tar.gz"
|
||||
, pkgs ? (import nixpkgs {}).pkgsCross.aarch64-multiplatform
|
||||
}:
|
||||
|
||||
# pkgs.callPackage is needed due to https://github.com/NixOS/nixpkgs/pull/126844
|
||||
pkgs.callPackage ({ mkShell, zlib, pkg-config }: mkShell {
|
||||
# these tools run on the build platform, but are configure to target the target platform
|
||||
nativeBuildInputs = [ pkg-config file ];
|
||||
# libraries needed for the target platform
|
||||
buildInputs = [ zlib ];
|
||||
}) {}
|
||||
|
||||
And ``hello.c``:
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
printf ("Hello, world!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
We can cross-compile it:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ nix-shell --run '$CC hello.c -o hello' cross-compile-shell.nix
|
||||
|
||||
And confirm it's aarch64:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ nix-shell --run 'file hello' cross-compile-shell.nix
|
||||
hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /nix/store/733hzlw1hixdm6dfdsb8dlwa2h8fl5qi-glibc-2.31-74-aarch64-unknown-linux-gnu/lib/ld-linux-aarch64.so.1, for GNU/Linux 2.6.32, with debug_info, not stripped
|
||||
|
||||
|
||||
Next steps
|
||||
----------
|
||||
|
||||
- `Official binary cache <https://cache.nixos.org>`_ doesn't come with binaries
|
||||
for packages that are cross-compiled, so it's important to set up
|
||||
:ref:`a binary cache and CI (GitHub Actions and Cachix) <github-actions>`.
|
||||
|
||||
- While many compilers in nixpkgs support cross-compilation,
|
||||
not all of them do.
|
||||
|
||||
On top of that, supporting cross-compilation is not trivial
|
||||
work and due to many possible combinations of what would
|
||||
need to be tested, packages some might not build.
|
||||
|
||||
`A detailed explanation how cross-compilation is implemented in Nix can help fixing those issues <https://nixos.org/manual/nixpkgs/stable/#chap-cross>`_.
|
||||
|
||||
- Nix community has a `dedicated Matrix room <https://matrix.to/#/#cross-compiling:nixos.org>`_
|
||||
for help around cross-compiling.
|
|
@ -14,4 +14,5 @@ Tutorials
|
|||
deploying-nixos-using-terraform.rst
|
||||
installing-nixos-on-a-raspberry-pi.rst
|
||||
integration-testing-using-virtual-machines.rst
|
||||
cross-compilation.rst
|
||||
contributing.rst
|
||||
|
|
Loading…
Reference in a new issue