summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/cross-compilation.xml5
-rw-r--r--pkgs/stdenv/booter.nix41
-rw-r--r--pkgs/top-level/splice.nix2
-rw-r--r--pkgs/top-level/stage.nix10
4 files changed, 52 insertions, 6 deletions
diff --git a/doc/cross-compilation.xml b/doc/cross-compilation.xml
index 06a8919c2a1..728616a9f26 100644
--- a/doc/cross-compilation.xml
+++ b/doc/cross-compilation.xml
@@ -167,6 +167,11 @@
       Because of this, a best-of-both-worlds solution is in the works with no splicing or explicit access of <varname>buildPackages</varname> needed.
       For now, feel free to use either method.
     </para>
+    <note><para>
+      There is also a "backlink" <varname>__targetPackages</varname>, yielding a package set whose <varname>buildPackages</varname> is the current package set.
+      This is a hack, though, to accommodate compilers with lousy build systems.
+      Please do not use this unless you are absolutely sure you are packaging such a compiler and there is no other way.
+    </para></note>
   </section>
 
 </section>
diff --git a/pkgs/stdenv/booter.nix b/pkgs/stdenv/booter.nix
index 2c82d12da95..d459deb6ab5 100644
--- a/pkgs/stdenv/booter.nix
+++ b/pkgs/stdenv/booter.nix
@@ -41,6 +41,35 @@
 # other words, this does a foldr not foldl.
 stageFuns: let
 
+  /* "dfold" a ternary function `op' between successive elements of `list' as if
+     it was a doubly-linked list with `lnul' and `rnul` base cases at either
+     end. In precise terms, `fold op lnul rnul [x_0 x_1 x_2 ... x_n-1]` is the
+     same as
+
+       let
+         f_-1  = lnul;
+         f_0   = op f_-1   x_0  f_1;
+         f_1   = op f_0    x_1  f_2;
+         f_2   = op f_1    x_2  f_3;
+         ...
+         f_n   = op f_n-1  x_n  f_n+1;
+         f_n+1 = rnul;
+       in
+         f_0
+  */
+  dfold = op: lnul: rnul: list:
+    let
+      len = builtins.length list;
+      go = pred: n:
+        if n == len
+        then rnul
+        else let
+          # Note the cycle -- call-by-need ensures finite fold.
+          cur  = op pred (builtins.elemAt list n) succ;
+          succ = go cur (n + 1);
+        in cur;
+    in go lnul 0;
+
   # Take the list and disallow custom overrides in all but the final stage,
   # and allow it in the final flag. Only defaults this boolean field if it
   # isn't already set.
@@ -55,19 +84,21 @@ stageFuns: let
 
   # Adds the stdenv to the arguments, and sticks in it the previous stage for
   # debugging purposes.
-  folder = stageFun: finalSoFar: let
-    args = stageFun finalSoFar;
+  folder = nextStage: stageFun: prevStage: let
+    args = stageFun prevStage;
     args' = args // {
       stdenv = args.stdenv // {
         # For debugging
-        __bootPackages = finalSoFar;
+        __bootPackages = prevStage;
+        __hatPackages = nextStage;
       };
     };
   in
     if args.__raw or false
     then args'
     else allPackages ((builtins.removeAttrs args' ["selfBuild"]) // {
-      buildPackages = if args.selfBuild or true then null else finalSoFar;
+      buildPackages = if args.selfBuild or true then null else prevStage;
+      __targetPackages = if args.selfBuild or true then null else nextStage;
     });
 
-in lib.lists.fold folder {} withAllowCustomOverrides
+in dfold folder {} {} withAllowCustomOverrides
diff --git a/pkgs/top-level/splice.nix b/pkgs/top-level/splice.nix
index bb6fc47b1d2..43951100de3 100644
--- a/pkgs/top-level/splice.nix
+++ b/pkgs/top-level/splice.nix
@@ -66,7 +66,7 @@ let
     if actuallySplice
     then splicer defaultBuildScope defaultRunScope // {
       # These should never be spliced under any circumstances
-      inherit (pkgs) pkgs buildPackages
+      inherit (pkgs) pkgs buildPackages __targetPackages
         buildPlatform targetPlatform hostPlatform;
     }
     else pkgs // pkgs.xorg;
diff --git a/pkgs/top-level/stage.nix b/pkgs/top-level/stage.nix
index ead6ec18fb3..d8e190cfd4b 100644
--- a/pkgs/top-level/stage.nix
+++ b/pkgs/top-level/stage.nix
@@ -50,6 +50,14 @@
   # us to avoid expensive splicing.
   buildPackages
 
+, # The package set used in the next stage. If null, `__targetPackages` will be
+  # defined internally as the final produced package set itself, just like with
+  # `buildPackages` and for the same reasons.
+  #
+  # THIS IS A HACK for compilers that don't think critically about cross-
+  # compilation. Please do *not* use unless you really know what you are doing.
+  __targetPackages
+
 , # The standard environment to use for building packages.
   stdenv
 
@@ -88,6 +96,8 @@ let
   stdenvBootstappingAndPlatforms = self: super: {
     buildPackages = (if buildPackages == null then self else buildPackages)
       // { recurseForDerivations = false; };
+    __targetPackages = (if __targetPackages == null then self else __targetPackages)
+      // { recurseForDerivations = false; };
     inherit stdenv
       buildPlatform hostPlatform targetPlatform;
   };