21.05 beta release
[NixPkgs.git] / pkgs / top-level / splice.nix
bloba093442d3698c921128372b02ea5a01bfcbd9bd9
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
12 # run-time version.
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:
26 let
28   spliceReal = { pkgsBuildBuild, pkgsBuildHost, pkgsBuildTarget
29                , pkgsHostHost, pkgsHostTarget
30                , pkgsTargetTarget
31                }: let
32     mash =
33       # Other pkgs sets
34       pkgsBuildBuild // pkgsBuildTarget // pkgsHostHost // pkgsTargetTarget
35       # The same pkgs sets one probably intends
36       // pkgsBuildHost // pkgsHostTarget;
37     merge = name: {
38       inherit name;
39       value = let
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 = throw "`valueHostHost` unimplemented: pass manually rather than relying on splice.";
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; })
52           // {
53             __spliced =
54                  (lib.optionalAttrs (pkgsBuildBuild ? ${name}) { buildBuild = valueBuildBuild; })
55               // (lib.optionalAttrs (pkgsBuildTarget ? ${name}) { buildTarget = valueBuildTarget; })
56               // { hostHost = valueHostHost; }
57               // (lib.optionalAttrs (pkgsTargetTarget ? ${name}) { targetTarget = valueTargetTarget;
58           });
59         };
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
62         # on {}
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});
69       in
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 = {};
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.
89         } else defaultValue;
90     };
91   in lib.listToAttrs (map merge (lib.attrNames mash));
93   splicePackages = { pkgsBuildBuild, pkgsBuildHost, pkgsBuildTarget
94                    , pkgsHostHost, pkgsHostTarget
95                    , pkgsTargetTarget
96                    } @ args:
97     if actuallySplice then spliceReal args else pkgsHostTarget;
99   splicedPackages = splicePackages {
100     inherit (pkgs)
101       pkgsBuildBuild pkgsBuildHost pkgsBuildTarget
102       pkgsHostHost pkgsHostTarget
103       pkgsTargetTarget
104       ;
105   } // {
106     # These should never be spliced under any circumstances
107     inherit (pkgs)
108       pkgsBuildBuild pkgsBuildHost pkgsBuildTarget
109       pkgsHostHost pkgsHostTarget
110       pkgsTargetTarget
111       buildPackages pkgs targetPackages
112       ;
113     inherit (pkgs.stdenv) buildPlatform targetPlatform hostPlatform;
114   };
116   splicedPackagesWithXorg = splicedPackages // builtins.removeAttrs splicedPackages.xorg [
117     "callPackage" "newScope" "overrideScope" "packages"
118   ];
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
135   # `newScope`.
136   __splicedPackages = splicedPackages // { recurseForDerivations = false; };