20211031_Cross_product.md (6049B)
1 Topics: nix 2 Title: Cross product 3 Date: Sun Oct 31 15:47:10 2021 4 5 I had cause this afternoon to remember the 6 [Monad Tutorial Fallacy](https://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/), 7 which has been summarised as saying that when you finally understand 8 them, you lose the ability to explain it to others. 9 10 I hypothesise that the same is probably true of cross-compilation in 11 the Nix package system, and therefore I present these notes not as a 12 superior alternative to any of the existing documentation, but because 13 I wrote them down to aid my understanding and now need to put them 14 somewhere I can refer back to them. 15 16 So. Let's suppose we're building NixWRT. Most likely we're building on 17 an x86-64 system, to produce an image which will run on a MIPS 18 device. In the event that there are any programs in that image which 19 generate code (which is unlikely as we're not shipping compilers), we 20 want them also to generate MIPS code. Thus, in the standard 21 terminology we have: 22 23 * build: x86-64 24 * host: MIPS 25 * target: MIPS 26 27 (This naming convention comes from Autoconf, and so we are stuck with 28 it. To make it make sense, consider the built product rather than the 29 build process: we are describing a thing that was built on x86-64, is 30 hosted on MIPS, and would - if it emitted any code - emit code that 31 runs on MIPS) 32 33 However, not all of the software we create (or depend on) will be 34 needed on the MIPS system - some of it (e.g. buid tools, compilers and 35 other kinds of translators) will have to run on x86-64. So how do we 36 keep track of it all? 37 38 Let's look at some examples: 39 40 * Package A contains source code which is translated into some other 41 form using programs provided by package B (e.g. B provides an SVG to PNG 42 convertor). The programs in B must run on the build machine: 43 thus, the `host` for B is the `build` for A. Provided that B is not 44 generating executable code - i.e. we don't have to worry about the 45 target - then we represent this by including B in the 46 `nativeBuildInputs` attrribute of A's derivation. 47 48 * Package A contains source code which is compiled using programs 49 provided by package B for execution on the build system. For 50 example, B is a C compiler which we are using to build `nconf`, 51 which we will then run to create the `.config` file that the linux 52 kernel build process uses. In this case the program in B must run 53 on the build system of A and also must _target_ the build system of 54 A. We represent this by putting B in the `depsBuildBuild` attribute 55 of A's derivation. 56 57 * if we have a package A which depends at runtime on another package B 58 (e.g. the build for A creates a shell script, one of whose commands 59 is provided by B) both those derivations have the same `host`. In 60 this case B doesn't care about the target, so we add it to the 61 `buildInputs` for A (if it does care, that's more complicated). As 62 the developers of A we must ensure that programs in B are reachable 63 from A, either by embeddding the full pathname of B into the script 64 or using a wrapper that sets $PATH. 65 66 * if A required at run-time some source code contained in B (e.g. A is 67 a script for some interpreter, and B is a source-distributed 68 library, for it) then B has no `host` to speak of. If there is any 69 native code component in that library, though, it must be code that 70 runs on the same system as A's host - so `buildInputs` again. See 71 [abcde](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/audio/abcde/default.nix) 72 for an example. Note also the `wrapProgram` call which sets 73 `PERL5LIB` to ensure that the code in A can find the code in B at 74 runtime. 75 76 * if A depends when it is *built* on source code contained in B 77 (suppose: the build invokes a Ruby script, and B is a gem required 78 by that script) then B must be runnable on the build system of A. 79 Host(B) = Build(A) implies `nativeBuildInputs` unless there is some 80 target shenanigans. Consulting the 81 [manual](https://nixos.org/manual/nixpkgs/unstable/#setup-hook-perl) 82 it seems that for some interpreters there is support for adding 83 the files in B to interpreter's search path while A is built. 84 85 * if A is a program that runs on the host, and is linked to binary 86 static libraries provided by B, the `host` for B must be the same as 87 for A, so my reading is that this is `buildInputs`. Note that A must 88 be able to find B at compile time, which is handled by the 89 [CC Wrapper](https://nixos.org/manual/nixpkgs/unstable/#cc-wrapper) 90 adding appropriate flags. 91 92 * if A is a program that runs on the host, and depends on binary 93 _shared_ libraries provided by B, the `host` for B must be the same 94 as for A so this is similar to the previous case. The absolute 95 pathname of the shared library provided by B will be embedded into 96 the binary of A. 97 98 Why am I caring about this right now? I rearranged bits of NixWRT and 99 updated it to a recent Nixpkgs revision, causing OCaml to stop 100 building for [reasons I didn't fully understand](https://github.com/NixOS/nixpkgs/issues/143883) 101 102 So, here is what I think is happening: 103 104 - the kernel is being built on x86-64 (build) for execution on MIPS 105 (host). It doesn't generate code when it's run (er, as far I know - 106 at any rate it can't be configured to generate code for some other 107 system than the one its running on) so I don't care about target. 108 109 - to create the kernel source tree we run Coccinelle on x86-64 - so 110 Coccinelle's `host` is the kernel's `build`. Coccinelle produces C 111 source files, so again I don't care about `target`. This means we 112 should use `nativeBuildInputs` to declare it as a dependency. 113 114 - Coccinelle is written in OCaml, which is a compiler and generates 115 code for some target system according to its build options. This 116 means that OCamls `host` _and_ `target` are both coccinelle's `build` 117 system, so we use `depsBuildBuild` to declare it as dependency. 118 119 120 121 Clear? If this doesn't help, I invite you to consider the possibility 122 that cross-compilation is like a burrito.