diff --git a/lib/customisation.nix b/lib/customisation.nix index 99c02ddeea89a..be6585d5fa6e4 100644 --- a/lib/customisation.nix +++ b/lib/customisation.nix @@ -2,19 +2,41 @@ let inherit (builtins) - intersectAttrs; + intersectAttrs + ; inherit (lib) - functionArgs isFunction mirrorFunctionArgs isAttrs setFunctionArgs - optionalAttrs attrNames filter elemAt concatStringsSep sortOn take length - filterAttrs optionalString flip pathIsDirectory head pipe isDerivation listToAttrs - mapAttrs seq flatten deepSeq warnIf isInOldestRelease extends + functionArgs + isFunction + mirrorFunctionArgs + isAttrs + setFunctionArgs + optionalAttrs + attrNames + filter + elemAt + concatStringsSep + sortOn + take + length + filterAttrs + optionalString + flip + pathIsDirectory + head + pipe + isDerivation + listToAttrs + mapAttrs + seq + flatten + deepSeq + extends ; inherit (lib.strings) levenshtein levenshteinAtMost; in rec { - /** `overrideDerivation drv f` takes a derivation (i.e., the result of a call to the builtin function `derivation`) and returns a new @@ -40,7 +62,6 @@ rec { You should in general prefer `drv.overrideAttrs` over this function; see the nixpkgs manual for more information on overriding. - # Inputs `drv` @@ -74,20 +95,21 @@ rec { ::: */ - overrideDerivation = drv: f: + overrideDerivation = + drv: f: let newDrv = derivation (drv.drvAttrs // (f drv)); - in flip (extendDerivation (seq drv.drvPath true)) newDrv ( - { meta = drv.meta or {}; - passthru = if drv ? passthru then drv.passthru else {}; + in + flip (extendDerivation (seq drv.drvPath true)) newDrv ( + { + meta = drv.meta or { }; + passthru = if drv ? passthru then drv.passthru else { }; } - // - (drv.passthru or {}) - // - optionalAttrs (drv ? __spliced) { - __spliced = {} // (mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced); - }); - + // (drv.passthru or { }) + // optionalAttrs (drv ? __spliced) { + __spliced = { } // (mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced); + } + ); /** `makeOverridable` takes a function from attribute set to attribute set and @@ -97,7 +119,6 @@ rec { Please refer to documentation on [`.overrideDerivation`](#sec-pkg-overrideDerivation) to learn about `overrideDerivation` and caveats related to its use. - # Inputs `f` @@ -128,37 +149,42 @@ rec { ::: */ - makeOverridable = f: + makeOverridable = + f: let # Creates a functor with the same arguments as f mirrorArgs = mirrorFunctionArgs f; in - mirrorArgs (origArgs: - let - result = f origArgs; - - # Changes the original arguments with (potentially a function that returns) a set of new attributes - overrideWith = newArgs: origArgs // (if isFunction newArgs then newArgs origArgs else newArgs); - - # Re-call the function but with different arguments - overrideArgs = mirrorArgs (newArgs: makeOverridable f (overrideWith newArgs)); - # Change the result of the function call by applying g to it - overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs; - in + mirrorArgs ( + origArgs: + let + result = f origArgs; + + # Changes the original arguments with (potentially a function that returns) a set of new attributes + overrideWith = newArgs: origArgs // (if isFunction newArgs then newArgs origArgs else newArgs); + + # Re-call the function but with different arguments + overrideArgs = mirrorArgs (newArgs: makeOverridable f (overrideWith newArgs)); + # Change the result of the function call by applying g to it + overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs; + in if isAttrs result then - result // { + result + // { override = overrideArgs; overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv); - ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv: - overrideResult (x: x.overrideAttrs fdrv); + ${if result ? overrideAttrs then "overrideAttrs" else null} = + fdrv: overrideResult (x: x.overrideAttrs fdrv); } else if isFunction result then # Transform the result into a functor while propagating its arguments - setFunctionArgs result (functionArgs result) // { + setFunctionArgs result (functionArgs result) + // { override = overrideArgs; } - else result); - + else + result + ); /** Call the package function in the file `fn` with the required @@ -188,7 +214,6 @@ rec { - # Inputs `autoArgs` @@ -209,7 +234,8 @@ rec { callPackageWith :: AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a ``` */ - callPackageWith = autoArgs: fn: args: + callPackageWith = + autoArgs: fn: args: let f = if isFunction fn then fn else import fn; fargs = functionArgs f; @@ -222,59 +248,72 @@ rec { # wouldn't be passed to it missingArgs = # Filter out arguments that have a default value - (filterAttrs (name: value: ! value) - # Filter out arguments that would be passed - (removeAttrs fargs (attrNames allArgs))); + ( + filterAttrs (name: value: !value) + # Filter out arguments that would be passed + (removeAttrs fargs (attrNames allArgs)) + ); # Get a list of suggested argument names for a given missing one - getSuggestions = arg: pipe (autoArgs // args) [ - attrNames - # Only use ones that are at most 2 edits away. While mork would work, - # levenshteinAtMost is only fast for 2 or less. - (filter (levenshteinAtMost 2 arg)) - # Put strings with shorter distance first - (sortOn (levenshtein arg)) - # Only take the first couple results - (take 3) - # Quote all entries - (map (x: "\"" + x + "\"")) - ]; - - prettySuggestions = suggestions: - if suggestions == [] then "" - else if length suggestions == 1 then ", did you mean ${elemAt suggestions 0}?" - else ", did you mean ${concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?"; - - errorForArg = arg: + getSuggestions = + arg: + pipe (autoArgs // args) [ + attrNames + # Only use ones that are at most 2 edits away. While mork would work, + # levenshteinAtMost is only fast for 2 or less. + (filter (levenshteinAtMost 2 arg)) + # Put strings with shorter distance first + (sortOn (levenshtein arg)) + # Only take the first couple results + (take 3) + # Quote all entries + (map (x: "\"" + x + "\"")) + ]; + + prettySuggestions = + suggestions: + if suggestions == [ ] then + "" + else if length suggestions == 1 then + ", did you mean ${elemAt suggestions 0}?" + else + ", did you mean ${concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?"; + + errorForArg = + arg: let loc = builtins.unsafeGetAttrPos arg fargs; # loc' can be removed once lib/minver.nix is >2.3.4, since that includes # https://github.com/NixOS/nix/pull/3468 which makes loc be non-null - loc' = if loc != null then loc.file + ":" + toString loc.line - else if ! isFunction fn then + loc' = + if loc != null then + loc.file + ":" + toString loc.line + else if !isFunction fn then toString fn + optionalString (pathIsDirectory fn) "/default.nix" - else ""; - in "Function called without required argument \"${arg}\" at " + else + ""; + in + "Function called without required argument \"${arg}\" at " + "${loc'}${prettySuggestions (getSuggestions arg)}"; # Only show the error for the first missing argument error = errorForArg (head (attrNames missingArgs)); - in if missingArgs == {} - then makeOverridable f allArgs - # This needs to be an abort so it can't be caught with `builtins.tryEval`, - # which is used by nix-env and ofborg to filter out packages that don't evaluate. - # This way we're forced to fix such errors in Nixpkgs, - # which is especially relevant with allowAliases = false - else abort "lib.customisation.callPackageWith: ${error}"; - + in + if missingArgs == { } then + makeOverridable f allArgs + # This needs to be an abort so it can't be caught with `builtins.tryEval`, + # which is used by nix-env and ofborg to filter out packages that don't evaluate. + # This way we're forced to fix such errors in Nixpkgs, + # which is especially relevant with allowAliases = false + else + abort "lib.customisation.callPackageWith: ${error}"; /** Like callPackage, but for a function that returns an attribute set of derivations. The override function is added to the individual attributes. - # Inputs `autoArgs` @@ -295,7 +334,8 @@ rec { callPackagesWith :: AttrSet -> ((AttrSet -> AttrSet) | Path) -> AttrSet -> AttrSet ``` */ - callPackagesWith = autoArgs: fn: args: + callPackagesWith = + autoArgs: fn: args: let f = if isFunction fn then fn else import fn; auto = intersectAttrs (functionArgs f) autoArgs; @@ -304,18 +344,19 @@ rec { pkgs = f origArgs; mkAttrOverridable = name: _: makeOverridable (mirrorArgs (newArgs: (f newArgs).${name})) origArgs; in - if isDerivation pkgs then throw - ("function `callPackages` was called on a *single* derivation " - + ''"${pkgs.name or ""}";'' - + " did you mean to use `callPackage` instead?") - else mapAttrs mkAttrOverridable pkgs; - + if isDerivation pkgs then + throw ( + "function `callPackages` was called on a *single* derivation " + + ''"${pkgs.name or ""}";'' + + " did you mean to use `callPackage` instead?" + ) + else + mapAttrs mkAttrOverridable pkgs; /** Add attributes to each output of a derivation without changing the derivation itself and check a given condition when evaluating. - # Inputs `condition` @@ -336,34 +377,48 @@ rec { extendDerivation :: Bool -> Any -> Derivation -> Derivation ``` */ - extendDerivation = condition: passthru: drv: + extendDerivation = + condition: passthru: drv: let outputs = drv.outputs or [ "out" ]; - commonAttrs = drv // (listToAttrs outputsList) // - ({ all = map (x: x.value) outputsList; }) // passthru; + commonAttrs = + drv // (listToAttrs outputsList) // ({ all = map (x: x.value) outputsList; }) // passthru; - outputToAttrListElement = outputName: - { name = outputName; - value = commonAttrs // { + 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; - } // + drvPath = + assert condition; + drv.${outputName}.drvPath; + outPath = + assert condition; + drv.${outputName}.outPath; + } + // # TODO: give the derivation control over the outputs. # `overrideAttrs` may not be the only attribute that needs # updating when switching outputs. - optionalAttrs (passthru?overrideAttrs) { + optionalAttrs (passthru ? overrideAttrs) { # TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing. overrideAttrs = f: (passthru.overrideAttrs f).${outputName}; }; - }; + }; outputsList = map outputToAttrListElement outputs; - in commonAttrs // { - drvPath = assert condition; drv.drvPath; - outPath = assert condition; drv.outPath; + in + commonAttrs + // { + drvPath = + assert condition; + drv.drvPath; + outPath = + assert condition; + drv.outPath; }; /** @@ -372,7 +427,6 @@ rec { result to ensure that there are no thunks kept alive to prevent garbage collection. - # Inputs `drv` @@ -385,21 +439,29 @@ rec { hydraJob :: (Derivation | Null) -> (Derivation | Null) ``` */ - hydraJob = drv: + hydraJob = + drv: let - outputs = drv.outputs or ["out"]; + outputs = drv.outputs or [ "out" ]; commonAttrs = - { inherit (drv) name system meta; inherit outputs; } + { + inherit (drv) name system meta; + inherit outputs; + } // optionalAttrs (drv._hydraAggregate or false) { _hydraAggregate = true; constituents = map hydraJob (flatten drv.constituents); } // (listToAttrs outputsList); - makeOutput = outputName: - let output = drv.${outputName}; in - { name = outputName; + makeOutput = + outputName: + let + output = drv.${outputName}; + in + { + name = outputName; value = commonAttrs // { outPath = output.outPath; drvPath = output.drvPath; @@ -411,8 +473,8 @@ rec { outputsList = map makeOutput outputs; drv' = (head outputsList).value; - in if drv == null then null else - deepSeq drv' drv'; + in + if drv == null then null else deepSeq drv' drv'; /** Make an attribute set (a "scope") from functions that take arguments from that same attribute set. @@ -538,19 +600,21 @@ rec { makeScope :: (AttrSet -> ((AttrSet -> a) | Path) -> AttrSet -> a) -> (AttrSet -> AttrSet) -> scope ``` */ - makeScope = newScope: f: - let self = f self // { - newScope = scope: newScope (self // scope); - callPackage = self.newScope {}; - overrideScope = g: makeScope newScope (extends g f); - packages = f; - }; - in self; + makeScope = + newScope: f: + let + self = f self // { + newScope = scope: newScope (self // scope); + callPackage = self.newScope { }; + overrideScope = g: makeScope newScope (extends g f); + packages = f; + }; + in + self; /** backward compatibility with old uncurried form; deprecated - # Inputs `splicePackages` @@ -579,9 +643,14 @@ rec { */ makeScopeWithSplicing = splicePackages: newScope: otherSplices: keep: extra: f: - makeScopeWithSplicing' - { inherit splicePackages newScope; } - { inherit otherSplices keep extra f; }; + makeScopeWithSplicing' { inherit splicePackages newScope; } { + inherit + otherSplices + keep + extra + f + ; + }; /** Like makeScope, but aims to support cross compilation. It's still ugly, but @@ -608,30 +677,32 @@ rec { ``` */ makeScopeWithSplicing' = - { splicePackages - , newScope + { + splicePackages, + newScope, }: - { otherSplices - # Attrs from `self` which won't be spliced. - # Avoid using keep, it's only used for a python hook workaround, added in PR #104201. - # ex: `keep = (self: { inherit (self) aAttr; })` - , keep ? (_self: {}) - # Additional attrs to add to the sets `callPackage`. - # When the package is from a subset (but not a subset within a package IS #211340) - # within `spliced0` it will be spliced. - # When using an package outside the set but it's available from `pkgs`, use the package from `pkgs.__splicedPackages`. - # 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 - # ex: - # ``` - # nix-repl> darwin.apple_sdk.frameworks.CoreFoundation - # «derivation ...CoreFoundation-11.0.0.drv» - # nix-repl> darwin.CoreFoundation - # error: attribute 'CoreFoundation' missing - # nix-repl> darwin.callPackage ({ CoreFoundation }: CoreFoundation) { } - # «derivation ...CoreFoundation-11.0.0.drv» - # ``` - , extra ? (_spliced0: {}) - , f + { + otherSplices, + # Attrs from `self` which won't be spliced. + # Avoid using keep, it's only used for a python hook workaround, added in PR #104201. + # ex: `keep = (self: { inherit (self) aAttr; })` + keep ? (_self: { }), + # Additional attrs to add to the sets `callPackage`. + # When the package is from a subset (but not a subset within a package IS #211340) + # within `spliced0` it will be spliced. + # When using an package outside the set but it's available from `pkgs`, use the package from `pkgs.__splicedPackages`. + # 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 + # ex: + # ``` + # nix-repl> darwin.apple_sdk.frameworks.CoreFoundation + # «derivation ...CoreFoundation-11.0.0.drv» + # nix-repl> darwin.CoreFoundation + # error: attribute 'CoreFoundation' missing + # nix-repl> darwin.callPackage ({ CoreFoundation }: CoreFoundation) { } + # «derivation ...CoreFoundation-11.0.0.drv» + # ``` + extra ? (_spliced0: { }), + f, }: let spliced0 = splicePackages { @@ -648,13 +719,15 @@ rec { callPackage = newScope spliced; # == self.newScope {}; # N.B. the other stages of the package set spliced in are *not* # overridden. - overrideScope = g: (makeScopeWithSplicing' - { inherit splicePackages newScope; } - { inherit otherSplices keep extra; + overrideScope = + g: + (makeScopeWithSplicing' { inherit splicePackages newScope; } { + inherit otherSplices keep extra; f = extends g f; }); packages = f; }; - in self; + in + self; }