diff --git a/lib/customisation.nix b/lib/customisation.nix index 9319fad0b822..30747f7fde7e 100644 --- a/lib/customisation.nix +++ b/lib/customisation.nix @@ -864,4 +864,139 @@ rec { transformDrv ; }; + + /** + Removes a prefix from the attribute names of a cross index. + + A cross index (short for "Cross Platform Pair Index") is a 6-field structure + organizing values by cross-compilation platform relationships. + + # Inputs + + `prefix` + : The prefix to remove from cross index attribute names + + `crossIndex` + : A cross index with prefixed names + + # Type + + ``` + renameCrossIndexFrom :: String -> AttrSet -> AttrSet + ``` + + # Examples + + :::{.example} + ## `lib.customisation.renameCrossIndexFrom` usage example + + ```nix + renameCrossIndexFrom "pkgs" { pkgsBuildBuild = ...; pkgsBuildHost = ...; ... } + => { buildBuild = ...; buildHost = ...; ... } + ``` + ::: + */ + renameCrossIndexFrom = prefix: x: { + buildBuild = x."${prefix}BuildBuild"; + buildHost = x."${prefix}BuildHost"; + buildTarget = x."${prefix}BuildTarget"; + hostHost = x."${prefix}HostHost"; + hostTarget = x."${prefix}HostTarget"; + targetTarget = x."${prefix}TargetTarget"; + }; + + /** + Adds a prefix to the attribute names of a cross index. + + A cross index (short for "Cross Platform Pair Index") is a 6-field structure + organizing values by cross-compilation platform relationships. + + # Inputs + + `prefix` + : The prefix to add to cross index attribute names + + `crossIndex` + : A cross index to be prefixed + + # Type + + ``` + renameCrossIndexTo :: String -> AttrSet -> AttrSet + ``` + + # Examples + + :::{.example} + ## `lib.customisation.renameCrossIndexTo` usage example + + ```nix + renameCrossIndexTo "self" { buildBuild = ...; buildHost = ...; ... } + => { selfBuildBuild = ...; selfBuildHost = ...; ... } + ``` + ::: + */ + renameCrossIndexTo = prefix: x: { + "${prefix}BuildBuild" = x.buildBuild; + "${prefix}BuildHost" = x.buildHost; + "${prefix}BuildTarget" = x.buildTarget; + "${prefix}HostHost" = x.hostHost; + "${prefix}HostTarget" = x.hostTarget; + "${prefix}TargetTarget" = x.targetTarget; + }; + + /** + Takes a function and applies it pointwise to each field of a cross index. + + A cross index (short for "Cross Platform Pair Index") is a 6-field structure + organizing values by cross-compilation platform relationships. + + # Inputs + + `f` + : Function to apply to each cross index value + + `crossIndex` + : A cross index to transform + + # Type + + ``` + mapCrossIndex :: (a -> b) -> AttrSet -> AttrSet + ``` + + # Examples + + :::{.example} + ## `lib.customisation.mapCrossIndex` usage example + + ```nix + mapCrossIndex (x: x * 10) { buildBuild = 1; buildHost = 2; ... } + => { buildBuild = 10; buildHost = 20; ... } + ``` + + ```nix + # Extract a package from package sets + mapCrossIndex (pkgs: pkgs.hello) crossIndexedPackageSets + ``` + ::: + */ + mapCrossIndex = + f: + { + buildBuild, + buildHost, + buildTarget, + hostHost, + hostTarget, + targetTarget, + }: + { + buildBuild = f buildBuild; + buildHost = f buildHost; + buildTarget = f buildTarget; + hostHost = f hostHost; + hostTarget = f hostTarget; + targetTarget = f targetTarget; + }; } diff --git a/lib/default.nix b/lib/default.nix index 157e4e2be278..e10332ca58dd 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -397,6 +397,9 @@ let makeScopeWithSplicing makeScopeWithSplicing' extendMkDerivation + renameCrossIndexFrom + renameCrossIndexTo + mapCrossIndex ; inherit (self.derivations) lazyDerivation optionalDrvAttr warnOnInstantiate; inherit (self.generators) mkLuaInline; diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index d12c27c34bbc..2ec670262649 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -4741,4 +4741,82 @@ runTests { expected = "/non-existent/this/does/not/exist/for/real/please-dont-mess-with-your-local-fs/default.nix"; }; + # Tests for cross index utilities + + testRenameCrossIndexFrom = { + expr = lib.renameCrossIndexFrom "pkgs" { + pkgsBuildBuild = "dummy-build-build"; + pkgsBuildHost = "dummy-build-host"; + pkgsBuildTarget = "dummy-build-target"; + pkgsHostHost = "dummy-host-host"; + pkgsHostTarget = "dummy-host-target"; + pkgsTargetTarget = "dummy-target-target"; + }; + expected = { + buildBuild = "dummy-build-build"; + buildHost = "dummy-build-host"; + buildTarget = "dummy-build-target"; + hostHost = "dummy-host-host"; + hostTarget = "dummy-host-target"; + targetTarget = "dummy-target-target"; + }; + }; + + testRenameCrossIndexTo = { + expr = lib.renameCrossIndexTo "self" { + buildBuild = "dummy-build-build"; + buildHost = "dummy-build-host"; + buildTarget = "dummy-build-target"; + hostHost = "dummy-host-host"; + hostTarget = "dummy-host-target"; + targetTarget = "dummy-target-target"; + }; + expected = { + selfBuildBuild = "dummy-build-build"; + selfBuildHost = "dummy-build-host"; + selfBuildTarget = "dummy-build-target"; + selfHostHost = "dummy-host-host"; + selfHostTarget = "dummy-host-target"; + selfTargetTarget = "dummy-target-target"; + }; + }; + + testMapCrossIndex = { + expr = lib.mapCrossIndex (x: x * 10) { + buildBuild = 1; + buildHost = 2; + buildTarget = 3; + hostHost = 4; + hostTarget = 5; + targetTarget = 6; + }; + expected = { + buildBuild = 10; + buildHost = 20; + buildTarget = 30; + hostHost = 40; + hostTarget = 50; + targetTarget = 60; + }; + }; + + testMapCrossIndexString = { + expr = lib.mapCrossIndex (x: "prefix-${x}") { + buildBuild = "bb"; + buildHost = "bh"; + buildTarget = "bt"; + hostHost = "hh"; + hostTarget = "ht"; + targetTarget = "tt"; + }; + expected = { + buildBuild = "prefix-bb"; + buildHost = "prefix-bh"; + buildTarget = "prefix-bt"; + hostHost = "prefix-hh"; + hostTarget = "prefix-ht"; + targetTarget = "prefix-tt"; + }; + }; + } diff --git a/pkgs/top-level/splice.nix b/pkgs/top-level/splice.nix index fe4566bb175e..24fbb5e4eabf 100644 --- a/pkgs/top-level/splice.nix +++ b/pkgs/top-level/splice.nix @@ -17,48 +17,30 @@ lib: pkgs: actuallySplice: let + inherit (lib.customisation) mapCrossIndex renameCrossIndexFrom; spliceReal = - { - pkgsBuildBuild, - pkgsBuildHost, - pkgsBuildTarget, - pkgsHostHost, - pkgsHostTarget, - pkgsTargetTarget, - }: + inputs: let mash = # Other pkgs sets - pkgsBuildBuild - // pkgsBuildTarget - // pkgsHostHost - // pkgsTargetTarget + inputs.buildBuild + // inputs.buildTarget + // inputs.hostHost + // inputs.targetTarget # The same pkgs sets one probably intends - // pkgsBuildHost - // pkgsHostTarget; + // inputs.buildHost + // inputs.hostTarget; merge = name: { inherit name; value = let defaultValue = mash.${name}; # `or {}` is for the non-derivation attsert splicing case, where `{}` is the identity. - valueBuildBuild = pkgsBuildBuild.${name} or { }; - valueBuildHost = pkgsBuildHost.${name} or { }; - valueBuildTarget = pkgsBuildTarget.${name} or { }; - valueHostHost = pkgsHostHost.${name} or { }; - valueHostTarget = pkgsHostTarget.${name} or { }; - valueTargetTarget = pkgsTargetTarget.${name} or { }; + value' = mapCrossIndex (x: x.${name} or { }) inputs; + augmentedValue = defaultValue // { - __spliced = - (lib.optionalAttrs (pkgsBuildBuild ? ${name}) { buildBuild = valueBuildBuild; }) - // (lib.optionalAttrs (pkgsBuildHost ? ${name}) { buildHost = valueBuildHost; }) - // (lib.optionalAttrs (pkgsBuildTarget ? ${name}) { buildTarget = valueBuildTarget; }) - // (lib.optionalAttrs (pkgsHostHost ? ${name}) { hostHost = valueHostHost; }) - // (lib.optionalAttrs (pkgsHostTarget ? ${name}) { hostTarget = valueHostTarget; }) - // (lib.optionalAttrs (pkgsTargetTarget ? ${name}) { - targetTarget = valueTargetTarget; - }); + __spliced = lib.filterAttrs (k: v: inputs.${k} ? ${name}) value'; }; # 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 @@ -76,27 +58,12 @@ let # on to splice them together. if lib.isDerivation defaultValue then augmentedValue - // spliceReal { - pkgsBuildBuild = tryGetOutputs valueBuildBuild; - pkgsBuildHost = tryGetOutputs valueBuildHost; - pkgsBuildTarget = tryGetOutputs valueBuildTarget; - pkgsHostHost = tryGetOutputs valueHostHost; - pkgsHostTarget = getOutputs valueHostTarget; - pkgsTargetTarget = tryGetOutputs valueTargetTarget; - # Just recur on plain attrsets - } + // spliceReal (mapCrossIndex tryGetOutputs value' // { hostTarget = getOutputs value'.hostTarget; }) else if lib.isAttrs defaultValue then - spliceReal { - pkgsBuildBuild = valueBuildBuild; - pkgsBuildHost = valueBuildHost; - pkgsBuildTarget = valueBuildTarget; - pkgsHostHost = valueHostHost; - pkgsHostTarget = valueHostTarget; - pkgsTargetTarget = valueTargetTarget; - # Don't be fancy about non-derivations. But we could have used used - # `__functor__` for functions instead. - } + spliceReal value' else + # Don't be fancy about non-derivations. But we could have used used + # `__functor__` for functions instead. defaultValue; }; in @@ -111,7 +78,7 @@ let pkgsHostTarget, pkgsTargetTarget, }@args: - if actuallySplice then spliceReal args else pkgsHostTarget; + if actuallySplice then spliceReal (renameCrossIndexFrom "pkgs" args) else pkgsHostTarget; splicedPackages = splicePackages {