diff options
Diffstat (limited to 'lib/customisation.nix')
-rw-r--r-- | lib/customisation.nix | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/lib/customisation.nix b/lib/customisation.nix new file mode 100644 index 00000000000..234a528527d --- /dev/null +++ b/lib/customisation.nix @@ -0,0 +1,249 @@ +{ lib }: + +rec { + + + /* `overrideDerivation drv f' takes a derivation (i.e., the result + of a call to the builtin function `derivation') and returns a new + derivation in which the attributes of the original are overridden + according to the function `f'. The function `f' is called with + the original derivation attributes. + + `overrideDerivation' allows certain "ad-hoc" customisation + scenarios (e.g. in ~/.config/nixpkgs/config.nix). For instance, + if you want to "patch" the derivation returned by a package + function in Nixpkgs to build another version than what the + function itself provides, you can do something like this: + + mySed = overrideDerivation pkgs.gnused (oldAttrs: { + name = "sed-4.2.2-pre"; + src = fetchurl { + url = ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2; + sha256 = "11nq06d131y4wmf3drm0yk502d2xc6n5qy82cg88rb9nqd2lj41k"; + }; + patches = []; + }); + + For another application, see build-support/vm, where this + function is used to build arbitrary derivations inside a QEMU + virtual machine. + */ + overrideDerivation = drv: f: + let + newDrv = derivation (drv.drvAttrs // (f drv)); + in lib.flip (extendDerivation true) newDrv ( + { meta = drv.meta or {}; + passthru = if drv ? passthru then drv.passthru else {}; + } + // + (drv.passthru or {}) + // + (if (drv ? crossDrv && drv ? nativeDrv) + then { + crossDrv = overrideDerivation drv.crossDrv f; + nativeDrv = overrideDerivation drv.nativeDrv f; + } + else { })); + + + /* `makeOverridable` takes a function from attribute set to attribute set and + injects `override` attribute which can be used to override arguments of + the function. + + nix-repl> x = {a, b}: { result = a + b; } + + nix-repl> y = lib.makeOverridable x { a = 1; b = 2; } + + nix-repl> y + { override = «lambda»; overrideDerivation = «lambda»; result = 3; } + + nix-repl> y.override { a = 10; } + { override = «lambda»; overrideDerivation = «lambda»; result = 12; } + + Please refer to "Nixpkgs Contributors Guide" section + "<pkg>.overrideDerivation" to learn about `overrideDerivation` and caveats + related to its use. + */ + makeOverridable = f: origArgs: + let + result = f origArgs; + + # Creates a functor with the same arguments as f + copyArgs = g: lib.setFunctionArgs g (lib.functionArgs f); + # Changes the original arguments with (potentially a function that returns) a set of new attributes + overrideWith = newArgs: origArgs // (if lib.isFunction newArgs then newArgs origArgs else newArgs); + + # Re-call the function but with different arguments + overrideArgs = copyArgs (newArgs: makeOverridable f (overrideWith newArgs)); + # Change the result of the function call by applying g to it + overrideResult = g: makeOverridable (copyArgs (args: g (f args))) origArgs; + in + if builtins.isAttrs result then + result // { + override = overrideArgs; + overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv); + ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv: + overrideResult (x: x.overrideAttrs fdrv); + } + else if lib.isFunction result then + # Transform the result into a functor while propagating its arguments + lib.setFunctionArgs result (lib.functionArgs result) // { + override = overrideArgs; + } + else result; + + + /* Call the package function in the file `fn' with the required + arguments automatically. The function is called with the + arguments `args', but any missing arguments are obtained from + `autoArgs'. This function is intended to be partially + parameterised, e.g., + + callPackage = callPackageWith pkgs; + pkgs = { + libfoo = callPackage ./foo.nix { }; + libbar = callPackage ./bar.nix { }; + }; + + If the `libbar' function expects an argument named `libfoo', it is + automatically passed as an argument. Overrides or missing + arguments can be supplied in `args', e.g. + + libbar = callPackage ./bar.nix { + libfoo = null; + enableX11 = true; + }; + */ + callPackageWith = autoArgs: fn: args: + let + f = if lib.isFunction fn then fn else import fn; + auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs; + in makeOverridable f (auto // args); + + + /* Like callPackage, but for a function that returns an attribute + set of derivations. The override function is added to the + individual attributes. */ + callPackagesWith = autoArgs: fn: args: + let + f = if lib.isFunction fn then fn else import fn; + auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs; + origArgs = auto // args; + pkgs = f origArgs; + mkAttrOverridable = name: _: makeOverridable (newArgs: (f newArgs).${name}) origArgs; + in + if lib.isDerivation pkgs then throw + ("function `callPackages` was called on a *single* derivation " + + ''"${pkgs.name or "<unknown-name>"}";'' + + " did you mean to use `callPackage` instead?") + else lib.mapAttrs mkAttrOverridable pkgs; + + + /* Add attributes to each output of a derivation without changing + the derivation itself and check a given condition when evaluating. */ + extendDerivation = condition: passthru: drv: + let + outputs = drv.outputs or [ "out" ]; + + commonAttrs = drv // (builtins.listToAttrs outputsList) // + ({ all = map (x: x.value) outputsList; }) // passthru; + + outputToAttrListElement = outputName: + { name = outputName; + value = commonAttrs // { + inherit (drv.${outputName}) type outputName; + outputSpecified = true; + drvPath = assert condition; drv.${outputName}.drvPath; + outPath = assert condition; drv.${outputName}.outPath; + }; + }; + + outputsList = map outputToAttrListElement outputs; + in commonAttrs // { + drvPath = assert condition; drv.drvPath; + outPath = assert condition; drv.outPath; + }; + + /* Strip a derivation of all non-essential attributes, returning + only those needed by hydra-eval-jobs. Also strictly evaluate the + result to ensure that there are no thunks kept alive to prevent + garbage collection. */ + hydraJob = drv: + let + outputs = drv.outputs or ["out"]; + + commonAttrs = + { inherit (drv) name system meta; inherit outputs; } + // lib.optionalAttrs (drv._hydraAggregate or false) { + _hydraAggregate = true; + constituents = map hydraJob (lib.flatten drv.constituents); + } + // (lib.listToAttrs outputsList); + + makeOutput = outputName: + let output = drv.${outputName}; in + { name = outputName; + value = commonAttrs // { + outPath = output.outPath; + drvPath = output.drvPath; + type = "derivation"; + inherit outputName; + }; + }; + + outputsList = map makeOutput outputs; + + drv' = (lib.head outputsList).value; + in lib.deepSeq drv' drv'; + + /* Make a set of packages with a common scope. All packages called + with the provided `callPackage' will be evaluated with the same + arguments. Any package in the set may depend on any other. The + `overrideScope'` function allows subsequent modification of the package + set in a consistent way, i.e. all packages in the set will be + called with the overridden packages. The package sets may be + hierarchical: the packages in the set are called with the scope + provided by `newScope' and the set provides a `newScope' attribute + which can form the parent scope for later package sets. */ + makeScope = newScope: f: + let self = f self // { + newScope = scope: newScope (self // scope); + callPackage = self.newScope {}; + overrideScope = g: lib.warn + "`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." + (makeScope newScope (lib.fixedPoints.extends (lib.flip g) f)); + overrideScope' = g: makeScope newScope (lib.fixedPoints.extends g f); + packages = f; + }; + in self; + + /* Like the above, but aims to support cross compilation. It's still ugly, but + hopefully it helps a little bit. */ + makeScopeWithSplicing = splicePackages: newScope: otherSplices: keep: extra: f: + let + spliced0 = splicePackages { + pkgsBuildBuild = otherSplices.selfBuildBuild; + pkgsBuildHost = otherSplices.selfBuildHost; + pkgsBuildTarget = otherSplices.selfBuildTarget; + pkgsHostHost = otherSplices.selfHostHost; + pkgsHostTarget = self; # Not `otherSplices.selfHostTarget`; + pkgsTargetTarget = otherSplices.selfTargetTarget; + }; + spliced = extra spliced0 // spliced0 // keep self; + self = f self // { + newScope = scope: newScope (spliced // scope); + callPackage = newScope spliced; # == self.newScope {}; + # N.B. the other stages of the package set spliced in are *not* + # overridden. + overrideScope = g: makeScopeWithSplicing + splicePackages + newScope + otherSplices + keep + extra + (lib.fixedPoints.extends g f); + packages = f; + }; + in self; + +} |