nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 lib,
3 stdenv,
4 resholve,
5 binlore,
6 writeTextFile,
7}:
8
9rec {
10 /*
11 These functions break up the work of partially validating the
12 'solutions' attrset and massaging it into env/cli args.
13
14 Note: some of the left-most args do not *have* to be passed as
15 deep as they are, but I've done so to provide more error context
16 */
17
18 # for brevity / line length
19 spaces = l: builtins.concatStringsSep " " l;
20 colons = l: builtins.concatStringsSep ":" l;
21 semicolons = l: builtins.concatStringsSep ";" l;
22
23 # Throw a fit with dotted attr path context
24 nope = path: msg: throw "${builtins.concatStringsSep "." path}: ${msg}";
25
26 # Special-case directive value representations by type
27 phraseDirective =
28 solution: env: name: val:
29 if builtins.isInt val then
30 toString val
31 else if builtins.isString val then
32 name
33 else if true == val then
34 name
35 else if false == val then
36 "" # omit!
37 else if null == val then
38 "" # omit!
39 else if builtins.isList val then
40 "${name}:${semicolons (map lib.escapeShellArg val)}"
41 else
42 nope [
43 solution
44 env
45 name
46 ] "unexpected type: ${builtins.typeOf val}";
47
48 # Build fake/fix/keep directives from Nix types
49 phraseDirectives =
50 solution: env: val:
51 lib.mapAttrsToList (phraseDirective solution env) val;
52
53 # Custom ~search-path routine to handle relative path strings
54 relSafeBinPath =
55 input:
56 if lib.isDerivation input then
57 ((lib.getOutput "bin" input) + "/bin")
58 else if builtins.isString input then
59 input
60 else
61 throw "unexpected type for input: ${builtins.typeOf input}";
62
63 # Special-case value representation by type/name
64 phraseEnvVal =
65 solution: env: val:
66 if env == "inputs" then
67 (colons (map relSafeBinPath val))
68 else if builtins.isString val then
69 val
70 else if builtins.isList val then
71 spaces val
72 else if builtins.isAttrs val then
73 spaces (phraseDirectives solution env val)
74 else
75 nope [
76 solution
77 env
78 ] "unexpected type: ${builtins.typeOf val}";
79
80 # Shell-format each env value
81 shellEnv =
82 solution: env: value:
83 lib.escapeShellArg (phraseEnvVal solution env value);
84
85 # Build a single ENV=val pair
86 phraseEnv =
87 solution: env: value:
88 "RESHOLVE_${lib.toUpper env}=${shellEnv solution env value}";
89
90 /*
91 Discard attrs:
92 - claimed by phraseArgs
93 - only needed for binlore.collect
94 */
95 removeUnneededArgs =
96 value:
97 removeAttrs value [
98 "scripts"
99 "flags"
100 "unresholved"
101 ];
102
103 # Verify required arguments are present
104 validateSolution =
105 {
106 scripts,
107 inputs,
108 interpreter,
109 ...
110 }:
111 true;
112
113 # Pull out specific solution keys to build ENV=val pairs
114 phraseEnvs =
115 solution: value: spaces (lib.mapAttrsToList (phraseEnv solution) (removeUnneededArgs value));
116
117 # Pull out specific solution keys to build CLI argstring
118 phraseArgs =
119 {
120 flags ? [ ],
121 scripts,
122 ...
123 }:
124 spaces (flags ++ scripts);
125
126 phraseBinloreArgs =
127 value:
128 let
129 hasUnresholved = builtins.hasAttr "unresholved" value;
130 in
131 {
132 drvs = value.inputs ++ lib.optionals hasUnresholved [ value.unresholved ];
133 strip = if hasUnresholved then [ value.unresholved ] else [ ];
134 };
135
136 # Build a single resholve invocation
137 phraseInvocation =
138 solution: value:
139 if validateSolution value then
140 # we pass resholve a directory
141 "RESHOLVE_LORE=${binlore.collect (phraseBinloreArgs value)} ${phraseEnvs solution value} ${resholve}/bin/resholve --overwrite ${phraseArgs value}"
142 else
143 throw "invalid solution"; # shouldn't trigger for now
144
145 injectUnresholved =
146 solutions: unresholved:
147 (builtins.mapAttrs (name: value: value // { inherit unresholved; }) solutions);
148
149 # Build resholve invocation for each solution.
150 phraseCommands =
151 solutions: unresholved:
152 builtins.concatStringsSep "\n" (
153 lib.mapAttrsToList phraseInvocation (injectUnresholved solutions unresholved)
154 );
155
156 /*
157 subshell/PS4/set -x and : command to output resholve envs
158 and invocation. Extra context makes it clearer what the
159 Nix API is doing, makes nix-shell debugging easier, etc.
160 */
161 phraseContext =
162 {
163 invokable,
164 prep ? ''cd "$out"'',
165 }:
166 ''
167 (
168 ${prep}
169 PS4=$'\x1f'"\033[33m[resholve context]\033[0m "
170 set -x
171 : invoking resholve with PWD=$PWD
172 ${invokable}
173 )
174 '';
175 phraseContextForPWD =
176 invokable:
177 phraseContext {
178 inherit invokable;
179 prep = "";
180 };
181 phraseContextForOut = invokable: phraseContext { inherit invokable; };
182
183 phraseSolution = name: solution: (phraseContextForOut (phraseInvocation name solution));
184 phraseSolutions =
185 solutions: unresholved: phraseContextForOut (phraseCommands solutions unresholved);
186
187 writeScript =
188 name: partialSolution: text:
189 writeTextFile {
190 inherit name text;
191 executable = true;
192 checkPhase = ''
193 ${
194 (phraseContextForPWD (
195 phraseInvocation name (
196 partialSolution
197 // {
198 scripts = [ "${placeholder "out"}" ];
199 }
200 )
201 ))
202 }
203 '';
204 };
205 writeScriptBin =
206 name: partialSolution: text:
207 writeTextFile rec {
208 inherit name text;
209 executable = true;
210 destination = "/bin/${name}";
211 checkPhase = ''
212 ${phraseContextForOut (
213 phraseInvocation name (
214 partialSolution
215 // {
216 scripts = [ "bin/${name}" ];
217 }
218 )
219 )}
220 '';
221 };
222 mkDerivation =
223 {
224 pname,
225 src,
226 version,
227 passthru ? { },
228 solutions,
229 postResholve ? "",
230 ...
231 }@attrs:
232 let
233 inherit stdenv;
234
235 /*
236 Knock out our special solutions arg, but otherwise
237 just build what the caller is giving us. We'll
238 actually resholve it separately below (after we
239 generate binlore for it).
240 */
241 unresholved = (
242 stdenv.mkDerivation (
243 (removeAttrs attrs [ "solutions" ])
244 // {
245 inherit version src;
246 pname = "${pname}-unresholved";
247 }
248 )
249 );
250 in
251 /*
252 resholve in a separate derivation; some concerns:
253 - we aren't keeping many of the user's args, so they
254 can't readily set LOGLEVEL and such...
255 - not sure how this affects multiple outputs
256 */
257 lib.extendDerivation true passthru (
258 stdenv.mkDerivation {
259 src = unresholved;
260 inherit version pname;
261 buildInputs = [ resholve ];
262 disallowedReferences = [ resholve ];
263
264 # retain a reference to the base
265 passthru = unresholved.passthru // {
266 unresholved = unresholved;
267 # fallback attr for update bot to query our src
268 originalSrc = unresholved.src;
269 };
270
271 # do these imply that we should use NoCC or something?
272 dontConfigure = true;
273 dontBuild = true;
274
275 installPhase = ''
276 cp -R $src $out
277 '';
278
279 # enable below for verbose debug info if needed
280 # supports default python.logging levels
281 # LOGLEVEL="INFO";
282 preFixup = phraseSolutions solutions unresholved;
283
284 postFixup = postResholve;
285
286 # don't break the metadata...
287 meta = unresholved.meta;
288 }
289 );
290}