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