Unverified Commit fc2b2af1 authored by Silvan Mosberger's avatar Silvan Mosberger Committed by GitHub
Browse files

Merge pull request #270299 from adisbladis/lib-customisation-allocs

lib.customisation: Refactor nested set access & avoid some allocations
parents d4010ce9 8d162ec7
Loading
Loading
Loading
Loading
+51 −40
Original line number Diff line number Diff line
{ lib }:

let
  inherit (builtins)
    intersectAttrs;
  inherit (lib)
    functionArgs isFunction mirrorFunctionArgs isAttrs setFunctionArgs levenshteinAtMost
    optionalAttrs attrNames levenshtein filter elemAt concatStringsSep sort take length
    filterAttrs optionalString flip pathIsDirectory head pipe isDerivation listToAttrs
    mapAttrs seq flatten deepSeq warnIf isInOldestRelease extends
    ;

in
rec {


@@ -43,15 +54,15 @@ rec {
  overrideDerivation = drv: f:
    let
      newDrv = derivation (drv.drvAttrs // (f drv));
    in lib.flip (extendDerivation (builtins.seq drv.drvPath true)) newDrv (
    in flip (extendDerivation (seq drv.drvPath true)) newDrv (
      { meta = drv.meta or {};
        passthru = if drv ? passthru then drv.passthru else {};
      }
      //
      (drv.passthru or {})
      //
      lib.optionalAttrs (drv ? __spliced) {
        __spliced = {} // (lib.mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced);
      optionalAttrs (drv ? __spliced) {
        __spliced = {} // (mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced);
      });


@@ -79,30 +90,30 @@ rec {
  makeOverridable = f:
    let
      # Creates a functor with the same arguments as f
      mirrorArgs = lib.mirrorFunctionArgs f;
      mirrorArgs = mirrorFunctionArgs f;
    in
    mirrorArgs (origArgs:
    let
      result = f origArgs;

      # Changes the original arguments with (potentially a function that returns) a set of new attributes
      overrideWith = newArgs: origArgs // (if lib.isFunction newArgs then newArgs origArgs else newArgs);
      overrideWith = newArgs: origArgs // (if isFunction newArgs then newArgs origArgs else newArgs);

      # Re-call the function but with different arguments
      overrideArgs = mirrorArgs (newArgs: makeOverridable f (overrideWith newArgs));
      # Change the result of the function call by applying g to it
      overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs;
    in
      if builtins.isAttrs result then
      if isAttrs result then
        result // {
          override = overrideArgs;
          overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv);
          ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv:
            overrideResult (x: x.overrideAttrs fdrv);
        }
      else if lib.isFunction result then
      else if isFunction result then
        # Transform the result into a functor while propagating its arguments
        lib.setFunctionArgs result (lib.functionArgs result) // {
        setFunctionArgs result (functionArgs result) // {
          override = overrideArgs;
        }
      else result);
@@ -140,39 +151,39 @@ rec {
  */
  callPackageWith = autoArgs: fn: args:
    let
      f = if lib.isFunction fn then fn else import fn;
      fargs = lib.functionArgs f;
      f = if isFunction fn then fn else import fn;
      fargs = functionArgs f;

      # All arguments that will be passed to the function
      # This includes automatic ones and ones passed explicitly
      allArgs = builtins.intersectAttrs fargs autoArgs // args;
      allArgs = intersectAttrs fargs autoArgs // args;

      # a list of argument names that the function requires, but
      # wouldn't be passed to it
      missingArgs = lib.attrNames
      missingArgs =
        # Filter out arguments that have a default value
        (lib.filterAttrs (name: value: ! value)
        (filterAttrs (name: value: ! value)
        # Filter out arguments that would be passed
        (removeAttrs fargs (lib.attrNames allArgs)));
        (removeAttrs fargs (attrNames allArgs)));

      # Get a list of suggested argument names for a given missing one
      getSuggestions = arg: lib.pipe (autoArgs // args) [
        lib.attrNames
      getSuggestions = arg: pipe (autoArgs // args) [
        attrNames
        # Only use ones that are at most 2 edits away. While mork would work,
        # levenshteinAtMost is only fast for 2 or less.
        (lib.filter (lib.strings.levenshteinAtMost 2 arg))
        (filter (levenshteinAtMost 2 arg))
        # Put strings with shorter distance first
        (lib.sort (x: y: lib.strings.levenshtein x arg < lib.strings.levenshtein y arg))
        (sort (x: y: levenshtein x arg < levenshtein y arg))
        # Only take the first couple results
        (lib.take 3)
        (take 3)
        # Quote all entries
        (map (x: "\"" + x + "\""))
      ];

      prettySuggestions = suggestions:
        if suggestions == [] then ""
        else if lib.length suggestions == 1 then ", did you mean ${lib.elemAt suggestions 0}?"
        else ", did you mean ${lib.concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?";
        else if length suggestions == 1 then ", did you mean ${elemAt suggestions 0}?"
        else ", did you mean ${concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?";

      errorForArg = arg:
        let
@@ -180,16 +191,16 @@ rec {
          # loc' can be removed once lib/minver.nix is >2.3.4, since that includes
          # https://github.com/NixOS/nix/pull/3468 which makes loc be non-null
          loc' = if loc != null then loc.file + ":" + toString loc.line
            else if ! lib.isFunction fn then
              toString fn + lib.optionalString (lib.sources.pathIsDirectory fn) "/default.nix"
            else if ! isFunction fn then
              toString fn + optionalString (pathIsDirectory fn) "/default.nix"
            else "<unknown location>";
        in "Function called without required argument \"${arg}\" at "
        + "${loc'}${prettySuggestions (getSuggestions arg)}";

      # Only show the error for the first missing argument
      error = errorForArg (lib.head missingArgs);
      error = errorForArg missingArgs.${head (attrNames missingArgs)};

    in if missingArgs == [] then makeOverridable f allArgs else abort error;
    in if missingArgs == {} then makeOverridable f allArgs else abort error;


  /* Like callPackage, but for a function that returns an attribute
@@ -201,17 +212,17 @@ rec {
  */
  callPackagesWith = autoArgs: fn: args:
    let
      f = if lib.isFunction fn then fn else import fn;
      auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs;
      f = if isFunction fn then fn else import fn;
      auto = intersectAttrs (functionArgs f) autoArgs;
      origArgs = auto // args;
      pkgs = f origArgs;
      mkAttrOverridable = name: _: makeOverridable (newArgs: (f newArgs).${name}) origArgs;
    in
      if lib.isDerivation pkgs then throw
      if isDerivation pkgs then throw
        ("function `callPackages` was called on a *single* derivation "
          + ''"${pkgs.name or "<unknown-name>"}";''
          + " did you mean to use `callPackage` instead?")
      else lib.mapAttrs mkAttrOverridable pkgs;
      else mapAttrs mkAttrOverridable pkgs;


  /* Add attributes to each output of a derivation without changing
@@ -224,7 +235,7 @@ rec {
    let
      outputs = drv.outputs or [ "out" ];

      commonAttrs = drv // (builtins.listToAttrs outputsList) //
      commonAttrs = drv // (listToAttrs outputsList) //
        ({ all = map (x: x.value) outputsList; }) // passthru;

      outputToAttrListElement = outputName:
@@ -238,7 +249,7 @@ rec {
            # TODO: give the derivation control over the outputs.
            #       `overrideAttrs` may not be the only attribute that needs
            #       updating when switching outputs.
            lib.optionalAttrs (passthru?overrideAttrs) {
            optionalAttrs (passthru?overrideAttrs) {
              # TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing.
              overrideAttrs = f: (passthru.overrideAttrs f).${outputName};
            };
@@ -264,11 +275,11 @@ rec {

      commonAttrs =
        { inherit (drv) name system meta; inherit outputs; }
        // lib.optionalAttrs (drv._hydraAggregate or false) {
        // optionalAttrs (drv._hydraAggregate or false) {
          _hydraAggregate = true;
          constituents = map hydraJob (lib.flatten drv.constituents);
          constituents = map hydraJob (flatten drv.constituents);
        }
        // (lib.listToAttrs outputsList);
        // (listToAttrs outputsList);

      makeOutput = outputName:
        let output = drv.${outputName}; in
@@ -283,9 +294,9 @@ rec {

      outputsList = map makeOutput outputs;

      drv' = (lib.head outputsList).value;
      drv' = (head outputsList).value;
    in if drv == null then null else
      lib.deepSeq drv' drv';
      deepSeq drv' drv';

  /* Make a set of packages with a common scope. All packages called
     with the provided `callPackage` will be evaluated with the same
@@ -304,11 +315,11 @@ rec {
    let self = f self // {
          newScope = scope: newScope (self // scope);
          callPackage = self.newScope {};
          overrideScope = g: makeScope newScope (lib.fixedPoints.extends g f);
          overrideScope = g: makeScope newScope (extends g f);
          # Remove after 24.11 is released.
          overrideScope' = g: lib.warnIf (lib.isInOldestRelease 2311)
          overrideScope' = g: warnIf (isInOldestRelease 2311)
            "`overrideScope'` (from `lib.makeScope`) has been renamed to `overrideScope`."
            (makeScope newScope (lib.fixedPoints.extends g f));
            (makeScope newScope (extends g f));
          packages = f;
        };
    in self;
@@ -384,7 +395,7 @@ rec {
        overrideScope = g: (makeScopeWithSplicing'
          { inherit splicePackages newScope; }
          { inherit otherSplices keep extra;
            f = lib.fixedPoints.extends g f;
            f = extends g f;
          });
        packages = f;
      };