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 // { 101 # For debugging 102 __bootPackages = prevStage; 103 __hatPackages = nextStage; 104 }; 105 }; 106 thisStage = 107 if args.__raw or false then 108 args' 109 else 110 allPackages ( 111 (builtins.removeAttrs args' [ "selfBuild" ]) 112 // { 113 adjacentPackages = 114 if args.selfBuild or true then 115 null 116 else 117 rec { 118 pkgsBuildBuild = prevStage.buildPackages; 119 pkgsBuildHost = prevStage; 120 pkgsBuildTarget = 121 if args.stdenv.targetPlatform == args.stdenv.hostPlatform then 122 pkgsBuildHost 123 else 124 assert args.stdenv.hostPlatform == args.stdenv.buildPlatform; 125 thisStage; 126 pkgsHostHost = 127 if args.stdenv.hostPlatform == args.stdenv.targetPlatform then 128 thisStage 129 else 130 assert args.stdenv.buildPlatform == args.stdenv.hostPlatform; 131 pkgsBuildHost; 132 pkgsTargetTarget = nextStage; 133 }; 134 } 135 ); 136 in 137 thisStage; 138 139 # This is a hack for resolving cross-compiled compilers' run-time 140 # deps. (That is, compilers that are themselves cross-compiled, as 141 # opposed to used to cross-compile packages.) 142 postStage = buildPackages: { 143 __raw = true; 144 stdenv.cc = 145 if buildPackages.stdenv.hasCC then 146 if 147 buildPackages.stdenv.cc.isClang or false 148 # buildPackages.clang checks targetPackages.stdenv.cc (i. e. this 149 # attribute) to get a sense of the its set's default compiler and 150 # chooses between libc++ and libstdc++ based on that. If we hit this 151 # code here, we'll cause an infinite recursion. Since a set with 152 # clang as its default compiler always means libc++, we can infer this 153 # decision statically. 154 then 155 buildPackages.pkgsBuildTarget.llvmPackages.libcxxClang 156 else 157 buildPackages.gcc 158 else 159 # This will blow up if anything uses it, but that's OK. The `if 160 # buildPackages.stdenv.cc.isClang then ... else ...` would blow up 161 # everything, so we make sure to avoid that. 162 buildPackages.stdenv.cc; 163 }; 164 165in 166dfold folder postStage (_: { }) withAllowCustomOverrides