From 7903613b0bcdd3e7182f672b98aed9d1964fa9c2 Mon Sep 17 00:00:00 2001 From: adisbladis Date: Mon, 27 Nov 2023 12:14:54 +1300 Subject: [PATCH 1/2] lib.customisation: Inherit lib/builtins into scope It makes the code more readable if we have less nested attrsets being accessed. --- lib/customisation.nix | 89 ++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/lib/customisation.nix b/lib/customisation.nix index 08fc5db0614d..87d08c32765b 100644 --- a/lib/customisation.nix +++ b/lib/customisation.nix @@ -1,5 +1,16 @@ { lib }: +let + inherit (builtins) + intersectAttrs; + inherit (lib) + functionArgs isFunction mirrorFunctionArgs isAttrs setFunctionArgs levenshteinAtMost + optionalAttrs attrNames levenshtein filter elemAt concatStringsSep sort take length + filterAttrs optionalString flip pathIsDirectory head pipe isDerivation listToAttrs + mapAttrs seq flatten deepSeq warnIf isInOldestRelease extends + ; + +in rec { @@ -43,15 +54,15 @@ rec { overrideDerivation = drv: f: let newDrv = derivation (drv.drvAttrs // (f drv)); - in lib.flip (extendDerivation (builtins.seq drv.drvPath true)) newDrv ( + in flip (extendDerivation (seq drv.drvPath true)) newDrv ( { meta = drv.meta or {}; passthru = if drv ? passthru then drv.passthru else {}; } // (drv.passthru or {}) // - lib.optionalAttrs (drv ? __spliced) { - __spliced = {} // (lib.mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced); + optionalAttrs (drv ? __spliced) { + __spliced = {} // (mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced); }); @@ -79,30 +90,30 @@ rec { makeOverridable = f: let # Creates a functor with the same arguments as f - mirrorArgs = lib.mirrorFunctionArgs 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 lib.isFunction newArgs then newArgs origArgs else newArgs); + 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 builtins.isAttrs result then + if 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 + else if isFunction result then # Transform the result into a functor while propagating its arguments - lib.setFunctionArgs result (lib.functionArgs result) // { + setFunctionArgs result (functionArgs result) // { override = overrideArgs; } else result); @@ -140,39 +151,39 @@ rec { */ callPackageWith = autoArgs: fn: args: let - f = if lib.isFunction fn then fn else import fn; - fargs = lib.functionArgs f; + f = if isFunction fn then fn else import fn; + fargs = functionArgs f; # All arguments that will be passed to the function # This includes automatic ones and ones passed explicitly - allArgs = builtins.intersectAttrs fargs autoArgs // args; + allArgs = intersectAttrs fargs autoArgs // args; # a list of argument names that the function requires, but # wouldn't be passed to it - missingArgs = lib.attrNames + missingArgs = attrNames # Filter out arguments that have a default value - (lib.filterAttrs (name: value: ! value) + (filterAttrs (name: value: ! value) # Filter out arguments that would be passed - (removeAttrs fargs (lib.attrNames allArgs))); + (removeAttrs fargs (attrNames allArgs))); # Get a list of suggested argument names for a given missing one - getSuggestions = arg: lib.pipe (autoArgs // args) [ - lib.attrNames + 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. - (lib.filter (lib.strings.levenshteinAtMost 2 arg)) + (filter (levenshteinAtMost 2 arg)) # Put strings with shorter distance first - (lib.sort (x: y: lib.strings.levenshtein x arg < lib.strings.levenshtein y arg)) + (sort (x: y: levenshtein x arg < levenshtein y arg)) # Only take the first couple results - (lib.take 3) + (take 3) # Quote all entries (map (x: "\"" + x + "\"")) ]; prettySuggestions = suggestions: if suggestions == [] then "" - else if lib.length suggestions == 1 then ", did you mean ${lib.elemAt suggestions 0}?" - else ", did you mean ${lib.concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?"; + 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 @@ -180,14 +191,14 @@ rec { # 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 ! lib.isFunction fn then - toString fn + lib.optionalString (lib.sources.pathIsDirectory fn) "/default.nix" + else if ! isFunction fn then + toString fn + optionalString (pathIsDirectory fn) "/default.nix" else ""; in "Function called without required argument \"${arg}\" at " + "${loc'}${prettySuggestions (getSuggestions arg)}"; # Only show the error for the first missing argument - error = errorForArg (lib.head missingArgs); + error = errorForArg (head missingArgs); in if missingArgs == [] then makeOverridable f allArgs else abort error; @@ -201,17 +212,17 @@ rec { */ callPackagesWith = autoArgs: fn: args: let - f = if lib.isFunction fn then fn else import fn; - auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs; + f = if isFunction fn then fn else import fn; + auto = intersectAttrs (functionArgs f) autoArgs; origArgs = auto // args; pkgs = f origArgs; mkAttrOverridable = name: _: makeOverridable (newArgs: (f newArgs).${name}) origArgs; in - if lib.isDerivation pkgs then throw + if isDerivation pkgs then throw ("function `callPackages` was called on a *single* derivation " + ''"${pkgs.name or ""}";'' + " did you mean to use `callPackage` instead?") - else lib.mapAttrs mkAttrOverridable pkgs; + else mapAttrs mkAttrOverridable pkgs; /* Add attributes to each output of a derivation without changing @@ -224,7 +235,7 @@ rec { let outputs = drv.outputs or [ "out" ]; - commonAttrs = drv // (builtins.listToAttrs outputsList) // + commonAttrs = drv // (listToAttrs outputsList) // ({ all = map (x: x.value) outputsList; }) // passthru; outputToAttrListElement = outputName: @@ -238,7 +249,7 @@ rec { # TODO: give the derivation control over the outputs. # `overrideAttrs` may not be the only attribute that needs # updating when switching outputs. - lib.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}; }; @@ -264,11 +275,11 @@ rec { commonAttrs = { inherit (drv) name system meta; inherit outputs; } - // lib.optionalAttrs (drv._hydraAggregate or false) { + // optionalAttrs (drv._hydraAggregate or false) { _hydraAggregate = true; - constituents = map hydraJob (lib.flatten drv.constituents); + constituents = map hydraJob (flatten drv.constituents); } - // (lib.listToAttrs outputsList); + // (listToAttrs outputsList); makeOutput = outputName: let output = drv.${outputName}; in @@ -283,9 +294,9 @@ rec { outputsList = map makeOutput outputs; - drv' = (lib.head outputsList).value; + drv' = (head outputsList).value; in if drv == null then null else - lib.deepSeq drv' drv'; + 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 @@ -304,11 +315,11 @@ rec { let self = f self // { newScope = scope: newScope (self // scope); callPackage = self.newScope {}; - overrideScope = g: makeScope newScope (lib.fixedPoints.extends g f); + overrideScope = g: makeScope newScope (extends g f); # Remove after 24.11 is released. - overrideScope' = g: lib.warnIf (lib.isInOldestRelease 2311) + overrideScope' = g: warnIf (isInOldestRelease 2311) "`overrideScope'` (from `lib.makeScope`) has been renamed to `overrideScope`." - (makeScope newScope (lib.fixedPoints.extends g f)); + (makeScope newScope (extends g f)); packages = f; }; in self; @@ -384,7 +395,7 @@ rec { overrideScope = g: (makeScopeWithSplicing' { inherit splicePackages newScope; } { inherit otherSplices keep extra; - f = lib.fixedPoints.extends g f; + f = extends g f; }); packages = f; }; From 8d162ec7b8c87525af611f163e424ea9b09f7fa0 Mon Sep 17 00:00:00 2001 From: adisbladis Date: Mon, 27 Nov 2023 12:28:52 +1300 Subject: [PATCH 2/2] lib.customisation: Don't allocate intermediate list for missing args --- lib/customisation.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/customisation.nix b/lib/customisation.nix index 87d08c32765b..dec1ab9f4faa 100644 --- a/lib/customisation.nix +++ b/lib/customisation.nix @@ -160,7 +160,7 @@ rec { # a list of argument names that the function requires, but # wouldn't be passed to it - missingArgs = attrNames + missingArgs = # Filter out arguments that have a default value (filterAttrs (name: value: ! value) # Filter out arguments that would be passed @@ -198,9 +198,9 @@ rec { + "${loc'}${prettySuggestions (getSuggestions arg)}"; # Only show the error for the first missing argument - error = errorForArg (head missingArgs); + error = errorForArg missingArgs.${head (attrNames missingArgs)}; - in if missingArgs == [] then makeOverridable f allArgs else abort error; + in if missingArgs == {} then makeOverridable f allArgs else abort error; /* Like callPackage, but for a function that returns an attribute