cripslock

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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.