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 buildPackages needed.
For now, feel free to use either method.
+
+ There is also a "backlink" __targetPackages, yielding a package set whose buildPackages 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.
+
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 a22587d5b57..43951100de3 100644
--- a/pkgs/top-level/splice.nix
+++ b/pkgs/top-level/splice.nix
@@ -64,7 +64,11 @@ let
splicedPackages =
if actuallySplice
- then splicer defaultBuildScope defaultRunScope
+ then splicer defaultBuildScope defaultRunScope // {
+ # These should never be spliced under any circumstances
+ inherit (pkgs) pkgs buildPackages __targetPackages
+ buildPlatform targetPlatform hostPlatform;
+ }
else pkgs // pkgs.xorg;
in
diff --git a/pkgs/top-level/stage.nix b/pkgs/top-level/stage.nix
index 6febedb79f3..d8e190cfd4b 100644
--- a/pkgs/top-level/stage.nix
+++ b/pkgs/top-level/stage.nix
@@ -46,9 +46,18 @@
##
, # The package set used at build-time. If null, `buildPackages` will
- # be defined internally as the produced package set as itself.
+ # be defined internally as the final produced package set itself. This allows
+ # 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
@@ -87,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;
};