1{ lib }:
2
3let
4 inherit (builtins)
5 intersectAttrs
6 unsafeGetAttrPos
7 ;
8 inherit (lib)
9 functionArgs
10 isFunction
11 mirrorFunctionArgs
12 isAttrs
13 setFunctionArgs
14 optionalAttrs
15 attrNames
16 filter
17 elemAt
18 concatStringsSep
19 sortOn
20 take
21 length
22 filterAttrs
23 optionalString
24 flip
25 pathIsDirectory
26 head
27 pipe
28 isDerivation
29 listToAttrs
30 mapAttrs
31 seq
32 flatten
33 deepSeq
34 extends
35 toFunction
36 id
37 ;
38 inherit (lib.strings) levenshtein levenshteinAtMost;
39
40in
41rec {
42
43 /**
44 `overrideDerivation drv f` takes a derivation (i.e., the result
45 of a call to the builtin function `derivation`) and returns a new
46 derivation in which the attributes of the original are overridden
47 according to the function `f`. The function `f` is called with
48 the original derivation attributes.
49
50 `overrideDerivation` allows certain "ad-hoc" customisation
51 scenarios (e.g. in ~/.config/nixpkgs/config.nix). For instance,
52 if you want to "patch" the derivation returned by a package
53 function in Nixpkgs to build another version than what the
54 function itself provides.
55
56 For another application, see build-support/vm, where this
57 function is used to build arbitrary derivations inside a QEMU
58 virtual machine.
59
60 Note that in order to preserve evaluation errors, the new derivation's
61 outPath depends on the old one's, which means that this function cannot
62 be used in circular situations when the old derivation also depends on the
63 new one.
64
65 You should in general prefer `drv.overrideAttrs` over this function;
66 see the nixpkgs manual for more information on overriding.
67
68 # Inputs
69
70 `drv`
71
72 : 1\. Function argument
73
74 `f`
75
76 : 2\. Function argument
77
78 # Type
79
80 ```
81 overrideDerivation :: Derivation -> ( Derivation -> AttrSet ) -> Derivation
82 ```
83
84 # Examples
85 :::{.example}
86 ## `lib.customisation.overrideDerivation` usage example
87
88 ```nix
89 mySed = overrideDerivation pkgs.gnused (oldAttrs: {
90 name = "sed-4.2.2-pre";
91 src = fetchurl {
92 url = ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2;
93 hash = "sha256-MxBJRcM2rYzQYwJ5XKxhXTQByvSg5jZc5cSHEZoB2IY=";
94 };
95 patches = [];
96 });
97 ```
98
99 :::
100 */
101 overrideDerivation =
102 drv: f:
103 let
104 newDrv = derivation (drv.drvAttrs // (f drv));
105 in
106 flip (extendDerivation (seq drv.drvPath true)) newDrv (
107 {
108 meta = drv.meta or { };
109 passthru = if drv ? passthru then drv.passthru else { };
110 }
111 // (drv.passthru or { })
112 // optionalAttrs (drv ? __spliced) {
113 __spliced = { } // (mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced);
114 }
115 );
116
117 /**
118 `makeOverridable` takes a function from attribute set to attribute set and
119 injects `override` attribute which can be used to override arguments of
120 the function.
121
122 Please refer to documentation on [`<pkg>.overrideDerivation`](#sec-pkg-overrideDerivation) to learn about `overrideDerivation` and caveats
123 related to its use.
124
125 # Inputs
126
127 `f`
128
129 : 1\. Function argument
130
131 # Type
132
133 ```
134 makeOverridable :: (AttrSet -> a) -> AttrSet -> a
135 ```
136
137 # Examples
138 :::{.example}
139 ## `lib.customisation.makeOverridable` usage example
140
141 ```nix
142 nix-repl> x = {a, b}: { result = a + b; }
143
144 nix-repl> y = lib.makeOverridable x { a = 1; b = 2; }
145
146 nix-repl> y
147 { override = «lambda»; overrideDerivation = «lambda»; result = 3; }
148
149 nix-repl> y.override { a = 10; }
150 { override = «lambda»; overrideDerivation = «lambda»; result = 12; }
151 ```
152
153 :::
154 */
155 makeOverridable =
156 f:
157 let
158 # Creates a functor with the same arguments as f
159 mirrorArgs = mirrorFunctionArgs f;
160 in
161 mirrorArgs (
162 origArgs:
163 let
164 result = f origArgs;
165
166 # Changes the original arguments with (potentially a function that returns) a set of new attributes
167 overrideWith = newArgs: origArgs // (if isFunction newArgs then newArgs origArgs else newArgs);
168
169 # Re-call the function but with different arguments
170 overrideArgs = mirrorArgs (
171 /**
172 Change the arguments with which a certain function is called.
173
174 In some cases, you may find a list of possible attributes to pass in this function's `__functionArgs` attribute, but it will not be complete for an original function like `args@{foo, ...}: ...`, which accepts arbitrary attributes.
175
176 This function was provided by `lib.makeOverridable`.
177 */
178 newArgs: makeOverridable f (overrideWith newArgs)
179 );
180 # Change the result of the function call by applying g to it
181 overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs;
182 in
183 if isAttrs result then
184 result
185 // {
186 override = overrideArgs;
187 overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
188 ${if result ? overrideAttrs then "overrideAttrs" else null} =
189 /**
190 Override the attributes that were passed to `mkDerivation` in order to generate this derivation.
191
192 This function is provided by `lib.makeOverridable`, and indirectly by `callPackage` among others, in order to make the combination of `override` and `overrideAttrs` work.
193 Specifically, it re-adds the `override` attribute to the result of `overrideAttrs`.
194
195 The real implementation of `overrideAttrs` is provided by `stdenv.mkDerivation`.
196 */
197 # NOTE: part of the above documentation had to be duplicated in `mkDerivation`'s `overrideAttrs`.
198 # design/tech debt issue: https://github.com/NixOS/nixpkgs/issues/273815
199 fdrv: overrideResult (x: x.overrideAttrs fdrv);
200 }
201 else if isFunction result then
202 # Transform the result into a functor while propagating its arguments
203 setFunctionArgs result (functionArgs result)
204 // {
205 override = overrideArgs;
206 }
207 else
208 result
209 );
210
211 /**
212 Call the package function in the file `fn` with the required
213 arguments automatically. The function is called with the
214 arguments `args`, but any missing arguments are obtained from
215 `autoArgs`. This function is intended to be partially
216 parameterised, e.g.,
217
218 ```nix
219 callPackage = callPackageWith pkgs;
220 pkgs = {
221 libfoo = callPackage ./foo.nix { };
222 libbar = callPackage ./bar.nix { };
223 };
224 ```
225
226 If the `libbar` function expects an argument named `libfoo`, it is
227 automatically passed as an argument. Overrides or missing
228 arguments can be supplied in `args`, e.g.
229
230 ```nix
231 libbar = callPackage ./bar.nix {
232 libfoo = null;
233 enableX11 = true;
234 };
235 ```
236
237 <!-- TODO: Apply "Example:" tag to the examples above -->
238
239 # Inputs
240
241 `autoArgs`
242
243 : 1\. Function argument
244
245 `fn`
246
247 : 2\. Function argument
248
249 `args`
250
251 : 3\. Function argument
252
253 # Type
254
255 ```
256 callPackageWith :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a
257 ```
258 */
259 callPackageWith =
260 autoArgs: fn: args:
261 let
262 f = if isFunction fn then fn else import fn;
263 fargs = functionArgs f;
264
265 # All arguments that will be passed to the function
266 # This includes automatic ones and ones passed explicitly
267 allArgs = intersectAttrs fargs autoArgs // args;
268
269 # a list of argument names that the function requires, but
270 # wouldn't be passed to it
271 missingArgs =
272 # Filter out arguments that have a default value
273 (
274 filterAttrs (name: value: !value)
275 # Filter out arguments that would be passed
276 (removeAttrs fargs (attrNames allArgs))
277 );
278
279 # Get a list of suggested argument names for a given missing one
280 getSuggestions =
281 arg:
282 pipe (autoArgs // args) [
283 attrNames
284 # Only use ones that are at most 2 edits away. While mork would work,
285 # levenshteinAtMost is only fast for 2 or less.
286 (filter (levenshteinAtMost 2 arg))
287 # Put strings with shorter distance first
288 (sortOn (levenshtein arg))
289 # Only take the first couple results
290 (take 3)
291 # Quote all entries
292 (map (x: "\"" + x + "\""))
293 ];
294
295 prettySuggestions =
296 suggestions:
297 if suggestions == [ ] then
298 ""
299 else if length suggestions == 1 then
300 ", did you mean ${elemAt suggestions 0}?"
301 else
302 ", did you mean ${concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?";
303
304 errorForArg =
305 arg:
306 let
307 loc = unsafeGetAttrPos arg fargs;
308 in
309 "Function called without required argument \"${arg}\" at "
310 + "${loc.file}:${toString loc.line}${prettySuggestions (getSuggestions arg)}";
311
312 # Only show the error for the first missing argument
313 error = errorForArg (head (attrNames missingArgs));
314
315 in
316 if missingArgs == { } then
317 makeOverridable f allArgs
318 # This needs to be an abort so it can't be caught with `builtins.tryEval`,
319 # which is used by nix-env and ofborg to filter out packages that don't evaluate.
320 # This way we're forced to fix such errors in Nixpkgs,
321 # which is especially relevant with allowAliases = false
322 else
323 abort "lib.customisation.callPackageWith: ${error}";
324
325 /**
326 Like callPackage, but for a function that returns an attribute
327 set of derivations. The override function is added to the
328 individual attributes.
329
330 # Inputs
331
332 `autoArgs`
333
334 : 1\. Function argument
335
336 `fn`
337
338 : 2\. Function argument
339
340 `args`
341
342 : 3\. Function argument
343
344 # Type
345
346 ```
347 callPackagesWith :: AttrSet -> ((AttrSet -> AttrSet) | Path) -> AttrSet -> AttrSet
348 ```
349 */
350 callPackagesWith =
351 autoArgs: fn: args:
352 let
353 f = if isFunction fn then fn else import fn;
354 auto = intersectAttrs (functionArgs f) autoArgs;
355 mirrorArgs = mirrorFunctionArgs f;
356 origArgs = auto // args;
357 pkgs = f origArgs;
358 mkAttrOverridable = name: _: makeOverridable (mirrorArgs (newArgs: (f newArgs).${name})) origArgs;
359 in
360 if isDerivation pkgs then
361 throw (
362 "function `callPackages` was called on a *single* derivation "
363 + ''"${pkgs.name or "<unknown-name>"}";''
364 + " did you mean to use `callPackage` instead?"
365 )
366 else
367 mapAttrs mkAttrOverridable pkgs;
368
369 /**
370 Add attributes to each output of a derivation without changing
371 the derivation itself and check a given condition when evaluating.
372
373 # Inputs
374
375 `condition`
376
377 : 1\. Function argument
378
379 `passthru`
380
381 : 2\. Function argument
382
383 `drv`
384
385 : 3\. Function argument
386
387 # Type
388
389 ```
390 extendDerivation :: Bool -> Any -> Derivation -> Derivation
391 ```
392 */
393 extendDerivation =
394 condition: passthru: drv:
395 let
396 outputs = drv.outputs or [ "out" ];
397
398 commonAttrs =
399 drv // (listToAttrs outputsList) // ({ all = map (x: x.value) outputsList; }) // passthru;
400
401 outputToAttrListElement = outputName: {
402 name = outputName;
403 value =
404 commonAttrs
405 // {
406 inherit (drv.${outputName}) type outputName;
407 outputSpecified = true;
408 drvPath =
409 assert condition;
410 drv.${outputName}.drvPath;
411 outPath =
412 assert condition;
413 drv.${outputName}.outPath;
414 }
415 //
416 # TODO: give the derivation control over the outputs.
417 # `overrideAttrs` may not be the only attribute that needs
418 # updating when switching outputs.
419 optionalAttrs (passthru ? overrideAttrs) {
420 # TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing.
421 overrideAttrs = f: (passthru.overrideAttrs f).${outputName};
422 };
423 };
424
425 outputsList = map outputToAttrListElement outputs;
426 in
427 commonAttrs
428 // {
429 drvPath =
430 assert condition;
431 drv.drvPath;
432 outPath =
433 assert condition;
434 drv.outPath;
435 };
436
437 /**
438 Strip a derivation of all non-essential attributes, returning
439 only those needed by hydra-eval-jobs. Also strictly evaluate the
440 result to ensure that there are no thunks kept alive to prevent
441 garbage collection.
442
443 # Inputs
444
445 `drv`
446
447 : 1\. Function argument
448
449 # Type
450
451 ```
452 hydraJob :: (Derivation | Null) -> (Derivation | Null)
453 ```
454 */
455 hydraJob =
456 drv:
457 let
458 outputs = drv.outputs or [ "out" ];
459
460 commonAttrs = {
461 inherit (drv) name system meta;
462 inherit outputs;
463 }
464 // optionalAttrs (drv._hydraAggregate or false) {
465 _hydraAggregate = true;
466 constituents = map hydraJob (flatten drv.constituents);
467 }
468 // (listToAttrs outputsList);
469
470 makeOutput =
471 outputName:
472 let
473 output = drv.${outputName};
474 in
475 {
476 name = outputName;
477 value = commonAttrs // {
478 outPath = output.outPath;
479 drvPath = output.drvPath;
480 type = "derivation";
481 inherit outputName;
482 };
483 };
484
485 outputsList = map makeOutput outputs;
486
487 drv' = (head outputsList).value;
488 in
489 if drv == null then null else deepSeq drv' drv';
490
491 /**
492 Make an attribute set (a "scope") from functions that take arguments from that same attribute set.
493 See [](#ex-makeScope) for how to use it.
494
495 # Inputs
496
497 1. `newScope` (`AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a`)
498
499 A function that takes an attribute set `attrs` and returns what ends up as `callPackage` in the output.
500
501 Typical values are `callPackageWith` or the output attribute `newScope`.
502
503 2. `f` (`AttrSet -> AttrSet`)
504
505 A function that takes an attribute set as returned by `makeScope newScope f` (a "scope") and returns any attribute set.
506
507 This function is used to compute the fixpoint of the resulting scope using `callPackage`.
508 Its argument is the lazily evaluated reference to the value of that fixpoint, and is typically called `self` or `final`.
509
510 See [](#ex-makeScope) for how to use it.
511 See [](#sec-functions-library-fixedPoints) for details on fixpoint computation.
512
513 # Output
514
515 `makeScope` returns an attribute set of a form called `scope`, which also contains the final attributes produced by `f`:
516
517 ```
518 scope :: {
519 callPackage :: ((AttrSet -> a) | Path) -> AttrSet -> a
520 newScope = AttrSet -> scope
521 overrideScope = (scope -> scope -> AttrSet) -> scope
522 packages :: AttrSet -> AttrSet
523 }
524 ```
525
526 - `callPackage` (`((AttrSet -> a) | Path) -> AttrSet -> a`)
527
528 A function that
529
530 1. Takes a function `p`, or a path to a Nix file that contains a function `p`, which takes an attribute set and returns value of arbitrary type `a`,
531 2. Takes an attribute set `args` with explicit attributes to pass to `p`,
532 3. Calls `f` with attributes from the original attribute set `attrs` passed to `newScope` updated with `args`, i.e. `attrs // args`, if they match the attributes in the argument of `p`.
533
534 All such functions `p` will be called with the same value for `attrs`.
535
536 See [](#ex-makeScope-callPackage) for how to use it.
537
538 - `newScope` (`AttrSet -> scope`)
539
540 Takes an attribute set `attrs` and returns a scope that extends the original scope.
541
542 - `overrideScope` (`(scope -> scope -> AttrSet) -> scope`)
543
544 Takes a function `g` of the form `final: prev: { # attributes }` to act as an overlay on `f`, and returns a new scope with values determined by `extends g f`.
545 See [](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.fixedPoints.extends) for details.
546
547 This allows subsequent modification of the final attribute set in a consistent way, i.e. all functions `p` invoked with `callPackage` will be called with the modified values.
548
549 - `packages` (`AttrSet -> AttrSet`)
550
551 The value of the argument `f` to `makeScope`.
552
553 - final attributes
554
555 The final values returned by `f`.
556
557 # Examples
558
559 :::{#ex-makeScope .example}
560 # Create an interdependent package set on top of `pkgs`
561
562 The functions in `foo.nix` and `bar.nix` can depend on each other, in the sense that `foo.nix` can contain a function that expects `bar` as an attribute in its argument.
563
564 ```nix
565 let
566 pkgs = import <nixpkgs> { };
567 in
568 pkgs.lib.makeScope pkgs.newScope (self: {
569 foo = self.callPackage ./foo.nix { };
570 bar = self.callPackage ./bar.nix { };
571 })
572 ```
573
574 evaluates to
575
576 ```nix
577 {
578 callPackage = «lambda»;
579 newScope = «lambda»;
580 overrideScope = «lambda»;
581 packages = «lambda»;
582 foo = «derivation»;
583 bar = «derivation»;
584 }
585 ```
586 :::
587
588 :::{#ex-makeScope-callPackage .example}
589 # Using `callPackage` from a scope
590
591 ```nix
592 let
593 pkgs = import <nixpkgs> { };
594 inherit (pkgs) lib;
595 scope = lib.makeScope lib.callPackageWith (self: { a = 1; b = 2; });
596 three = scope.callPackage ({ a, b }: a + b) { };
597 four = scope.callPackage ({ a, b }: a + b) { a = 2; };
598 in
599 [ three four ]
600 ```
601
602 evaluates to
603
604 ```nix
605 [ 3 4 ]
606 ```
607 :::
608
609 # Type
610
611 ```
612 makeScope :: (AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a) -> (AttrSet -> AttrSet) -> scope
613 ```
614 */
615 makeScope =
616 newScope: f:
617 let
618 self = f self // {
619 newScope = scope: newScope (self // scope);
620 callPackage = self.newScope { };
621 overrideScope = g: makeScope newScope (extends g f);
622 packages = f;
623 };
624 in
625 self;
626
627 /**
628 backward compatibility with old uncurried form; deprecated
629
630 # Inputs
631
632 `splicePackages`
633
634 : 1\. Function argument
635
636 `newScope`
637
638 : 2\. Function argument
639
640 `otherSplices`
641
642 : 3\. Function argument
643
644 `keep`
645
646 : 4\. Function argument
647
648 `extra`
649
650 : 5\. Function argument
651
652 `f`
653
654 : 6\. Function argument
655 */
656 makeScopeWithSplicing =
657 splicePackages: newScope: otherSplices: keep: extra: f:
658 makeScopeWithSplicing' { inherit splicePackages newScope; } {
659 inherit
660 otherSplices
661 keep
662 extra
663 f
664 ;
665 };
666
667 /**
668 Like makeScope, but aims to support cross compilation. It's still ugly, but
669 hopefully it helps a little bit.
670
671 # Type
672
673 ```
674 makeScopeWithSplicing' ::
675 { splicePackages :: Splice -> AttrSet
676 , newScope :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a
677 }
678 -> { otherSplices :: Splice, keep :: AttrSet -> AttrSet, extra :: AttrSet -> AttrSet }
679 -> AttrSet
680
681 Splice ::
682 { pkgsBuildBuild :: AttrSet
683 , pkgsBuildHost :: AttrSet
684 , pkgsBuildTarget :: AttrSet
685 , pkgsHostHost :: AttrSet
686 , pkgsHostTarget :: AttrSet
687 , pkgsTargetTarget :: AttrSet
688 }
689 ```
690 */
691 makeScopeWithSplicing' =
692 {
693 splicePackages,
694 newScope,
695 }:
696 {
697 otherSplices,
698 # Attrs from `self` which won't be spliced.
699 # Avoid using keep, it's only used for a python hook workaround, added in PR #104201.
700 # ex: `keep = (self: { inherit (self) aAttr; })`
701 keep ? (_self: { }),
702 # Additional attrs to add to the sets `callPackage`.
703 # When the package is from a subset (but not a subset within a package IS #211340)
704 # within `spliced0` it will be spliced.
705 # When using an package outside the set but it's available from `pkgs`, use the package from `pkgs.__splicedPackages`.
706 # 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
707 # ex:
708 # ```
709 # nix-repl> darwin.apple_sdk.frameworks.CoreFoundation
710 # «derivation ...CoreFoundation-11.0.0.drv»
711 # nix-repl> darwin.CoreFoundation
712 # error: attribute 'CoreFoundation' missing
713 # nix-repl> darwin.callPackage ({ CoreFoundation }: CoreFoundation) { }
714 # «derivation ...CoreFoundation-11.0.0.drv»
715 # ```
716 extra ? (_spliced0: { }),
717 f,
718 }:
719 let
720 spliced0 = splicePackages {
721 pkgsBuildBuild = otherSplices.selfBuildBuild;
722 pkgsBuildHost = otherSplices.selfBuildHost;
723 pkgsBuildTarget = otherSplices.selfBuildTarget;
724 pkgsHostHost = otherSplices.selfHostHost;
725 pkgsHostTarget = self; # Not `otherSplices.selfHostTarget`;
726 pkgsTargetTarget = otherSplices.selfTargetTarget;
727 };
728 spliced = extra spliced0 // spliced0 // keep self;
729 self = f self // {
730 newScope = scope: newScope (spliced // scope);
731 callPackage = newScope spliced; # == self.newScope {};
732 # N.B. the other stages of the package set spliced in are *not*
733 # overridden.
734 overrideScope =
735 g:
736 (makeScopeWithSplicing' { inherit splicePackages newScope; } {
737 inherit otherSplices keep extra;
738 f = extends g f;
739 });
740 packages = f;
741 };
742 in
743 self;
744
745 /**
746 Define a `mkDerivation`-like function based on another `mkDerivation`-like function.
747
748 [`stdenv.mkDerivation`](#part-stdenv) gives access to
749 its final set of derivation attributes when it is passed a function,
750 or when it is passed an overlay-style function in `overrideAttrs`.
751
752 Instead of composing new `stdenv.mkDerivation`-like build helpers
753 using normal function composition,
754 `extendMkDerivation` makes sure that the returned build helper
755 supports such first class recursion like `mkDerivation` does.
756
757 `extendMkDerivation` takes an extra attribute set to configure its behaviour.
758 One can optionally specify
759 `transformDrv` to specify a function to apply to the result derivation,
760 or `inheritFunctionArgs` to decide whether to inherit the `__functionArgs`
761 from the base build helper.
762
763 # Inputs
764
765 `extendMkDerivation`-specific configurations
766 : `constructDrv`: Base build helper, the `mkDerivation`-like build helper to extend.
767 : `excludeDrvArgNames`: Argument names not to pass from the input fixed-point arguments to `constructDrv`. Note: It doesn't apply to the updating arguments returned by `extendDrvArgs`.
768 : `extendDrvArgs` : An extension (overlay) of the argument set, like the one taken by [overrideAttrs](#sec-pkg-overrideAttrs) but applied before passing to `constructDrv`.
769 : `inheritFunctionArgs`: Whether to inherit `__functionArgs` from the base build helper (default to `true`).
770 : `transformDrv`: Function to apply to the result derivation (default to `lib.id`).
771
772 # Type
773
774 ```
775 extendMkDerivation ::
776 {
777 constructDrv :: ((FixedPointArgs | AttrSet) -> a)
778 excludeDrvArgNames :: [ String ],
779 extendDrvArgs :: (AttrSet -> AttrSet -> AttrSet)
780 inheritFunctionArgs :: Bool,
781 transformDrv :: a -> a,
782 }
783 -> (FixedPointArgs | AttrSet) -> a
784
785 FixedPointArgs = AttrSet -> AttrSet
786 a = Derivation when defining a build helper
787 ```
788
789 # Examples
790
791 :::{.example}
792 ## `lib.customisation.extendMkDerivation` usage example
793 ```nix-repl
794 mkLocalDerivation = lib.extendMkDerivation {
795 constructDrv = pkgs.stdenv.mkDerivation;
796 excludeDrvArgNames = [ "specialArg" ];
797 extendDrvArgs =
798 finalAttrs: args@{ preferLocalBuild ? true, allowSubstitute ? false, specialArg ? (_: false), ... }:
799 { inherit preferLocalBuild allowSubstitute; passthru = { inherit specialArg; } // args.passthru or { }; };
800 }
801
802 mkLocalDerivation.__functionArgs
803 => { allowSubstitute = true; preferLocalBuild = true; specialArg = true; }
804
805 mkLocalDerivation { inherit (pkgs.hello) pname version src; specialArg = _: false; }
806 => «derivation /nix/store/xirl67m60ahg6jmzicx43a81g635g8z8-hello-2.12.1.drv»
807
808 mkLocalDerivation (finalAttrs: { inherit (pkgs.hello) pname version src; specialArg = _: false; })
809 => «derivation /nix/store/xirl67m60ahg6jmzicx43a81g635g8z8-hello-2.12.1.drv»
810
811 (mkLocalDerivation (finalAttrs: { inherit (pkgs.hello) pname version src; passthru = { foo = "a"; bar = "${finalAttrs.passthru.foo}b"; }; })).bar
812 => "ab"
813 ```
814 :::
815
816 :::{.note}
817 If `transformDrv` is specified,
818 it should take care of existing attributes that perform overriding
819 (e.g., [`overrideAttrs`](#sec-pkg-overrideAttrs))
820 to ensure that the overriding functionality of the result derivation
821 work as expected.
822 Modifications that breaks the overriding include
823 direct [attribute set update](https://nixos.org/manual/nix/stable/language/operators#update)
824 and [`lib.extendDerivation`](#function-library-lib.customisation.extendDerivation).
825 :::
826 */
827 extendMkDerivation =
828 let
829 extendsWithExclusion =
830 excludedNames: g: f: final:
831 let
832 previous = f final;
833 in
834 removeAttrs previous excludedNames // g final previous;
835 in
836 {
837 constructDrv,
838 excludeDrvArgNames ? [ ],
839 extendDrvArgs,
840 inheritFunctionArgs ? true,
841 transformDrv ? id,
842 }:
843 setFunctionArgs
844 # Adds the fixed-point style support
845 (
846 fpargs:
847 transformDrv (
848 constructDrv (extendsWithExclusion excludeDrvArgNames extendDrvArgs (toFunction fpargs))
849 )
850 )
851 # Add __functionArgs
852 (
853 # Inherit the __functionArgs from the base build helper
854 optionalAttrs inheritFunctionArgs (removeAttrs (functionArgs constructDrv) excludeDrvArgNames)
855 # Recover the __functionArgs from the derived build helper
856 // functionArgs (extendDrvArgs { })
857 )
858 // {
859 inherit
860 # Expose to the result build helper.
861 constructDrv
862 excludeDrvArgNames
863 extendDrvArgs
864 transformDrv
865 ;
866 };
867}