0
0
Fork 0
mirror of https://github.com/NixOS/nixpkgs.git synced 2025-07-14 06:00:33 +03:00

Merge pull request #270299 from adisbladis/lib-customisation-allocs

lib.customisation: Refactor nested set access & avoid some allocations
This commit is contained in:
Silvan Mosberger 2023-11-27 16:01:05 +01:00 committed by GitHub
commit fc2b2af15e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,5 +1,16 @@
{ lib }: { 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 { rec {
@ -43,15 +54,15 @@ rec {
overrideDerivation = drv: f: overrideDerivation = drv: f:
let let
newDrv = derivation (drv.drvAttrs // (f drv)); 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 {}; { meta = drv.meta or {};
passthru = if drv ? passthru then drv.passthru else {}; passthru = if drv ? passthru then drv.passthru else {};
} }
// //
(drv.passthru or {}) (drv.passthru or {})
// //
lib.optionalAttrs (drv ? __spliced) { optionalAttrs (drv ? __spliced) {
__spliced = {} // (lib.mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced); __spliced = {} // (mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced);
}); });
@ -79,30 +90,30 @@ rec {
makeOverridable = f: makeOverridable = f:
let let
# Creates a functor with the same arguments as f # Creates a functor with the same arguments as f
mirrorArgs = lib.mirrorFunctionArgs f; mirrorArgs = mirrorFunctionArgs f;
in in
mirrorArgs (origArgs: mirrorArgs (origArgs:
let let
result = f origArgs; result = f origArgs;
# Changes the original arguments with (potentially a function that returns) a set of new attributes # 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 # Re-call the function but with different arguments
overrideArgs = mirrorArgs (newArgs: makeOverridable f (overrideWith newArgs)); overrideArgs = mirrorArgs (newArgs: makeOverridable f (overrideWith newArgs));
# Change the result of the function call by applying g to it # Change the result of the function call by applying g to it
overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs; overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs;
in in
if builtins.isAttrs result then if isAttrs result then
result // { result // {
override = overrideArgs; override = overrideArgs;
overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv); overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv: ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
overrideResult (x: x.overrideAttrs 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 # Transform the result into a functor while propagating its arguments
lib.setFunctionArgs result (lib.functionArgs result) // { setFunctionArgs result (functionArgs result) // {
override = overrideArgs; override = overrideArgs;
} }
else result); else result);
@ -140,39 +151,39 @@ rec {
*/ */
callPackageWith = autoArgs: fn: args: callPackageWith = autoArgs: fn: args:
let let
f = if lib.isFunction fn then fn else import fn; f = if isFunction fn then fn else import fn;
fargs = lib.functionArgs f; fargs = functionArgs f;
# All arguments that will be passed to the function # All arguments that will be passed to the function
# This includes automatic ones and ones passed explicitly # 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 # a list of argument names that the function requires, but
# wouldn't be passed to it # wouldn't be passed to it
missingArgs = lib.attrNames missingArgs =
# Filter out arguments that have a default value # Filter out arguments that have a default value
(lib.filterAttrs (name: value: ! value) (filterAttrs (name: value: ! value)
# Filter out arguments that would be passed # 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 # Get a list of suggested argument names for a given missing one
getSuggestions = arg: lib.pipe (autoArgs // args) [ getSuggestions = arg: pipe (autoArgs // args) [
lib.attrNames attrNames
# Only use ones that are at most 2 edits away. While mork would work, # Only use ones that are at most 2 edits away. While mork would work,
# levenshteinAtMost is only fast for 2 or less. # 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 # 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 # Only take the first couple results
(lib.take 3) (take 3)
# Quote all entries # Quote all entries
(map (x: "\"" + x + "\"")) (map (x: "\"" + x + "\""))
]; ];
prettySuggestions = suggestions: prettySuggestions = suggestions:
if suggestions == [] then "" if suggestions == [] then ""
else if lib.length suggestions == 1 then ", did you mean ${lib.elemAt suggestions 0}?" else if length suggestions == 1 then ", did you mean ${elemAt suggestions 0}?"
else ", did you mean ${lib.concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?"; else ", did you mean ${concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?";
errorForArg = arg: errorForArg = arg:
let let
@ -180,16 +191,16 @@ rec {
# loc' can be removed once lib/minver.nix is >2.3.4, since that includes # 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 # https://github.com/NixOS/nix/pull/3468 which makes loc be non-null
loc' = if loc != null then loc.file + ":" + toString loc.line loc' = if loc != null then loc.file + ":" + toString loc.line
else if ! lib.isFunction fn then else if ! isFunction fn then
toString fn + lib.optionalString (lib.sources.pathIsDirectory fn) "/default.nix" toString fn + optionalString (pathIsDirectory fn) "/default.nix"
else "<unknown location>"; else "<unknown location>";
in "Function called without required argument \"${arg}\" at " in "Function called without required argument \"${arg}\" at "
+ "${loc'}${prettySuggestions (getSuggestions arg)}"; + "${loc'}${prettySuggestions (getSuggestions arg)}";
# Only show the error for the first missing argument # Only show the error for the first missing argument
error = errorForArg (lib.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 /* Like callPackage, but for a function that returns an attribute
@ -201,17 +212,17 @@ rec {
*/ */
callPackagesWith = autoArgs: fn: args: callPackagesWith = autoArgs: fn: args:
let let
f = if lib.isFunction fn then fn else import fn; f = if isFunction fn then fn else import fn;
auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs; auto = intersectAttrs (functionArgs f) autoArgs;
origArgs = auto // args; origArgs = auto // args;
pkgs = f origArgs; pkgs = f origArgs;
mkAttrOverridable = name: _: makeOverridable (newArgs: (f newArgs).${name}) origArgs; mkAttrOverridable = name: _: makeOverridable (newArgs: (f newArgs).${name}) origArgs;
in in
if lib.isDerivation pkgs then throw if isDerivation pkgs then throw
("function `callPackages` was called on a *single* derivation " ("function `callPackages` was called on a *single* derivation "
+ ''"${pkgs.name or "<unknown-name>"}";'' + ''"${pkgs.name or "<unknown-name>"}";''
+ " did you mean to use `callPackage` instead?") + " 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 /* Add attributes to each output of a derivation without changing
@ -224,7 +235,7 @@ rec {
let let
outputs = drv.outputs or [ "out" ]; outputs = drv.outputs or [ "out" ];
commonAttrs = drv // (builtins.listToAttrs outputsList) // commonAttrs = drv // (listToAttrs outputsList) //
({ all = map (x: x.value) outputsList; }) // passthru; ({ all = map (x: x.value) outputsList; }) // passthru;
outputToAttrListElement = outputName: outputToAttrListElement = outputName:
@ -238,7 +249,7 @@ rec {
# TODO: give the derivation control over the outputs. # TODO: give the derivation control over the outputs.
# `overrideAttrs` may not be the only attribute that needs # `overrideAttrs` may not be the only attribute that needs
# updating when switching outputs. # 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. # TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing.
overrideAttrs = f: (passthru.overrideAttrs f).${outputName}; overrideAttrs = f: (passthru.overrideAttrs f).${outputName};
}; };
@ -264,11 +275,11 @@ rec {
commonAttrs = commonAttrs =
{ inherit (drv) name system meta; inherit outputs; } { inherit (drv) name system meta; inherit outputs; }
// lib.optionalAttrs (drv._hydraAggregate or false) { // optionalAttrs (drv._hydraAggregate or false) {
_hydraAggregate = true; _hydraAggregate = true;
constituents = map hydraJob (lib.flatten drv.constituents); constituents = map hydraJob (flatten drv.constituents);
} }
// (lib.listToAttrs outputsList); // (listToAttrs outputsList);
makeOutput = outputName: makeOutput = outputName:
let output = drv.${outputName}; in let output = drv.${outputName}; in
@ -283,9 +294,9 @@ rec {
outputsList = map makeOutput outputs; outputsList = map makeOutput outputs;
drv' = (lib.head outputsList).value; drv' = (head outputsList).value;
in if drv == null then null else 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 /* Make a set of packages with a common scope. All packages called
with the provided `callPackage` will be evaluated with the same with the provided `callPackage` will be evaluated with the same
@ -304,11 +315,11 @@ rec {
let self = f self // { let self = f self // {
newScope = scope: newScope (self // scope); newScope = scope: newScope (self // scope);
callPackage = self.newScope {}; 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. # 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`." "`overrideScope'` (from `lib.makeScope`) has been renamed to `overrideScope`."
(makeScope newScope (lib.fixedPoints.extends g f)); (makeScope newScope (extends g f));
packages = f; packages = f;
}; };
in self; in self;
@ -384,7 +395,7 @@ rec {
overrideScope = g: (makeScopeWithSplicing' overrideScope = g: (makeScopeWithSplicing'
{ inherit splicePackages newScope; } { inherit splicePackages newScope; }
{ inherit otherSplices keep extra; { inherit otherSplices keep extra;
f = lib.fixedPoints.extends g f; f = extends g f;
}); });
packages = f; packages = f;
}; };