1
0
Fork 1
mirror of https://github.com/NixOS/nixpkgs.git synced 2024-12-25 03:17:13 +00:00
nixpkgs/pkgs/top-level/splice.nix
2017-12-10 17:01:23 -05:00

89 lines
4 KiB
Nix

# The `splicedPackages' package set, and its use by `callPackage`
#
# The `buildPackages` pkg set is a new concept, and the vast majority package
# expression (the other *.nix files) are not designed with it in mind. This
# presents us with a problem with how to get the right version (build-time vs
# run-time) of a package to a consumer that isn't used to thinking so cleverly.
#
# The solution is to splice the package sets together as we do below, so every
# `callPackage`d expression in fact gets both versions. Each# derivation (and
# each derivation's outputs) consists of the run-time version, augmented with a
# `nativeDrv` field for the build-time version, and `crossDrv` field for the
# run-time version.
#
# We could have used any names we want for the disambiguated versions, but
# `crossDrv` and `nativeDrv` were somewhat similarly used for the old
# cross-compiling infrastructure. The names are mostly invisible as
# `mkDerivation` knows how to pull out the right ones for `buildDepends` and
# friends, but a few packages use them directly, so it seemed efficient (to
# @Ericson2314) to reuse those names, at least initially, to minimize breakage.
#
# For performance reasons, rather than uniformally splice in all cases, we only
# do so when `pkgs` and `buildPackages` are distinct. The `actuallySplice`
# parameter there the boolean value of that equality check.
lib: pkgs: actuallySplice:
let
defaultBuildScope = pkgs.buildPackages // pkgs.buildPackages.xorg;
# TODO(@Ericson2314): we shouldn't preclude run-time fetching by removing
# these attributes. We should have a more general solution for selecting
# whether `nativeDrv` or `crossDrv` is the default in `defaultScope`.
pkgsWithoutFetchers = lib.filterAttrs (n: _: !lib.hasPrefix "fetch" n) pkgs;
defaultRunScope = pkgsWithoutFetchers // pkgs.xorg;
splicer = buildPkgs: runPkgs: let
mash = buildPkgs // runPkgs;
merge = name: {
inherit name;
value = let
defaultValue = mash.${name};
# `or {}` is for the non-derivation attsert splicing case, where `{}` is the identity.
buildValue = buildPkgs.${name} or {};
runValue = runPkgs.${name} or {};
augmentedValue = defaultValue
// (lib.optionalAttrs (buildPkgs ? ${name}) { nativeDrv = buildValue; })
// (lib.optionalAttrs (runPkgs ? ${name}) { crossDrv = runValue; });
# Get the set of outputs of a derivation. If one derivation fails to
# evaluate we don't want to diverge the entire splice, so we fall back
# on {}
tryGetOutputs = value0: let
inherit (builtins.tryEval value0) success value;
in getOutputs (lib.optionalAttrs success value);
getOutputs = value: lib.genAttrs
(value.outputs or (lib.optional (value ? out) "out"))
(output: value.${output});
in
# The derivation along with its outputs, which we recur
# on to splice them together.
if lib.isDerivation defaultValue then augmentedValue
// splicer (tryGetOutputs buildValue) (getOutputs runValue)
# Just recur on plain attrsets
else if lib.isAttrs defaultValue then splicer buildValue runValue
# Don't be fancy about non-derivations. But we could have used used
# `__functor__` for functions instead.
else defaultValue;
};
in lib.listToAttrs (map merge (lib.attrNames mash));
splicedPackages =
if actuallySplice
then splicer defaultBuildScope defaultRunScope // {
# These should never be spliced under any circumstances
inherit (pkgs) pkgs buildPackages targetPackages
buildPlatform targetPlatform hostPlatform;
}
else pkgs // pkgs.xorg;
in
{
# We use `callPackage' to be able to omit function arguments that can be
# obtained `pkgs` or `buildPackages` and their `xorg` package sets. Use
# `newScope' for sets of packages in `pkgs' (see e.g. `gnome' below).
callPackage = pkgs.newScope {};
callPackages = lib.callPackagesWith splicedPackages;
newScope = extra: lib.callPackageWith (splicedPackages // extra);
}