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