1 # The `splicedPackages' package set, and its use by `callPackage`
3 # The `buildPackages` pkg set is a new concept, and the vast majority package
4 # expression (the other *.nix files) are not designed with it in mind. This
5 # presents us with a problem with how to get the right version (build-time vs
6 # run-time) of a package to a consumer that isn't used to thinking so cleverly.
8 # The solution is to splice the package sets together as we do below, so every
9 # `callPackage`d expression in fact gets both versions. Each# derivation (and
10 # each derivation's outputs) consists of the run-time version, augmented with a
11 # `nativeDrv` field for the build-time version, and `crossDrv` field for the
14 # We could have used any names we want for the disambiguated versions, but
15 # `crossDrv` and `nativeDrv` were somewhat similarly used for the old
16 # cross-compiling infrastructure. The names are mostly invisible as
17 # `mkDerivation` knows how to pull out the right ones for `buildDepends` and
18 # friends, but a few packages use them directly, so it seemed efficient (to
19 # @Ericson2314) to reuse those names, at least initially, to minimize breakage.
21 # For performance reasons, rather than uniformally splice in all cases, we only
22 # do so when `pkgs` and `buildPackages` are distinct. The `actuallySplice`
23 # parameter there the boolean value of that equality check.
24 lib: pkgs: actuallySplice:
28 spliceReal = { pkgsBuildBuild, pkgsBuildHost, pkgsBuildTarget
29 , pkgsHostHost, pkgsHostTarget
34 pkgsBuildBuild // pkgsBuildTarget // pkgsHostHost // pkgsTargetTarget
35 # The same pkgs sets one probably intends
36 // pkgsBuildHost // pkgsHostTarget;
40 defaultValue = mash.${name};
41 # `or {}` is for the non-derivation attsert splicing case, where `{}` is the identity.
42 valueBuildBuild = pkgsBuildBuild.${name} or {};
43 valueBuildHost = pkgsBuildHost.${name} or {};
44 valueBuildTarget = pkgsBuildTarget.${name} or {};
45 valueHostHost = pkgsHostHost.${name} or {};
46 valueHostTarget = pkgsHostTarget.${name} or {};
47 valueTargetTarget = pkgsTargetTarget.${name} or {};
48 augmentedValue = defaultValue
49 # TODO(@Ericson2314): Stop using old names after transition period
50 // (lib.optionalAttrs (pkgsBuildHost ? ${name}) { nativeDrv = valueBuildHost; })
51 // (lib.optionalAttrs (pkgsHostTarget ? ${name}) { crossDrv = valueHostTarget; })
54 (lib.optionalAttrs (pkgsBuildBuild ? ${name}) { buildBuild = valueBuildBuild; })
55 // (lib.optionalAttrs (pkgsBuildTarget ? ${name}) { buildTarget = valueBuildTarget; })
56 // (lib.optionalAttrs (pkgsHostHost ? ${name}) { hostHost = valueHostHost; })
57 // (lib.optionalAttrs (pkgsTargetTarget ? ${name}) { targetTarget = valueTargetTarget;
60 # Get the set of outputs of a derivation. If one derivation fails to
61 # evaluate we don't want to diverge the entire splice, so we fall back
63 tryGetOutputs = value0: let
64 inherit (builtins.tryEval value0) success value;
65 in getOutputs (lib.optionalAttrs success value);
66 getOutputs = value: lib.genAttrs
67 (value.outputs or (lib.optional (value ? out) "out"))
68 (output: value.${output});
70 # The derivation along with its outputs, which we recur
71 # on to splice them together.
72 if lib.isDerivation defaultValue then augmentedValue // spliceReal {
73 pkgsBuildBuild = tryGetOutputs valueBuildBuild;
74 pkgsBuildHost = tryGetOutputs valueBuildHost;
75 pkgsBuildTarget = tryGetOutputs valueBuildTarget;
76 pkgsHostHost = tryGetOutputs valueHostHost;
77 pkgsHostTarget = getOutputs valueHostTarget;
78 pkgsTargetTarget = tryGetOutputs valueTargetTarget;
79 # Just recur on plain attrsets
80 } else if lib.isAttrs defaultValue then spliceReal {
81 pkgsBuildBuild = valueBuildBuild;
82 pkgsBuildHost = valueBuildHost;
83 pkgsBuildTarget = valueBuildTarget;
84 pkgsHostHost = valueHostHost;
85 pkgsHostTarget = valueHostTarget;
86 pkgsTargetTarget = valueTargetTarget;
87 # Don't be fancy about non-derivations. But we could have used used
88 # `__functor__` for functions instead.
91 in lib.listToAttrs (map merge (lib.attrNames mash));
93 splicePackages = { pkgsBuildBuild, pkgsBuildHost, pkgsBuildTarget
94 , pkgsHostHost, pkgsHostTarget
97 if actuallySplice then spliceReal args else pkgsHostTarget;
99 splicedPackages = splicePackages {
101 pkgsBuildBuild pkgsBuildHost pkgsBuildTarget
102 pkgsHostHost pkgsHostTarget
106 # These should never be spliced under any circumstances
108 pkgsBuildBuild pkgsBuildHost pkgsBuildTarget
109 pkgsHostHost pkgsHostTarget
111 buildPackages pkgs targetPackages
113 inherit (pkgs.stdenv) buildPlatform targetPlatform hostPlatform;
116 splicedPackagesWithXorg = splicedPackages // builtins.removeAttrs splicedPackages.xorg [
117 "callPackage" "newScope" "overrideScope" "packages"
123 inherit splicePackages;
125 # We use `callPackage' to be able to omit function arguments that can be
126 # obtained `pkgs` or `buildPackages` and their `xorg` package sets. Use
127 # `newScope' for sets of packages in `pkgs' (see e.g. `gnome' below).
128 callPackage = pkgs.newScope {};
130 callPackages = lib.callPackagesWith splicedPackagesWithXorg;
132 newScope = extra: lib.callPackageWith (splicedPackagesWithXorg // extra);
134 # Haskell package sets need this because they reimplement their own
136 __splicedPackages = splicedPackages // { recurseForDerivations = false; };