1# The `splicedPackages' package set, and its use by `callPackage`
2#
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.
7#
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
11# a `__spliced.buildHost` field for the build-time version, and
12# `__spliced.hostTarget` field for the run-time version.
13#
14# For performance reasons, rather than uniformally splice in all cases, we only
15# do so when `pkgs` and `buildPackages` are distinct. The `actuallySplice`
16# parameter there the boolean value of that equality check.
17lib: pkgs: actuallySplice:
18
19let
20
21 spliceReal =
22 {
23 pkgsBuildBuild,
24 pkgsBuildHost,
25 pkgsBuildTarget,
26 pkgsHostHost,
27 pkgsHostTarget,
28 pkgsTargetTarget,
29 }:
30 let
31 mash =
32 # Other pkgs sets
33 pkgsBuildBuild
34 // pkgsBuildTarget
35 // pkgsHostHost
36 // pkgsTargetTarget
37 # The same pkgs sets one probably intends
38 // pkgsBuildHost
39 // pkgsHostTarget;
40 merge = name: {
41 inherit name;
42 value =
43 let
44 defaultValue = mash.${name};
45 # `or {}` is for the non-derivation attsert splicing case, where `{}` is the identity.
46 valueBuildBuild = pkgsBuildBuild.${name} or { };
47 valueBuildHost = pkgsBuildHost.${name} or { };
48 valueBuildTarget = pkgsBuildTarget.${name} or { };
49 valueHostHost = pkgsHostHost.${name} or { };
50 valueHostTarget = pkgsHostTarget.${name} or { };
51 valueTargetTarget = pkgsTargetTarget.${name} or { };
52 augmentedValue = defaultValue // {
53 __spliced =
54 (lib.optionalAttrs (pkgsBuildBuild ? ${name}) { buildBuild = valueBuildBuild; })
55 // (lib.optionalAttrs (pkgsBuildHost ? ${name}) { buildHost = valueBuildHost; })
56 // (lib.optionalAttrs (pkgsBuildTarget ? ${name}) { buildTarget = valueBuildTarget; })
57 // (lib.optionalAttrs (pkgsHostHost ? ${name}) { hostHost = valueHostHost; })
58 // (lib.optionalAttrs (pkgsHostTarget ? ${name}) { hostTarget = valueHostTarget; })
59 // (lib.optionalAttrs (pkgsTargetTarget ? ${name}) {
60 targetTarget = valueTargetTarget;
61 });
62 };
63 # Get the set of outputs of a derivation. If one derivation fails to
64 # evaluate we don't want to diverge the entire splice, so we fall back
65 # on {}
66 tryGetOutputs =
67 value0:
68 let
69 inherit (builtins.tryEval value0) success value;
70 in
71 getOutputs (lib.optionalAttrs success value);
72 getOutputs =
73 value: lib.genAttrs (value.outputs or (lib.optional (value ? out) "out")) (output: value.${output});
74 in
75 # The derivation along with its outputs, which we recur
76 # on to splice them together.
77 if lib.isDerivation defaultValue then
78 augmentedValue
79 // spliceReal {
80 pkgsBuildBuild = tryGetOutputs valueBuildBuild;
81 pkgsBuildHost = tryGetOutputs valueBuildHost;
82 pkgsBuildTarget = tryGetOutputs valueBuildTarget;
83 pkgsHostHost = tryGetOutputs valueHostHost;
84 pkgsHostTarget = getOutputs valueHostTarget;
85 pkgsTargetTarget = tryGetOutputs valueTargetTarget;
86 # Just recur on plain attrsets
87 }
88 else if lib.isAttrs defaultValue then
89 spliceReal {
90 pkgsBuildBuild = valueBuildBuild;
91 pkgsBuildHost = valueBuildHost;
92 pkgsBuildTarget = valueBuildTarget;
93 pkgsHostHost = valueHostHost;
94 pkgsHostTarget = valueHostTarget;
95 pkgsTargetTarget = valueTargetTarget;
96 # Don't be fancy about non-derivations. But we could have used used
97 # `__functor__` for functions instead.
98 }
99 else
100 defaultValue;
101 };
102 in
103 lib.listToAttrs (map merge (lib.attrNames mash));
104
105 splicePackages =
106 {
107 pkgsBuildBuild,
108 pkgsBuildHost,
109 pkgsBuildTarget,
110 pkgsHostHost,
111 pkgsHostTarget,
112 pkgsTargetTarget,
113 }@args:
114 if actuallySplice then spliceReal args else pkgsHostTarget;
115
116 splicedPackages =
117 splicePackages {
118 inherit (pkgs)
119 pkgsBuildBuild
120 pkgsBuildHost
121 pkgsBuildTarget
122 pkgsHostHost
123 pkgsHostTarget
124 pkgsTargetTarget
125 ;
126 }
127 // {
128 # These should never be spliced under any circumstances
129 inherit (pkgs)
130 pkgsBuildBuild
131 pkgsBuildHost
132 pkgsBuildTarget
133 pkgsHostHost
134 pkgsHostTarget
135 pkgsTargetTarget
136 buildPackages
137 pkgs
138 targetPackages
139 ;
140 };
141
142 splicedPackagesWithXorg =
143 splicedPackages
144 // builtins.removeAttrs splicedPackages.xorg [
145 "callPackage"
146 "newScope"
147 "overrideScope"
148 "packages"
149 ];
150
151 packagesWithXorg =
152 pkgs
153 // builtins.removeAttrs pkgs.xorg [
154 "callPackage"
155 "newScope"
156 "overrideScope"
157 "packages"
158 ];
159
160 pkgsForCall = if actuallySplice then splicedPackagesWithXorg else packagesWithXorg;
161
162in
163
164{
165 inherit splicePackages;
166
167 # We use `callPackage' to be able to omit function arguments that can be
168 # obtained `pkgs` or `buildPackages` and their `xorg` package sets. Use
169 # `newScope' for sets of packages in `pkgs' (see e.g. `gnome' below).
170 callPackage = pkgs.newScope { };
171
172 callPackages = lib.callPackagesWith pkgsForCall;
173
174 newScope = extra: lib.callPackageWith (pkgsForCall // extra);
175
176 # prefill 2 fields of the function for convenience
177 makeScopeWithSplicing = lib.makeScopeWithSplicing splicePackages pkgs.newScope;
178 makeScopeWithSplicing' = lib.makeScopeWithSplicing' {
179 inherit splicePackages;
180 inherit (pkgs) newScope;
181 };
182
183 # generate 'otherSplices' for 'makeScopeWithSplicing'
184 generateSplicesForMkScope =
185 attrs:
186 let
187 split =
188 X:
189 [ X ]
190 ++ (
191 if builtins.isList attrs then
192 attrs
193 else if builtins.isString attrs then
194 lib.splitString "." attrs
195 else
196 throw "generateSplicesForMkScope must be passed a list of string or string"
197 );
198 bad = throw "attribute should be found";
199 in
200 {
201 selfBuildBuild = lib.attrByPath (split "pkgsBuildBuild") bad pkgs;
202 selfBuildHost = lib.attrByPath (split "pkgsBuildHost") bad pkgs;
203 selfBuildTarget = lib.attrByPath (split "pkgsBuildTarget") bad pkgs;
204 selfHostHost = lib.attrByPath (split "pkgsHostHost") bad pkgs;
205 selfHostTarget = lib.attrByPath (split "pkgsHostTarget") bad pkgs;
206 selfTargetTarget = lib.attrByPath (split "pkgsTargetTarget") { } pkgs;
207 };
208
209 # Haskell package sets need this because they reimplement their own
210 # `newScope`.
211 __splicedPackages =
212 if actuallySplice then splicedPackages // { recurseForDerivations = false; } else pkgs;
213}