about summary refs log tree commit diff
path: root/pkgs/stdenv/booter.nix
blob: 1df05099fbf5edb4ebd65e6f2f1f6e50103c4deb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# This file defines a single function for booting a package set from a list of
# stages. The exact mechanics of that function are defined below; here I
# (@Ericson2314) wish to describe the purpose of the abstraction.
#
# The first goal is consistency across stdenvs. Regardless of what this function
# does, by making every stdenv use it for bootstrapping we ensure that they all
# work in a similar way. [Before this abstraction, each stdenv was its own
# special snowflake due to different authors writing in different times.]
#
# The second goal is consistency across each stdenv's stage functions. By
# writing each stage it terms of the previous stage, commonalities between them
# are more easily observable. [Before, there usually was a big attribute set
# with each stage, and stages would access the previous stage by name.]
#
# The third goal is composition. Because each stage is written in terms of the
# previous, the list can be reordered or, more practically, extended with new
# stages. The latter is used for cross compiling and custom
# stdenvs. Additionally, certain options should by default apply only to the
# last stage, whatever it may be. By delaying the creation of stage package sets
# until the final fold, we prevent these options from inhibiting composition.
#
# The fourth and final goal is debugging. Normal packages should only source
# their dependencies from the current stage. But for the sake of debugging, it
# is nice that all packages still remain accessible. We make sure previous
# stages are kept around with a `stdenv.__bootPackges` attribute referring the
# previous stage. It is idiomatic that attributes prefixed with `__` come with
# special restrictions and should not be used under normal circumstances.
{ lib, allPackages }:

# Type:
#   [ pkgset -> (args to stage/default.nix) or ({ __raw = true; } // pkgs) ]
#   -> pkgset
#
# In english: This takes a list of function from the previous stage pkgset and
# returns the final pkgset. Each of those functions returns, if `__raw` is
# undefined or false, args for this stage's pkgset (the most complex and
# important arg is the stdenv), or, if `__raw = true`, simply this stage's
# pkgset itself.
#
# The list takes stages in order, so the final stage is last in the list. In
# 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, `dfold op lnul rnul [x_0 x_1 x_2 ... x_n-1]` is the
     same as

       let
         f_-1  = lnul f_0;
         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 f_n;
       in
         f_0
  */
  dfold = op: lnul: rnul: list:
    let
      len = builtins.length list;
      go = pred: n:
        if n == len
        then rnul pred
        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;
      lapp = lnul cur;
      cur = go lapp 0;
    in cur;

  # 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.
  withAllowCustomOverrides = lib.lists.imap1
    (index: stageFun: prevStage:
      # So true by default for only the first element because one
      # 1-indexing. Since we reverse the list, this means this is true
      # for the final stage.
      { allowCustomOverrides = index == 1; }
      // (stageFun prevStage))
    (lib.lists.reverseList stageFuns);

  # Adds the stdenv to the arguments, and sticks in it the previous stage for
  # debugging purposes.
  folder = nextStage: stageFun: prevStage: let
    args = stageFun prevStage;
    args' = args // {
      stdenv = args.stdenv // {
        # For debugging
        __bootPackages = prevStage;
        __hatPackages = nextStage;
      };
    };
    thisStage =
      if args.__raw or false
      then args'
      else allPackages ((builtins.removeAttrs args' ["selfBuild"]) // {
        adjacentPackages = if args.selfBuild or true then null else rec {
          pkgsBuildBuild = prevStage.buildPackages;
          pkgsBuildHost = prevStage;
          pkgsBuildTarget =
            if args.stdenv.targetPlatform == args.stdenv.hostPlatform
            then pkgsBuildHost
            else assert args.stdenv.hostPlatform == args.stdenv.buildPlatform; thisStage;
          pkgsHostHost =
            if args.stdenv.hostPlatform == args.stdenv.targetPlatform
            then thisStage
            else assert args.stdenv.buildPlatform == args.stdenv.hostPlatform; pkgsBuildHost;
          pkgsTargetTarget = nextStage;
        };
      });
  in thisStage;

  # This is a hack for resolving cross-compiled compilers' run-time
  # deps. (That is, compilers that are themselves cross-compiled, as
  # opposed to used to cross-compile packages.)
  postStage = buildPackages: {
    __raw = true;
    stdenv.cc =
      if buildPackages.stdenv.cc.isClang or false
      then buildPackages.clang
      else buildPackages.gcc;
  };

in dfold folder postStage (_: {}) withAllowCustomOverrides