1{ lib }:
2
3rec {
4
5
6 /* `overrideDerivation drv f` takes a derivation (i.e., the result
7 of a call to the builtin function `derivation`) and returns a new
8 derivation in which the attributes of the original are overridden
9 according to the function `f`. The function `f` is called with
10 the original derivation attributes.
11
12 `overrideDerivation` allows certain "ad-hoc" customisation
13 scenarios (e.g. in ~/.config/nixpkgs/config.nix). For instance,
14 if you want to "patch" the derivation returned by a package
15 function in Nixpkgs to build another version than what the
16 function itself provides, you can do something like this:
17
18 mySed = overrideDerivation pkgs.gnused (oldAttrs: {
19 name = "sed-4.2.2-pre";
20 src = fetchurl {
21 url = ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2;
22 sha256 = "11nq06d131y4wmf3drm0yk502d2xc6n5qy82cg88rb9nqd2lj41k";
23 };
24 patches = [];
25 });
26
27 For another application, see build-support/vm, where this
28 function is used to build arbitrary derivations inside a QEMU
29 virtual machine.
30
31 Note that in order to preserve evaluation errors, the new derivation's
32 outPath depends on the old one's, which means that this function cannot
33 be used in circular situations when the old derivation also depends on the
34 new one.
35
36 You should in general prefer `drv.overrideAttrs` over this function;
37 see the nixpkgs manual for more information on overriding.
38 */
39 overrideDerivation = drv: f:
40 let
41 newDrv = derivation (drv.drvAttrs // (f drv));
42 in lib.flip (extendDerivation (builtins.seq drv.drvPath true)) newDrv (
43 { meta = drv.meta or {};
44 passthru = if drv ? passthru then drv.passthru else {};
45 }
46 //
47 (drv.passthru or {})
48 //
49 # TODO(@Artturin): remove before release 23.05 and only have __spliced.
50 (lib.optionalAttrs (drv ? crossDrv && drv ? nativeDrv) {
51 crossDrv = overrideDerivation drv.crossDrv f;
52 nativeDrv = overrideDerivation drv.nativeDrv f;
53 })
54 //
55 lib.optionalAttrs (drv ? __spliced) {
56 __spliced = {} // (lib.mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced);
57 });
58
59
60 /* `makeOverridable` takes a function from attribute set to attribute set and
61 injects `override` attribute which can be used to override arguments of
62 the function.
63
64 nix-repl> x = {a, b}: { result = a + b; }
65
66 nix-repl> y = lib.makeOverridable x { a = 1; b = 2; }
67
68 nix-repl> y
69 { override = «lambda»; overrideDerivation = «lambda»; result = 3; }
70
71 nix-repl> y.override { a = 10; }
72 { override = «lambda»; overrideDerivation = «lambda»; result = 12; }
73
74 Please refer to "Nixpkgs Contributors Guide" section
75 "<pkg>.overrideDerivation" to learn about `overrideDerivation` and caveats
76 related to its use.
77 */
78 makeOverridable = f: origArgs:
79 let
80 result = f origArgs;
81
82 # Creates a functor with the same arguments as f
83 copyArgs = g: lib.setFunctionArgs g (lib.functionArgs f);
84 # Changes the original arguments with (potentially a function that returns) a set of new attributes
85 overrideWith = newArgs: origArgs // (if lib.isFunction newArgs then newArgs origArgs else newArgs);
86
87 # Re-call the function but with different arguments
88 overrideArgs = copyArgs (newArgs: makeOverridable f (overrideWith newArgs));
89 # Change the result of the function call by applying g to it
90 overrideResult = g: makeOverridable (copyArgs (args: g (f args))) origArgs;
91 in
92 if builtins.isAttrs result then
93 result // {
94 override = overrideArgs;
95 overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
96 ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
97 overrideResult (x: x.overrideAttrs fdrv);
98 }
99 else if lib.isFunction result then
100 # Transform the result into a functor while propagating its arguments
101 lib.setFunctionArgs result (lib.functionArgs result) // {
102 override = overrideArgs;
103 }
104 else result;
105
106
107 /* Call the package function in the file `fn` with the required
108 arguments automatically. The function is called with the
109 arguments `args`, but any missing arguments are obtained from
110 `autoArgs`. This function is intended to be partially
111 parameterised, e.g.,
112
113 callPackage = callPackageWith pkgs;
114 pkgs = {
115 libfoo = callPackage ./foo.nix { };
116 libbar = callPackage ./bar.nix { };
117 };
118
119 If the `libbar` function expects an argument named `libfoo`, it is
120 automatically passed as an argument. Overrides or missing
121 arguments can be supplied in `args`, e.g.
122
123 libbar = callPackage ./bar.nix {
124 libfoo = null;
125 enableX11 = true;
126 };
127 */
128 callPackageWith = autoArgs: fn: args:
129 let
130 f = if lib.isFunction fn then fn else import fn;
131 fargs = lib.functionArgs f;
132
133 # All arguments that will be passed to the function
134 # This includes automatic ones and ones passed explicitly
135 allArgs = builtins.intersectAttrs fargs autoArgs // args;
136
137 # A list of argument names that the function requires, but
138 # wouldn't be passed to it
139 missingArgs = lib.attrNames
140 # Filter out arguments that have a default value
141 (lib.filterAttrs (name: value: ! value)
142 # Filter out arguments that would be passed
143 (removeAttrs fargs (lib.attrNames allArgs)));
144
145 # Get a list of suggested argument names for a given missing one
146 getSuggestions = arg: lib.pipe (autoArgs // args) [
147 lib.attrNames
148 # Only use ones that are at most 2 edits away. While mork would work,
149 # levenshteinAtMost is only fast for 2 or less.
150 (lib.filter (lib.strings.levenshteinAtMost 2 arg))
151 # Put strings with shorter distance first
152 (lib.sort (x: y: lib.strings.levenshtein x arg < lib.strings.levenshtein y arg))
153 # Only take the first couple results
154 (lib.take 3)
155 # Quote all entries
156 (map (x: "\"" + x + "\""))
157 ];
158
159 prettySuggestions = suggestions:
160 if suggestions == [] then ""
161 else if lib.length suggestions == 1 then ", did you mean ${lib.elemAt suggestions 0}?"
162 else ", did you mean ${lib.concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?";
163
164 errorForArg = arg:
165 let
166 loc = builtins.unsafeGetAttrPos arg fargs;
167 # loc' can be removed once lib/minver.nix is >2.3.4, since that includes
168 # https://github.com/NixOS/nix/pull/3468 which makes loc be non-null
169 loc' = if loc != null then loc.file + ":" + toString loc.line
170 else if ! lib.isFunction fn then
171 toString fn + lib.optionalString (lib.sources.pathIsDirectory fn) "/default.nix"
172 else "<unknown location>";
173 in "Function called without required argument \"${arg}\" at "
174 + "${loc'}${prettySuggestions (getSuggestions arg)}";
175
176 # Only show the error for the first missing argument
177 error = errorForArg (lib.head missingArgs);
178
179 in if missingArgs == [] then makeOverridable f allArgs else abort error;
180
181
182 /* Like callPackage, but for a function that returns an attribute
183 set of derivations. The override function is added to the
184 individual attributes. */
185 callPackagesWith = autoArgs: fn: args:
186 let
187 f = if lib.isFunction fn then fn else import fn;
188 auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs;
189 origArgs = auto // args;
190 pkgs = f origArgs;
191 mkAttrOverridable = name: _: makeOverridable (newArgs: (f newArgs).${name}) origArgs;
192 in
193 if lib.isDerivation pkgs then throw
194 ("function `callPackages` was called on a *single* derivation "
195 + ''"${pkgs.name or "<unknown-name>"}";''
196 + " did you mean to use `callPackage` instead?")
197 else lib.mapAttrs mkAttrOverridable pkgs;
198
199
200 /* Add attributes to each output of a derivation without changing
201 the derivation itself and check a given condition when evaluating. */
202 extendDerivation = condition: passthru: drv:
203 let
204 outputs = drv.outputs or [ "out" ];
205
206 commonAttrs = drv // (builtins.listToAttrs outputsList) //
207 ({ all = map (x: x.value) outputsList; }) // passthru;
208
209 outputToAttrListElement = outputName:
210 { name = outputName;
211 value = commonAttrs // {
212 inherit (drv.${outputName}) type outputName;
213 outputSpecified = true;
214 drvPath = assert condition; drv.${outputName}.drvPath;
215 outPath = assert condition; drv.${outputName}.outPath;
216 } //
217 # TODO: give the derivation control over the outputs.
218 # `overrideAttrs` may not be the only attribute that needs
219 # updating when switching outputs.
220 lib.optionalAttrs (passthru?overrideAttrs) {
221 # TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing.
222 overrideAttrs = f: (passthru.overrideAttrs f).${outputName};
223 };
224 };
225
226 outputsList = map outputToAttrListElement outputs;
227 in commonAttrs // {
228 drvPath = assert condition; drv.drvPath;
229 outPath = assert condition; drv.outPath;
230 };
231
232 /* Strip a derivation of all non-essential attributes, returning
233 only those needed by hydra-eval-jobs. Also strictly evaluate the
234 result to ensure that there are no thunks kept alive to prevent
235 garbage collection. */
236 hydraJob = drv:
237 let
238 outputs = drv.outputs or ["out"];
239
240 commonAttrs =
241 { inherit (drv) name system meta; inherit outputs; }
242 // lib.optionalAttrs (drv._hydraAggregate or false) {
243 _hydraAggregate = true;
244 constituents = map hydraJob (lib.flatten drv.constituents);
245 }
246 // (lib.listToAttrs outputsList);
247
248 makeOutput = outputName:
249 let output = drv.${outputName}; in
250 { name = outputName;
251 value = commonAttrs // {
252 outPath = output.outPath;
253 drvPath = output.drvPath;
254 type = "derivation";
255 inherit outputName;
256 };
257 };
258
259 outputsList = map makeOutput outputs;
260
261 drv' = (lib.head outputsList).value;
262 in if drv == null then null else
263 lib.deepSeq drv' drv';
264
265 /* Make a set of packages with a common scope. All packages called
266 with the provided `callPackage` will be evaluated with the same
267 arguments. Any package in the set may depend on any other. The
268 `overrideScope'` function allows subsequent modification of the package
269 set in a consistent way, i.e. all packages in the set will be
270 called with the overridden packages. The package sets may be
271 hierarchical: the packages in the set are called with the scope
272 provided by `newScope` and the set provides a `newScope` attribute
273 which can form the parent scope for later package sets. */
274 makeScope = newScope: f:
275 let self = f self // {
276 newScope = scope: newScope (self // scope);
277 callPackage = self.newScope {};
278 overrideScope = g: lib.warn
279 "`overrideScope` (from `lib.makeScope`) is deprecated. Do `overrideScope' (self: super: { … })` instead of `overrideScope (super: self: { … })`. All other overrides have the parameters in that order, including other definitions of `overrideScope`. This was the only definition violating the pattern."
280 (makeScope newScope (lib.fixedPoints.extends (lib.flip g) f));
281 overrideScope' = g: makeScope newScope (lib.fixedPoints.extends g f);
282 packages = f;
283 };
284 in self;
285
286 /* Like the above, but aims to support cross compilation. It's still ugly, but
287 hopefully it helps a little bit. */
288 makeScopeWithSplicing = splicePackages: newScope: otherSplices: keep: extra: f:
289 let
290 spliced0 = splicePackages {
291 pkgsBuildBuild = otherSplices.selfBuildBuild;
292 pkgsBuildHost = otherSplices.selfBuildHost;
293 pkgsBuildTarget = otherSplices.selfBuildTarget;
294 pkgsHostHost = otherSplices.selfHostHost;
295 pkgsHostTarget = self; # Not `otherSplices.selfHostTarget`;
296 pkgsTargetTarget = otherSplices.selfTargetTarget;
297 };
298 spliced = extra spliced0 // spliced0 // keep self;
299 self = f self // {
300 newScope = scope: newScope (spliced // scope);
301 callPackage = newScope spliced; # == self.newScope {};
302 # N.B. the other stages of the package set spliced in are *not*
303 # overridden.
304 overrideScope = g: makeScopeWithSplicing
305 splicePackages
306 newScope
307 otherSplices
308 keep
309 extra
310 (lib.fixedPoints.extends g f);
311 packages = f;
312 };
313 in self;
314
315}