nixpkgs/pkgs/top-level/splice.nix
Artturin ea7c2e29be splice.nix: improve performance with early cut-off
All these 062c4f5974/pkgs/top-level/splice.nix (L116-L121)
are already in pkgs, so they don't have to be added on top again when there's no splicing

|stat                  | before         | after          | Δ              | Δ%    |
|----------------------|----------------|----------------|----------------|-------|
|cpuTime               | 767.33         | 757.25         | ↘ 10.08        | -1.31%|
|envs-bytes            | 23,767,294,088 | 23,767,243,520 | ↘ 50,568       | -0.00%|
|envs-elements         | 1,260,156,605  | 1,260,151,924  | ↘ 4,681        | -0.00%|
|envs-number           | 855,377,578    | 855,376,758    | ↘ 820          | -0.00%|
|gc-heapSize           | 66,525,835,264 | 64,428,679,168 | ↘ 2,097,156,096| -3.15%|
|gc-totalBytes         | 146,231,201,712| 145,437,495,488| ↘ 793,706,224  | -0.54%|
|list-bytes            | 3,319,349,160  | 3,319,349,160  | 0              |       |
|list-concats          | 88,567,304     | 88,567,304     | 0              |       |
|list-elements         | 414,918,645    | 414,918,645    | 0              |       |
|nrAvoided             | 989,429,178    | 989,427,108    | ↘ 2,070        | -0.00%|
|nrFunctionCalls       | 784,049,358    | 784,048,538    | ↘ 820          | -0.00%|
|nrLookups             | 412,401,378    | 412,399,016    | ↘ 2,362        | -0.00%|
|nrOpUpdateValuesCopied| 2,132,453,124  | 2,082,900,933  | ↘ 49,552,191   | -2.32%|
|nrOpUpdates           | 99,050,775     | 99,048,489     | ↘ 2,286        | -0.00%|
|nrPrimOpCalls         | 411,581,753    | 411,581,753    | 0              |       |
|nrThunks              | 1,248,612,305  | 1,248,600,848  | ↘ 11,457       | -0.00%|
|sets-bytes            | 48,379,815,792 | 47,586,523,600 | ↘ 793,292,192  | -1.64%|
|sets-elements         | 2,840,072,218  | 2,790,496,848  | ↘ 49,575,370   | -1.75%|
|sets-number           | 183,666,269    | 183,660,877    | ↘ 5,392        | -0.00%|
|sizes-Attr            | 16             | 16             | 0              |       |
|sizes-Bindings        | 16             | 16             | 0              |       |
|sizes-Env             | 16             | 16             | 0              |       |
|sizes-Value           | 24             | 24             | 0              |       |
|symbols-bytes         | 3,454,822      | 3,454,838      | ↗ 16           | 0.00% |
|symbols-number        | 188,756        | 188,757        | ↗ 1            | 0.00% |
|values-bytes          | 38,132,032,032 | 38,131,756,776 | ↘ 275,256      | -0.00%|
|values-number         | 1,588,834,668  | 1,588,823,199  | ↘ 11,469       | -0.00%|

The table was formatted manually from stats copied from ofborg(I forgot how to do it properly), so it may
look a bit wonky
2024-10-25 18:12:41 +03:00

184 lines
7.1 KiB
Nix

# The `splicedPackages' package set, and its use by `callPackage`
#
# The `buildPackages` pkg set is a new concept, and the vast majority package
# expression (the other *.nix files) are not designed with it in mind. This
# presents us with a problem with how to get the right version (build-time vs
# run-time) of a package to a consumer that isn't used to thinking so cleverly.
#
# The solution is to splice the package sets together as we do below, so every
# `callPackage`d expression in fact gets both versions. Each derivation (and
# each derivation's outputs) consists of the run-time version, augmented with
# a `__spliced.buildHost` field for the build-time version, and
# `__spliced.hostTarget` field for the run-time version.
#
# For performance reasons, rather than uniformally splice in all cases, we only
# do so when `pkgs` and `buildPackages` are distinct. The `actuallySplice`
# parameter there the boolean value of that equality check.
lib: pkgs: actuallySplice:
let
spliceReal =
{ pkgsBuildBuild
, pkgsBuildHost
, pkgsBuildTarget
, pkgsHostHost
, pkgsHostTarget
, pkgsTargetTarget
}:
let
mash =
# Other pkgs sets
pkgsBuildBuild // pkgsBuildTarget // pkgsHostHost // pkgsTargetTarget
# The same pkgs sets one probably intends
// pkgsBuildHost // pkgsHostTarget;
merge = name: {
inherit name;
value =
let
defaultValue = mash.${name};
# `or {}` is for the non-derivation attsert splicing case, where `{}` is the identity.
valueBuildBuild = pkgsBuildBuild.${name} or { };
valueBuildHost = pkgsBuildHost.${name} or { };
valueBuildTarget = pkgsBuildTarget.${name} or { };
valueHostHost = pkgsHostHost.${name} or { };
valueHostTarget = pkgsHostTarget.${name} or { };
valueTargetTarget = pkgsTargetTarget.${name} or { };
augmentedValue = defaultValue
// {
__spliced =
(lib.optionalAttrs (pkgsBuildBuild ? ${name}) { buildBuild = valueBuildBuild; })
// (lib.optionalAttrs (pkgsBuildHost ? ${name}) { buildHost = valueBuildHost; })
// (lib.optionalAttrs (pkgsBuildTarget ? ${name}) { buildTarget = valueBuildTarget; })
// (lib.optionalAttrs (pkgsHostHost ? ${name}) { hostHost = valueHostHost; })
// (lib.optionalAttrs (pkgsHostTarget ? ${name}) { hostTarget = valueHostTarget; })
// (lib.optionalAttrs (pkgsTargetTarget ? ${name}) {
targetTarget = valueTargetTarget;
});
};
# Get the set of outputs of a derivation. If one derivation fails to
# evaluate we don't want to diverge the entire splice, so we fall back
# on {}
tryGetOutputs = value0:
let
inherit (builtins.tryEval value0) success value;
in
getOutputs (lib.optionalAttrs success value);
getOutputs = value: lib.genAttrs
(value.outputs or (lib.optional (value ? out) "out"))
(output: value.${output});
in
# The derivation along with its outputs, which we recur
# on to splice them together.
if lib.isDerivation defaultValue then augmentedValue // spliceReal {
pkgsBuildBuild = tryGetOutputs valueBuildBuild;
pkgsBuildHost = tryGetOutputs valueBuildHost;
pkgsBuildTarget = tryGetOutputs valueBuildTarget;
pkgsHostHost = tryGetOutputs valueHostHost;
pkgsHostTarget = getOutputs valueHostTarget;
pkgsTargetTarget = tryGetOutputs valueTargetTarget;
# Just recur on plain attrsets
} else if lib.isAttrs defaultValue then
spliceReal
{
pkgsBuildBuild = valueBuildBuild;
pkgsBuildHost = valueBuildHost;
pkgsBuildTarget = valueBuildTarget;
pkgsHostHost = valueHostHost;
pkgsHostTarget = valueHostTarget;
pkgsTargetTarget = valueTargetTarget;
# Don't be fancy about non-derivations. But we could have used used
# `__functor__` for functions instead.
} else defaultValue;
};
in
lib.listToAttrs (map merge (lib.attrNames mash));
splicePackages =
{ pkgsBuildBuild
, pkgsBuildHost
, pkgsBuildTarget
, pkgsHostHost
, pkgsHostTarget
, pkgsTargetTarget
} @ args:
if actuallySplice then spliceReal args else pkgsHostTarget;
splicedPackages = splicePackages
{
inherit (pkgs)
pkgsBuildBuild pkgsBuildHost pkgsBuildTarget
pkgsHostHost pkgsHostTarget
pkgsTargetTarget
;
} // {
# These should never be spliced under any circumstances
inherit (pkgs)
pkgsBuildBuild pkgsBuildHost pkgsBuildTarget
pkgsHostHost pkgsHostTarget
pkgsTargetTarget
buildPackages pkgs targetPackages
;
inherit (pkgs.stdenv) buildPlatform targetPlatform hostPlatform;
};
splicedPackagesWithXorg = splicedPackages // builtins.removeAttrs splicedPackages.xorg [
"callPackage"
"newScope"
"overrideScope"
"packages"
];
packagesWithXorg = pkgs // builtins.removeAttrs pkgs.xorg [
"callPackage"
"newScope"
"overrideScope"
"packages"
];
pkgsForCall = if actuallySplice then splicedPackagesWithXorg else packagesWithXorg;
in
{
inherit splicePackages;
# We use `callPackage' to be able to omit function arguments that can be
# obtained `pkgs` or `buildPackages` and their `xorg` package sets. Use
# `newScope' for sets of packages in `pkgs' (see e.g. `gnome' below).
callPackage = pkgs.newScope { };
callPackages = lib.callPackagesWith pkgsForCall;
newScope = extra: lib.callPackageWith (pkgsForCall // extra);
# prefill 2 fields of the function for convenience
makeScopeWithSplicing = lib.makeScopeWithSplicing splicePackages pkgs.newScope;
makeScopeWithSplicing' = lib.makeScopeWithSplicing' { inherit splicePackages; inherit (pkgs) newScope; };
# generate 'otherSplices' for 'makeScopeWithSplicing'
generateSplicesForMkScope = attrs:
let
split = X: [ X ] ++ (
if builtins.isList attrs
then attrs
else if builtins.isString attrs
then lib.splitString "." attrs
else throw "generateSplicesForMkScope must be passed a list of string or string"
);
bad = throw "attribute should be found";
in
{
selfBuildBuild = lib.attrByPath (split "pkgsBuildBuild") bad pkgs;
selfBuildHost = lib.attrByPath (split "pkgsBuildHost") bad pkgs;
selfBuildTarget = lib.attrByPath (split "pkgsBuildTarget") bad pkgs;
selfHostHost = lib.attrByPath (split "pkgsHostHost") bad pkgs;
selfHostTarget = lib.attrByPath (split "pkgsHostTarget") bad pkgs;
selfTargetTarget = lib.attrByPath (split "pkgsTargetTarget") { } pkgs;
};
# Haskell package sets need this because they reimplement their own
# `newScope`.
__splicedPackages = if actuallySplice then splicedPackages // { recurseForDerivations = false; } else pkgs;
}