at master 26 kB view raw
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}