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.
17
18 For another application, see build-support/vm, where this
19 function is used to build arbitrary derivations inside a QEMU
20 virtual machine.
21
22 Note that in order to preserve evaluation errors, the new derivation's
23 outPath depends on the old one's, which means that this function cannot
24 be used in circular situations when the old derivation also depends on the
25 new one.
26
27 You should in general prefer `drv.overrideAttrs` over this function;
28 see the nixpkgs manual for more information on overriding.
29
30 Example:
31 mySed = overrideDerivation pkgs.gnused (oldAttrs: {
32 name = "sed-4.2.2-pre";
33 src = fetchurl {
34 url = ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2;
35 hash = "sha256-MxBJRcM2rYzQYwJ5XKxhXTQByvSg5jZc5cSHEZoB2IY=";
36 };
37 patches = [];
38 });
39
40 Type:
41 overrideDerivation :: Derivation -> ( Derivation -> AttrSet ) -> Derivation
42 */
43 overrideDerivation = drv: f:
44 let
45 newDrv = derivation (drv.drvAttrs // (f drv));
46 in lib.flip (extendDerivation (builtins.seq drv.drvPath true)) newDrv (
47 { meta = drv.meta or {};
48 passthru = if drv ? passthru then drv.passthru else {};
49 }
50 //
51 (drv.passthru or {})
52 //
53 lib.optionalAttrs (drv ? __spliced) {
54 __spliced = {} // (lib.mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced);
55 });
56
57
58 /* `makeOverridable` takes a function from attribute set to attribute set and
59 injects `override` attribute which can be used to override arguments of
60 the function.
61
62 Please refer to documentation on [`<pkg>.overrideDerivation`](#sec-pkg-overrideDerivation) to learn about `overrideDerivation` and caveats
63 related to its use.
64
65 Example:
66 nix-repl> x = {a, b}: { result = a + b; }
67
68 nix-repl> y = lib.makeOverridable x { a = 1; b = 2; }
69
70 nix-repl> y
71 { override = «lambda»; overrideDerivation = «lambda»; result = 3; }
72
73 nix-repl> y.override { a = 10; }
74 { override = «lambda»; overrideDerivation = «lambda»; result = 12; }
75
76 Type:
77 makeOverridable :: (AttrSet -> a) -> AttrSet -> a
78 */
79 makeOverridable = f:
80 let
81 # Creates a functor with the same arguments as f
82 mirrorArgs = lib.mirrorFunctionArgs f;
83 in
84 mirrorArgs (origArgs:
85 let
86 result = f origArgs;
87
88 # Changes the original arguments with (potentially a function that returns) a set of new attributes
89 overrideWith = newArgs: origArgs // (if lib.isFunction newArgs then newArgs origArgs else newArgs);
90
91 # Re-call the function but with different arguments
92 overrideArgs = mirrorArgs (newArgs: makeOverridable f (overrideWith newArgs));
93 # Change the result of the function call by applying g to it
94 overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs;
95 in
96 if builtins.isAttrs result then
97 result // {
98 override = overrideArgs;
99 overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
100 ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
101 overrideResult (x: x.overrideAttrs fdrv);
102 }
103 else if lib.isFunction result then
104 # Transform the result into a functor while propagating its arguments
105 lib.setFunctionArgs result (lib.functionArgs result) // {
106 override = overrideArgs;
107 }
108 else result);
109
110
111 /* Call the package function in the file `fn` with the required
112 arguments automatically. The function is called with the
113 arguments `args`, but any missing arguments are obtained from
114 `autoArgs`. This function is intended to be partially
115 parameterised, e.g.,
116
117 ```nix
118 callPackage = callPackageWith pkgs;
119 pkgs = {
120 libfoo = callPackage ./foo.nix { };
121 libbar = callPackage ./bar.nix { };
122 };
123 ```
124
125 If the `libbar` function expects an argument named `libfoo`, it is
126 automatically passed as an argument. Overrides or missing
127 arguments can be supplied in `args`, e.g.
128
129 ```nix
130 libbar = callPackage ./bar.nix {
131 libfoo = null;
132 enableX11 = true;
133 };
134 ```
135
136 <!-- TODO: Apply "Example:" tag to the examples above -->
137
138 Type:
139 callPackageWith :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a
140 */
141 callPackageWith = autoArgs: fn: args:
142 let
143 f = if lib.isFunction fn then fn else import fn;
144 fargs = lib.functionArgs f;
145
146 # All arguments that will be passed to the function
147 # This includes automatic ones and ones passed explicitly
148 allArgs = builtins.intersectAttrs fargs autoArgs // args;
149
150 # a list of argument names that the function requires, but
151 # wouldn't be passed to it
152 missingArgs = lib.attrNames
153 # Filter out arguments that have a default value
154 (lib.filterAttrs (name: value: ! value)
155 # Filter out arguments that would be passed
156 (removeAttrs fargs (lib.attrNames allArgs)));
157
158 # Get a list of suggested argument names for a given missing one
159 getSuggestions = arg: lib.pipe (autoArgs // args) [
160 lib.attrNames
161 # Only use ones that are at most 2 edits away. While mork would work,
162 # levenshteinAtMost is only fast for 2 or less.
163 (lib.filter (lib.strings.levenshteinAtMost 2 arg))
164 # Put strings with shorter distance first
165 (lib.sort (x: y: lib.strings.levenshtein x arg < lib.strings.levenshtein y arg))
166 # Only take the first couple results
167 (lib.take 3)
168 # Quote all entries
169 (map (x: "\"" + x + "\""))
170 ];
171
172 prettySuggestions = suggestions:
173 if suggestions == [] then ""
174 else if lib.length suggestions == 1 then ", did you mean ${lib.elemAt suggestions 0}?"
175 else ", did you mean ${lib.concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?";
176
177 errorForArg = arg:
178 let
179 loc = builtins.unsafeGetAttrPos arg fargs;
180 # loc' can be removed once lib/minver.nix is >2.3.4, since that includes
181 # https://github.com/NixOS/nix/pull/3468 which makes loc be non-null
182 loc' = if loc != null then loc.file + ":" + toString loc.line
183 else if ! lib.isFunction fn then
184 toString fn + lib.optionalString (lib.sources.pathIsDirectory fn) "/default.nix"
185 else "<unknown location>";
186 in "Function called without required argument \"${arg}\" at "
187 + "${loc'}${prettySuggestions (getSuggestions arg)}";
188
189 # Only show the error for the first missing argument
190 error = errorForArg (lib.head missingArgs);
191
192 in if missingArgs == [] then makeOverridable f allArgs else abort error;
193
194
195 /* Like callPackage, but for a function that returns an attribute
196 set of derivations. The override function is added to the
197 individual attributes.
198
199 Type:
200 callPackagesWith :: AttrSet -> ((AttrSet -> AttrSet) | Path) -> AttrSet -> AttrSet
201 */
202 callPackagesWith = autoArgs: fn: args:
203 let
204 f = if lib.isFunction fn then fn else import fn;
205 auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs;
206 origArgs = auto // args;
207 pkgs = f origArgs;
208 mkAttrOverridable = name: _: makeOverridable (newArgs: (f newArgs).${name}) origArgs;
209 in
210 if lib.isDerivation pkgs then throw
211 ("function `callPackages` was called on a *single* derivation "
212 + ''"${pkgs.name or "<unknown-name>"}";''
213 + " did you mean to use `callPackage` instead?")
214 else lib.mapAttrs mkAttrOverridable pkgs;
215
216
217 /* Add attributes to each output of a derivation without changing
218 the derivation itself and check a given condition when evaluating.
219
220 Type:
221 extendDerivation :: Bool -> Any -> Derivation -> Derivation
222 */
223 extendDerivation = condition: passthru: drv:
224 let
225 outputs = drv.outputs or [ "out" ];
226
227 commonAttrs = drv // (builtins.listToAttrs outputsList) //
228 ({ all = map (x: x.value) outputsList; }) // passthru;
229
230 outputToAttrListElement = outputName:
231 { name = outputName;
232 value = commonAttrs // {
233 inherit (drv.${outputName}) type outputName;
234 outputSpecified = true;
235 drvPath = assert condition; drv.${outputName}.drvPath;
236 outPath = assert condition; drv.${outputName}.outPath;
237 } //
238 # TODO: give the derivation control over the outputs.
239 # `overrideAttrs` may not be the only attribute that needs
240 # updating when switching outputs.
241 lib.optionalAttrs (passthru?overrideAttrs) {
242 # TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing.
243 overrideAttrs = f: (passthru.overrideAttrs f).${outputName};
244 };
245 };
246
247 outputsList = map outputToAttrListElement outputs;
248 in commonAttrs // {
249 drvPath = assert condition; drv.drvPath;
250 outPath = assert condition; drv.outPath;
251 };
252
253 /* Strip a derivation of all non-essential attributes, returning
254 only those needed by hydra-eval-jobs. Also strictly evaluate the
255 result to ensure that there are no thunks kept alive to prevent
256 garbage collection.
257
258 Type:
259 hydraJob :: (Derivation | Null) -> (Derivation | Null)
260 */
261 hydraJob = drv:
262 let
263 outputs = drv.outputs or ["out"];
264
265 commonAttrs =
266 { inherit (drv) name system meta; inherit outputs; }
267 // lib.optionalAttrs (drv._hydraAggregate or false) {
268 _hydraAggregate = true;
269 constituents = map hydraJob (lib.flatten drv.constituents);
270 }
271 // (lib.listToAttrs outputsList);
272
273 makeOutput = outputName:
274 let output = drv.${outputName}; in
275 { name = outputName;
276 value = commonAttrs // {
277 outPath = output.outPath;
278 drvPath = output.drvPath;
279 type = "derivation";
280 inherit outputName;
281 };
282 };
283
284 outputsList = map makeOutput outputs;
285
286 drv' = (lib.head outputsList).value;
287 in if drv == null then null else
288 lib.deepSeq drv' drv';
289
290 /* Make a set of packages with a common scope. All packages called
291 with the provided `callPackage` will be evaluated with the same
292 arguments. Any package in the set may depend on any other. The
293 `overrideScope'` function allows subsequent modification of the package
294 set in a consistent way, i.e. all packages in the set will be
295 called with the overridden packages. The package sets may be
296 hierarchical: the packages in the set are called with the scope
297 provided by `newScope` and the set provides a `newScope` attribute
298 which can form the parent scope for later package sets.
299
300 Type:
301 makeScope :: (AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a) -> (AttrSet -> AttrSet) -> AttrSet
302 */
303 makeScope = newScope: f:
304 let self = f self // {
305 newScope = scope: newScope (self // scope);
306 callPackage = self.newScope {};
307 overrideScope = g: makeScope newScope (lib.fixedPoints.extends g f);
308 # Remove after 24.11 is released.
309 overrideScope' = g: lib.warnIf (lib.isInOldestRelease 2311)
310 "`overrideScope'` (from `lib.makeScope`) has been renamed to `overrideScope`."
311 (makeScope newScope (lib.fixedPoints.extends g f));
312 packages = f;
313 };
314 in self;
315
316 /* backward compatibility with old uncurried form; deprecated */
317 makeScopeWithSplicing =
318 splicePackages: newScope: otherSplices: keep: extra: f:
319 makeScopeWithSplicing'
320 { inherit splicePackages newScope; }
321 { inherit otherSplices keep extra f; };
322
323 /* Like makeScope, but aims to support cross compilation. It's still ugly, but
324 hopefully it helps a little bit.
325
326 Type:
327 makeScopeWithSplicing' ::
328 { splicePackages :: Splice -> AttrSet
329 , newScope :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a
330 }
331 -> { otherSplices :: Splice, keep :: AttrSet -> AttrSet, extra :: AttrSet -> AttrSet }
332 -> AttrSet
333
334 Splice ::
335 { pkgsBuildBuild :: AttrSet
336 , pkgsBuildHost :: AttrSet
337 , pkgsBuildTarget :: AttrSet
338 , pkgsHostHost :: AttrSet
339 , pkgsHostTarget :: AttrSet
340 , pkgsTargetTarget :: AttrSet
341 }
342 */
343 makeScopeWithSplicing' =
344 { splicePackages
345 , newScope
346 }:
347 { otherSplices
348 # Attrs from `self` which won't be spliced.
349 # Avoid using keep, it's only used for a python hook workaround, added in PR #104201.
350 # ex: `keep = (self: { inherit (self) aAttr; })`
351 , keep ? (_self: {})
352 # Additional attrs to add to the sets `callPackage`.
353 # When the package is from a subset (but not a subset within a package IS #211340)
354 # within `spliced0` it will be spliced.
355 # When using an package outside the set but it's available from `pkgs`, use the package from `pkgs.__splicedPackages`.
356 # If the package is not available within the set or in `pkgs`, such as a package in a let binding, it will not be spliced
357 # ex:
358 # ```
359 # nix-repl> darwin.apple_sdk.frameworks.CoreFoundation
360 # «derivation ...CoreFoundation-11.0.0.drv»
361 # nix-repl> darwin.CoreFoundation
362 # error: attribute 'CoreFoundation' missing
363 # nix-repl> darwin.callPackage ({ CoreFoundation }: CoreFoundation) { }
364 # «derivation ...CoreFoundation-11.0.0.drv»
365 # ```
366 , extra ? (_spliced0: {})
367 , f
368 }:
369 let
370 spliced0 = splicePackages {
371 pkgsBuildBuild = otherSplices.selfBuildBuild;
372 pkgsBuildHost = otherSplices.selfBuildHost;
373 pkgsBuildTarget = otherSplices.selfBuildTarget;
374 pkgsHostHost = otherSplices.selfHostHost;
375 pkgsHostTarget = self; # Not `otherSplices.selfHostTarget`;
376 pkgsTargetTarget = otherSplices.selfTargetTarget;
377 };
378 spliced = extra spliced0 // spliced0 // keep self;
379 self = f self // {
380 newScope = scope: newScope (spliced // scope);
381 callPackage = newScope spliced; # == self.newScope {};
382 # N.B. the other stages of the package set spliced in are *not*
383 # overridden.
384 overrideScope = g: (makeScopeWithSplicing'
385 { inherit splicePackages newScope; }
386 { inherit otherSplices keep extra;
387 f = lib.fixedPoints.extends g f;
388 });
389 packages = f;
390 };
391 in self;
392
393}