python310Packages.pydeconz: 104 -> 105
[NixPkgs.git] / lib / customisation.nix
blobcc9a9b1c55d0af93b4c96281986942873ea23731
1 { lib }:
3 rec {
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.
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:
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        });
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   overrideDerivation = drv: f:
32     let
33       newDrv = derivation (drv.drvAttrs // (f drv));
34     in lib.flip (extendDerivation true) newDrv (
35       { meta = drv.meta or {};
36         passthru = if drv ? passthru then drv.passthru else {};
37       }
38       //
39       (drv.passthru or {})
40       //
41       (if (drv ? crossDrv && drv ? nativeDrv)
42        then {
43          crossDrv = overrideDerivation drv.crossDrv f;
44          nativeDrv = overrideDerivation drv.nativeDrv f;
45        }
46        else { }));
49   /* `makeOverridable` takes a function from attribute set to attribute set and
50      injects `override` attribute which can be used to override arguments of
51      the function.
53        nix-repl> x = {a, b}: { result = a + b; }
55        nix-repl> y = lib.makeOverridable x { a = 1; b = 2; }
57        nix-repl> y
58        { override = «lambda»; overrideDerivation = «lambda»; result = 3; }
60        nix-repl> y.override { a = 10; }
61        { override = «lambda»; overrideDerivation = «lambda»; result = 12; }
63      Please refer to "Nixpkgs Contributors Guide" section
64      "<pkg>.overrideDerivation" to learn about `overrideDerivation` and caveats
65      related to its use.
66   */
67   makeOverridable = f: origArgs:
68     let
69       result = f origArgs;
71       # Creates a functor with the same arguments as f
72       copyArgs = g: lib.setFunctionArgs g (lib.functionArgs f);
73       # Changes the original arguments with (potentially a function that returns) a set of new attributes
74       overrideWith = newArgs: origArgs // (if lib.isFunction newArgs then newArgs origArgs else newArgs);
76       # Re-call the function but with different arguments
77       overrideArgs = copyArgs (newArgs: makeOverridable f (overrideWith newArgs));
78       # Change the result of the function call by applying g to it
79       overrideResult = g: makeOverridable (copyArgs (args: g (f args))) origArgs;
80     in
81       if builtins.isAttrs result then
82         result // {
83           override = overrideArgs;
84           overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
85           ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
86             overrideResult (x: x.overrideAttrs fdrv);
87         }
88       else if lib.isFunction result then
89         # Transform the result into a functor while propagating its arguments
90         lib.setFunctionArgs result (lib.functionArgs result) // {
91           override = overrideArgs;
92         }
93       else result;
96   /* Call the package function in the file `fn' with the required
97     arguments automatically.  The function is called with the
98     arguments `args', but any missing arguments are obtained from
99     `autoArgs'.  This function is intended to be partially
100     parameterised, e.g.,
102       callPackage = callPackageWith pkgs;
103       pkgs = {
104         libfoo = callPackage ./foo.nix { };
105         libbar = callPackage ./bar.nix { };
106       };
108     If the `libbar' function expects an argument named `libfoo', it is
109     automatically passed as an argument.  Overrides or missing
110     arguments can be supplied in `args', e.g.
112       libbar = callPackage ./bar.nix {
113         libfoo = null;
114         enableX11 = true;
115       };
116   */
117   callPackageWith = autoArgs: fn: args:
118     let
119       f = if lib.isFunction fn then fn else import fn;
120       fargs = lib.functionArgs f;
122       # All arguments that will be passed to the function
123       # This includes automatic ones and ones passed explicitly
124       allArgs = builtins.intersectAttrs fargs autoArgs // args;
126       # A list of argument names that the function requires, but
127       # wouldn't be passed to it
128       missingArgs = lib.attrNames
129         # Filter out arguments that have a default value
130         (lib.filterAttrs (name: value: ! value)
131         # Filter out arguments that would be passed
132         (removeAttrs fargs (lib.attrNames allArgs)));
134       # Get a list of suggested argument names for a given missing one
135       getSuggestions = arg: lib.pipe (autoArgs // args) [
136         lib.attrNames
137         # Only use ones that are at most 2 edits away. While mork would work,
138         # levenshteinAtMost is only fast for 2 or less.
139         (lib.filter (lib.strings.levenshteinAtMost 2 arg))
140         # Put strings with shorter distance first
141         (lib.sort (x: y: lib.strings.levenshtein x arg < lib.strings.levenshtein y arg))
142         # Only take the first couple results
143         (lib.take 3)
144         # Quote all entries
145         (map (x: "\"" + x + "\""))
146       ];
148       prettySuggestions = suggestions:
149         if suggestions == [] then ""
150         else if lib.length suggestions == 1 then ", did you mean ${lib.elemAt suggestions 0}?"
151         else ", did you mean ${lib.concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?";
153       errorForArg = arg:
154         let
155           loc = builtins.unsafeGetAttrPos arg fargs;
156           # loc' can be removed once lib/minver.nix is >2.3.4, since that includes
157           # https://github.com/NixOS/nix/pull/3468 which makes loc be non-null
158           loc' = if loc != null then loc.file + ":" + toString loc.line
159             else if ! lib.isFunction fn then
160               toString fn + lib.optionalString (lib.sources.pathIsDirectory fn) "/default.nix"
161             else "<unknown location>";
162         in "Function called without required argument \"${arg}\" at "
163         + "${loc'}${prettySuggestions (getSuggestions arg)}";
165       # Only show the error for the first missing argument
166       error = errorForArg (lib.head missingArgs);
168     in if missingArgs == [] then makeOverridable f allArgs else throw error;
171   /* Like callPackage, but for a function that returns an attribute
172      set of derivations. The override function is added to the
173      individual attributes. */
174   callPackagesWith = autoArgs: fn: args:
175     let
176       f = if lib.isFunction fn then fn else import fn;
177       auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs;
178       origArgs = auto // args;
179       pkgs = f origArgs;
180       mkAttrOverridable = name: _: makeOverridable (newArgs: (f newArgs).${name}) origArgs;
181     in
182       if lib.isDerivation pkgs then throw
183         ("function `callPackages` was called on a *single* derivation "
184           + ''"${pkgs.name or "<unknown-name>"}";''
185           + " did you mean to use `callPackage` instead?")
186       else lib.mapAttrs mkAttrOverridable pkgs;
189   /* Add attributes to each output of a derivation without changing
190      the derivation itself and check a given condition when evaluating. */
191   extendDerivation = condition: passthru: drv:
192     let
193       outputs = drv.outputs or [ "out" ];
195       commonAttrs = drv // (builtins.listToAttrs outputsList) //
196         ({ all = map (x: x.value) outputsList; }) // passthru;
198       outputToAttrListElement = outputName:
199         { name = outputName;
200           value = commonAttrs // {
201             inherit (drv.${outputName}) type outputName;
202             outputSpecified = true;
203             drvPath = assert condition; drv.${outputName}.drvPath;
204             outPath = assert condition; drv.${outputName}.outPath;
205           };
206         };
208       outputsList = map outputToAttrListElement outputs;
209     in commonAttrs // {
210       drvPath = assert condition; drv.drvPath;
211       outPath = assert condition; drv.outPath;
212     };
214   /* Strip a derivation of all non-essential attributes, returning
215      only those needed by hydra-eval-jobs. Also strictly evaluate the
216      result to ensure that there are no thunks kept alive to prevent
217      garbage collection. */
218   hydraJob = drv:
219     let
220       outputs = drv.outputs or ["out"];
222       commonAttrs =
223         { inherit (drv) name system meta; inherit outputs; }
224         // lib.optionalAttrs (drv._hydraAggregate or false) {
225           _hydraAggregate = true;
226           constituents = map hydraJob (lib.flatten drv.constituents);
227         }
228         // (lib.listToAttrs outputsList);
230       makeOutput = outputName:
231         let output = drv.${outputName}; in
232         { name = outputName;
233           value = commonAttrs // {
234             outPath = output.outPath;
235             drvPath = output.drvPath;
236             type = "derivation";
237             inherit outputName;
238           };
239         };
241       outputsList = map makeOutput outputs;
243       drv' = (lib.head outputsList).value;
244     in lib.deepSeq drv' drv';
246   /* Make a set of packages with a common scope. All packages called
247      with the provided `callPackage' will be evaluated with the same
248      arguments. Any package in the set may depend on any other. The
249      `overrideScope'` function allows subsequent modification of the package
250      set in a consistent way, i.e. all packages in the set will be
251      called with the overridden packages. The package sets may be
252      hierarchical: the packages in the set are called with the scope
253      provided by `newScope' and the set provides a `newScope' attribute
254      which can form the parent scope for later package sets. */
255   makeScope = newScope: f:
256     let self = f self // {
257           newScope = scope: newScope (self // scope);
258           callPackage = self.newScope {};
259           overrideScope = g: lib.warn
260             "`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."
261             (makeScope newScope (lib.fixedPoints.extends (lib.flip g) f));
262           overrideScope' = g: makeScope newScope (lib.fixedPoints.extends g f);
263           packages = f;
264         };
265     in self;
267   /* Like the above, but aims to support cross compilation. It's still ugly, but
268      hopefully it helps a little bit. */
269   makeScopeWithSplicing = splicePackages: newScope: otherSplices: keep: extra: f:
270     let
271       spliced0 = splicePackages {
272         pkgsBuildBuild = otherSplices.selfBuildBuild;
273         pkgsBuildHost = otherSplices.selfBuildHost;
274         pkgsBuildTarget = otherSplices.selfBuildTarget;
275         pkgsHostHost = otherSplices.selfHostHost;
276         pkgsHostTarget = self; # Not `otherSplices.selfHostTarget`;
277         pkgsTargetTarget = otherSplices.selfTargetTarget;
278       };
279       spliced = extra spliced0 // spliced0 // keep self;
280       self = f self // {
281         newScope = scope: newScope (spliced // scope);
282         callPackage = newScope spliced; # == self.newScope {};
283         # N.B. the other stages of the package set spliced in are *not*
284         # overridden.
285         overrideScope = g: makeScopeWithSplicing
286           splicePackages
287           newScope
288           otherSplices
289           keep
290           extra
291           (lib.fixedPoints.extends g f);
292         packages = f;
293       };
294     in self;