[Nix-dev] Re: Cross compiling

Lluís Batlle viriketo at gmail.com
Mon Nov 16 18:37:40 CET 2009


Hello again,

let me say something about the proposal from Nicolas on adapting nixpkgs.
he proposes a redefinition of mkDerivation, like:
> stdenvCross = stdenv: stdenv // {
>  mkDerivation = {buildInputs, runInputs, ...}@args: let
>    hostInputs = map (drv: drv.host) buildInputs;
>    targetInputs = map (drv: drv.target) runInputs;
>    hostDrv = stdenv.mkDerivation (args // {..});
>    targetDrv = stdenv.mkDerivation (args // {..});
>  in hostDrv // {
>    inherit hostDrv tagertDrv;
>  };
>  host = system;
>  target = targetSystem;
> };

Here I see he maps 'host' to 'build inputs' and 'target' to
'runInputs'. I think this is not adequate.
Let me explain the usual approach from autotools. This does not
involve dependencies, but differentiates two important parameters of
the cross build:
--build "architecture" and --host "architecture".
*build* means the architecture where the software is built. And *host*
means the architecture where the software will be *run*. There is no
more difference than that. Notice how their term "host" refers to the
architecture running the product of the build, what we sometimes refer
as the "target architecture". Sometimes I've seen the term "host" used
as opposed to "target". I just wanted to clarify this point on
terminology, and I propose to use that of the autotools: host and
build, and forget 'target'.

Regarding the dichotomy "build inputs" and "runtime inputs", I don't
think it maps directly to "all build inputs are compiled for the
*build* architecture", and "all runtime inputs are compiled for the
*host* architecture".
Nix does quite a good job determining the runtime and the buildtime
dependencies, and I don't think we should clarify anything about that
in the nix expressions. Otherwise, I propose having the difference in
stdenv for "buildInputs" and "hostInputs". As these would be specified
by the same attribute name (for example:
readline = ... stdenv.mkDerivation {
   hostInputs = [ ncurses ];
   buildInputs = [ perl ];  # readline does not need perl, but let's
suppose it for the example
}

I think this implies that "ncurses" and "perl" are not simple
derivations, because they will be asked to be a derivation built for
one architecture or native. They should be overridable or a function.
I don't think the 'function' is anything viable, but having all
results of mkDerivation 'overridable' for a possible cross
compilation, could be interesting.

I'm not as good at nix expressions as some of the people in this list,
so I ask if do you think this is feasible.

In order to keep the all current nixpkgs expressions working, and
having the cross compilation functionality only in those properly
defining "hostInputs", we could assume that all buildInputs (current
usage) map to the here proposed buildInputs (in contrast to
hostInputs).


Another topic. How to define the *host* architecture? Since now I used
the string typical of config.guess suggested by ludo, but I've
encountered that sometimes I need the map from *config-guess-string*
to *architecture* (this is, armv5tel-unknown-linux-gnueabi to arm).
That is important for the kernel headers (needed for the most basic
linux cross-or-not-cross toolchain), for building the kernel, and for
uboot, for example. Additionally, I need to recognize if the target OS
is Linux. The glibc must be used with the proper kernel headers for
the OS the toolchain is built for. I would be pleased to add "mingw32"
as the cross-building possibilities: I only need the mingw and
win32api headers to get it working, I think. I haven't found a
pkgs/lib/strings.nix function to match a substring "-linux-", for
example, but I guess people here can write easily, and even knowing
the complexity increase implications of its evaluation.

There are also additional parameters that may help defining a cross
toolchain, like building with fpu support or not (libm and gcc need
with this information). Maybe we could use an 'architecture' struct
like:
{
   configGuessString = "armv5tel-unknown-linux-gnueabi";
   armEABI = true; # There are two popular ABIs in arm, not only one.
   float = "soft";  # there are different hw FPUs available, not only
one, so no boolean.
   architecture = "arm";
   os = "linux";
}
This would provide all information needed to the components involved
in the build of a cross-toolchain, as far as I know by now. Maybe
others of relevance may come. The configGuessString has most of the
information in the string already, and it is passed to any builder
through mkDerivation as an environment variable already, and maybe we
can write some code that maps from 'configGuessString' to
'architecture', but I'd better leave the user specify that map
manually.

Let me add that I did all the changes with the current gcc 4.3.4 and
glibc 2.9 in nixpkgs, but it should not be difficult to port that to
any future compile/libc pair we decide to use. I tried to keep the
expressions for the cross compiler and the host libc the same as for
the native, to ease future updates, not having to maintain different
expressions for the cross and not cross compiler/libc, because most of
the expressions is common, and having all integrated may consolidate a
better understanding of the hold stdenv building process.

What do you think? We could have a debate on irc if you want. I think
some people from Delft has been involved in adding cross compilation
to nixpkgs before my attempt, and I would also like to know their
opinion on all this changes.

Regards,
Lluís.

2009/11/15 Nicolas Pierron <nicolas.b.pierron at gmail.com>:
> Hi list,
>
> On Sun, Nov 15, 2009 at 06:28, Llus Batlle <viriketo at gmail.com> wrote:
>> @@ -2701,6 +2730,11 @@
>>
>>   bison = bison23;
>>
>> +  bisonArm = import ../development/tools/parsing/bison/bison-2.3.nix {
>> +    inherit fetchurl m4;
>> +    stdenv = stdenvCross "armv5tel-unknown-linux-gnueabi";
>> +  };
>> +
>>   bison1875 = import ../development/tools/parsing/bison/bison-1.875.nix {
>>     inherit fetchurl stdenv m4;
>>   };
>
> As I've reported on IRC, I don't think having multiple stdenv is a
> good choice.  This small parts of all-packages highlight the problem.
> You don't want to duplicate each package for each target.
>
> On IRC, I've suggested to consider the usual compilation as a special
> case of the cross compilation and not considering the
> cross-compilation as a special case of the usual compilation.  This
> suggestion implies that stdenv have to be modified to handle the host
> and the target of the compilation.  Such information will be provided
> as argument of all-packages.nix and will be used to update stdenv.
> Cross-compiled programs need to have 2 sets of inputs, to express the
> host inputs (build time dependencies) and the target inputs (run time
> dependencies).  Such distinctions could help us to get information to
> package developers by reporting any build time dependency leaking in
> the run time.  Thus a derivation will look like:
>
> {stdenv, fetchurl, foo, bar, baz}:
>
> stdenv.mkDerivation {
>  name = "..";
>  src = fetchurl { .. };
>  buildInputs = [ foo bar ];
>  runInputs = [ bar baz ];
>
>  ... /* hidden complexity */ ...
> }
>
> In order to make this derivation working for most packages, we should
> change stdenv like that:
>
> stdenvCross = stdenv: stdenv // {
>  mkDerivation = {buildInputs, runInputs, ...}@args: let
>    hostInputs = map (drv: drv.host) buildInputs;
>    targetInputs = map (drv: drv.target) runInputs;
>    hostDrv = stdenv.mkDerivation (args // {..});
>    targetDrv = stdenv.mkDerivation (args // {..});
>  in hostDrv // {
>    inherit hostDrv tagertDrv;
>  };
>  host = system;
>  target = targetSystem;
> };
>
> Such stdenv modifier will add two attributes to the resulting
> derivation which define the host-derivation and the target-derivation.
>  We need the host-derivation if some programs are needed to generate
> code for the target (make, bison, gcc, bash, ..) and we need the
> target derivation to make the program working on the target (glibc,
> python, ...).  When this stdenv is not used for cross-compiling, the
> host-derivation and the target-derivation would be identical which
> should avoid extra duplication.
>
> Package arguments don't need to be duplicated because each derivation
> contains a hostDrv & targetDrv attributes for each need.  So we will
> not end-up with
>
>  foo = import ../path/to/foo {
>    inherit stdenv fetchurl bison;
>  };
>
>  fooArm = import ../path/to/foo {
>    inherit fetchurl;
>    stdenv = stdenvCross "armv5tel-unknown-linux-gnueabi";
>    bison = bisonArm; # because bison is a run time dependency of foo.
>  };
>
>  foox86_64 = import ../path/to/foo {
>    inherit fetchurl;
>    stdenv = stdenvCross "x86_64-pc-linux";
>    bison = bisonx86_64; # because bison is a run time dependency of foo.
>  };
>
>  fooi686 = import ../path/to/foo {
>    inherit fetchurl;
>    stdenv = stdenvCross "i686-pc-linux";
>    bison = bisoni686; # because bison is a run time dependency of foo.
>  };
>
> which leak package information into all-packages.nix and cause a lot
> of duplications to handle a lot of cases which are similar. (let's
> factor that)
>
> Of course many programs may not handle cross-compilation, and this can
> be handle easily inside each derivation by failing before even
> starting the compilation:
>
> {stdenv, ...}:
>
> assert stdenv.host == stdenv.target;
>
> stdenv.mkDerivation {
>  ...
> }
>
> Thus, with such solution we avoid the duplication of all-packages.nix,
> and we add support for cross compilation in each package without
> changing packages arguments.
>
> --
> Nicolas Pierron
> http://www.linkedin.com/in/nicolasbpierron - http://nbp.name/
> If you are doing something twice then you should try to do it once.
>



More information about the nix-dev mailing list