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}