Unverified Commit acb6191c authored by Robert Hensing's avatar Robert Hensing Committed by GitHub
Browse files

lib: Add splicing utilities (#426889)

parents 87102544 f8566d22
Loading
Loading
Loading
Loading
+135 −0
Original line number Diff line number Diff line
@@ -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;
    };
}
+3 −0
Original line number Diff line number Diff line
@@ -397,6 +397,9 @@ let
        makeScopeWithSplicing
        makeScopeWithSplicing'
        extendMkDerivation
        renameCrossIndexFrom
        renameCrossIndexTo
        mapCrossIndex
        ;
      inherit (self.derivations) lazyDerivation optionalDrvAttr warnOnInstantiate;
      inherit (self.generators) mkLuaInline;
+78 −0
Original line number Diff line number Diff line
@@ -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";
    };
  };

}
+16 −49
Original line number Diff line number Diff line
@@ -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;
            spliceReal value'
          else
            # Don't be fancy about non-derivations. But we could have used used
            # `__functor__` for functions instead.
            }
          else
            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 {