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