1# This file defines a single function for booting a package set from a list of
2# stages. The exact mechanics of that function are defined below; here I
3# (@Ericson2314) wish to describe the purpose of the abstraction.
4#
5# The first goal is consistency across stdenvs. Regardless of what this function
6# does, by making every stdenv use it for bootstrapping we ensure that they all
7# work in a similar way. [Before this abstraction, each stdenv was its own
8# special snowflake due to different authors writing in different times.]
9#
10# The second goal is consistency across each stdenv's stage functions. By
11# writing each stage in terms of the previous stage, commonalities between them
12# are more easily observable. [Before, there usually was a big attribute set
13# with each stage, and stages would access the previous stage by name.]
14#
15# The third goal is composition. Because each stage is written in terms of the
16# previous, the list can be reordered or, more practically, extended with new
17# stages. The latter is used for cross compiling and custom
18# stdenvs. Additionally, certain options should by default apply only to the
19# last stage, whatever it may be. By delaying the creation of stage package sets
20# until the final fold, we prevent these options from inhibiting composition.
21#
22# The fourth and final goal is debugging. Normal packages should only source
23# their dependencies from the current stage. But for the sake of debugging, it
24# is nice that all packages still remain accessible. We make sure previous
25# stages are kept around with a `stdenv.__bootPackages` attribute referring the
26# previous stage. It is idiomatic that attributes prefixed with `__` come with
27# special restrictions and should not be used under normal circumstances.
28{ lib, allPackages }:
29
30# Type:
31# [ pkgset -> (args to stage/default.nix) or ({ __raw = true; } // pkgs) ]
32# -> pkgset
33#
34# In english: This takes a list of function from the previous stage pkgset and
35# returns the final pkgset. Each of those functions returns, if `__raw` is
36# undefined or false, args for this stage's pkgset (the most complex and
37# important arg is the stdenv), or, if `__raw = true`, simply this stage's
38# pkgset itself.
39#
40# The list takes stages in order, so the final stage is last in the list. In
41# other words, this does a foldr not foldl.
42stageFuns:
43let
44
45 /*
46 "dfold" a ternary function `op' between successive elements of `list' as if
47 it was a doubly-linked list with `lnul' and `rnul` base cases at either
48 end. In precise terms, `dfold op lnul rnul [x_0 x_1 x_2 ... x_n-1]` is the
49 same as
50
51 let
52 f_-1 = lnul f_0;
53 f_0 = op f_-1 x_0 f_1;
54 f_1 = op f_0 x_1 f_2;
55 f_2 = op f_1 x_2 f_3;
56 ...
57 f_n = op f_n-1 x_n f_n+1;
58 f_n+1 = rnul f_n;
59 in
60 f_0
61 */
62 dfold =
63 op: lnul: rnul: list:
64 let
65 len = builtins.length list;
66 go =
67 pred: n:
68 if n == len then
69 rnul pred
70 else
71 let
72 # Note the cycle -- call-by-need ensures finite fold.
73 cur = op pred (builtins.elemAt list n) succ;
74 succ = go cur (n + 1);
75 in
76 cur;
77 lapp = lnul cur;
78 cur = go lapp 0;
79 in
80 cur;
81
82 # Take the list and disallow custom overrides in all but the final stage,
83 # and allow it in the final flag. Only defaults this boolean field if it
84 # isn't already set.
85 withAllowCustomOverrides = lib.lists.imap1 (
86 index: stageFun: prevStage:
87 # So true by default for only the first element because one
88 # 1-indexing. Since we reverse the list, this means this is true
89 # for the final stage.
90 { allowCustomOverrides = index == 1; } // (stageFun prevStage)
91 ) (lib.lists.reverseList stageFuns);
92
93 # Adds the stdenv to the arguments, and sticks in it the previous stage for
94 # debugging purposes.
95 folder =
96 nextStage: stageFun: prevStage:
97 let
98 args = stageFun prevStage;
99 args' = args // {
100 stdenv = args.stdenv.override (prevArgs: {
101 # Extra package attributes for debugging
102 extraAttrs = prevArgs.extraAttrs or { } // {
103 __bootPackages = prevStage;
104 __hatPackages = nextStage;
105 };
106 });
107 };
108 thisStage =
109 if args.__raw or false then
110 args'
111 else
112 allPackages (
113 (builtins.removeAttrs args' [ "selfBuild" ])
114 // {
115 adjacentPackages =
116 if args.selfBuild or true then
117 null
118 else
119 rec {
120 pkgsBuildBuild = prevStage.buildPackages;
121 pkgsBuildHost = prevStage;
122 pkgsBuildTarget =
123 if args.stdenv.targetPlatform == args.stdenv.hostPlatform then
124 pkgsBuildHost
125 else
126 assert args.stdenv.hostPlatform == args.stdenv.buildPlatform;
127 thisStage;
128 pkgsHostHost =
129 if args.stdenv.hostPlatform == args.stdenv.targetPlatform then
130 thisStage
131 else
132 assert args.stdenv.buildPlatform == args.stdenv.hostPlatform;
133 pkgsBuildHost;
134 pkgsTargetTarget = nextStage;
135 };
136 }
137 );
138 in
139 thisStage;
140
141 # This is a hack for resolving cross-compiled compilers' run-time
142 # deps. (That is, compilers that are themselves cross-compiled, as
143 # opposed to used to cross-compile packages.)
144 postStage = buildPackages: {
145 __raw = true;
146 stdenv.cc =
147 if buildPackages.stdenv.hasCC then
148 if
149 buildPackages.stdenv.cc.isClang or false
150 # buildPackages.clang checks targetPackages.stdenv.cc (i. e. this
151 # attribute) to get a sense of the its set's default compiler and
152 # chooses between libc++ and libstdc++ based on that. If we hit this
153 # code here, we'll cause an infinite recursion. Since a set with
154 # clang as its default compiler always means libc++, we can infer this
155 # decision statically.
156 then
157 buildPackages.pkgsBuildTarget.llvmPackages.libcxxClang
158 else
159 buildPackages.gcc
160 else
161 # This will blow up if anything uses it, but that's OK. The `if
162 # buildPackages.stdenv.cc.isClang then ... else ...` would blow up
163 # everything, so we make sure to avoid that.
164 buildPackages.stdenv.cc;
165 };
166
167in
168dfold folder postStage (_: { }) withAllowCustomOverrides