1{ lib }:
2/**
3 Usage:
4
5 You define you custom builder script by adding all build steps to a list.
6 for example:
7 builder = writeScript "fsg-4.4-builder"
8 (textClosure [doUnpack addInputs preBuild doMake installPhase doForceShare]);
9
10 a step is defined by noDepEntry, fullDepEntry or packEntry.
11 To ensure that prerequisite are met those are added before the task itself by
12 textClosureDupList. Duplicated items are removed again.
13
14 See trace/nixpkgs/trunk/pkgs/top-level/builder-defs.nix for some predefined build steps
15
16 Attention:
17
18 let
19 pkgs = (import <nixpkgs>) {};
20 in let
21 inherit (pkgs.stringsWithDeps) fullDepEntry packEntry noDepEntry textClosureMap;
22 inherit (pkgs.lib) id;
23
24 nameA = noDepEntry "Text a";
25 nameB = fullDepEntry "Text b" ["nameA"];
26 nameC = fullDepEntry "Text c" ["nameA"];
27
28 stages = {
29 nameHeader = noDepEntry "#! /bin/sh \n";
30 inherit nameA nameB nameC;
31 };
32 in
33 textClosureMap id stages
34 [ "nameHeader" "nameA" "nameB" "nameC"
35 nameC # <- added twice. add a dep entry if you know that it will be added once only [1]
36 "nameB" # <- this will not be added again because the attr name (reference) is used
37 ]
38
39 # result: Str("#! /bin/sh \n\nText a\nText b\nText c\nText c",[])
40
41 [1] maybe this behaviour should be removed to keep things simple (?)
42*/
43
44let
45 inherit (lib)
46 concatStringsSep
47 head
48 isAttrs
49 listToAttrs
50 tail
51 ;
52in
53rec {
54
55 /**
56 Topologically sort a collection of dependent strings.
57 Only the values to keys listed in `arg` and their dependencies will be included in the result.
58
59 ::: {.note}
60 This function doesn't formally fulfill the definition of topological sorting, but it's good enough for our purposes in Nixpkgs.
61 :::
62
63 # Inputs
64
65 `predefined` (attribute set)
66
67 : strings with annotated dependencies (strings or attribute set)
68 A value can be a simple string if it has no dependencies.
69 Otherwise, is can be an attribute set with the following attributes:
70 - `deps` (list of strings)
71 - `text` (Any
72
73 `arg` (list of strings)
74
75 : Keys for which the values in the dependency closure will be included in the result
76
77 # Type
78
79 ```
80 textClosureList :: { ${phase} :: { deps :: [String]; text :: String; } | String; } -> [String] -> [String]
81 ```
82
83 # Examples
84 :::{.example}
85 ## `lib.stringsWithDeps.textClosureList` usage example
86
87 ```nix
88 textClosureList {
89 a = {
90 deps = [ "b" "c" "e" ];
91 text = "a: depends on b, c and e";
92 };
93 b = {
94 deps = [ ];
95 text = "b: no dependencies";
96 };
97 c = {
98 deps = [ "b" ];
99 text = "c: depends on b";
100 };
101 d = {
102 deps = [ "c" ];
103 text = "d: not being depended on by anything in `arg`";
104 };
105 e = {
106 deps = [ "c" ];
107 text = "e: depends on c, depended on by a, not in `arg`";
108 };
109 } [
110 "a"
111 "b"
112 "c"
113 ]
114 => [
115 "b: no dependencies"
116 "c: depends on b"
117 "e: depends on c, depended on by a, not in `arg`"
118 "a: depends on b, c and e"
119 ]
120 ```
121 :::
122
123 Common real world usages are:
124 - Ordering the dependent phases of `system.activationScripts`
125 - Ordering the dependent phases of `system.userActivationScripts`
126
127 For further examples see: [NixOS activation script](https://nixos.org/manual/nixos/stable/#sec-activation-script)
128 */
129 textClosureList =
130 predefined: arg:
131 let
132 f =
133 done: todo:
134 if todo == [ ] then
135 {
136 result = [ ];
137 inherit done;
138 }
139 else
140 let
141 entry = head todo;
142 in
143 if isAttrs entry then
144 let
145 x = f done entry.deps;
146 y = f x.done (tail todo);
147 in
148 {
149 result = x.result ++ [ entry.text ] ++ y.result;
150 done = y.done;
151 }
152 else if done ? ${entry} then
153 f done (tail todo)
154 else
155 f (
156 done
157 // listToAttrs [
158 {
159 name = entry;
160 value = 1;
161 }
162 ]
163 ) ([ predefined.${entry} ] ++ tail todo);
164 in
165 (f { } arg).result;
166
167 textClosureMap =
168 f: predefined: names:
169 concatStringsSep "\n" (map f (textClosureList predefined names));
170
171 noDepEntry = text: {
172 inherit text;
173 deps = [ ];
174 };
175 fullDepEntry = text: deps: { inherit text deps; };
176 packEntry = deps: {
177 inherit deps;
178 text = "";
179 };
180
181 stringAfter = deps: text: { inherit text deps; };
182
183}