diff --git a/lib/customisation.nix b/lib/customisation.nix index ce00e364ba76b..d63a219e1ae9e 100644 --- a/lib/customisation.nix +++ b/lib/customisation.nix @@ -32,6 +32,8 @@ let extends toFunction id + genAttrs + subtractLists ; inherit (lib.strings) levenshtein levenshteinAtMost; @@ -194,10 +196,19 @@ rec { ); # Change the result of the function call by applying g to it overrideResult = g: makeOverridable (mirrorArgs (args: g (f args))) origArgs; + newOverriderNames = + filter (n: n != "override" && n != "overrideDerivation" && result ? ${n}) ( + subtractLists (result.__overriders or [ ]) [ "overrideAttrs" ] ++ result.__overriders or [ ] + ) + ++ [ + "override" + "overrideDerivation" + ]; in if isAttrs result then result // { + __overriders = newOverriderNames; override = overrideArgs; overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv); ${if result ? overrideAttrs then "overrideAttrs" else null} = @@ -213,10 +224,16 @@ rec { # design/tech debt issue: https://github.com/NixOS/nixpkgs/issues/273815 fdrv: overrideResult (x: x.overrideAttrs fdrv); } + // optionalAttrs (result ? __overriders) ( + genAttrs (filter ( + x: x != "override" && x != "overrideAttrs" && x != "overrideDerivation" && result ? ${x} + ) result.__overriders) (name: fdrv: overrideResult (x: x.${name} fdrv)) + ) else if isFunction result then # Transform the result into a functor while propagating its arguments setFunctionArgs result (functionArgs result) // { + __overriders = newOverriderNames; override = overrideArgs; } else @@ -639,6 +656,7 @@ rec { newScope = scope: newScope (self // scope); overrideScope = g: makeScope newScope (extends g f); packages = f; + __overriders = [ "overrideScope" ]; }; in self; @@ -757,6 +775,7 @@ rec { f = extends g f; }); packages = f; + __overriders = [ "overrideScope" ]; }; in self; diff --git a/lib/fixed-points.nix b/lib/fixed-points.nix index c80a7f72eba83..c3082a0aee980 100644 --- a/lib/fixed-points.nix +++ b/lib/fixed-points.nix @@ -454,6 +454,7 @@ rec { (rattrs self) // { ${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs); + __overriders = [ extenderName ]; } ); diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index da09797567209..d3b88ce3d8c74 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -87,6 +87,7 @@ let meta mod nameValuePair + optionalAttrs optionalDrvAttr optionAttrSetToDocList overrideExisting @@ -4810,16 +4811,19 @@ runTests { directory = ./packages-from-directory/scope; }); expected = lib.recurseIntoAttrs { + __overriders = [ "overrideScope" ]; a = "a"; b = "b"; # Note: Other files/directories in `./test-data/c/` are ignored and can be # used by `package.nix`. c = "c"; my-namespace = lib.recurseIntoAttrs { + __overriders = [ "overrideScope" ]; d = "d"; e = "e"; f = "f"; my-sub-namespace = lib.recurseIntoAttrs { + __overriders = [ "overrideScope" ]; g = "g"; h = "h"; }; @@ -4876,6 +4880,69 @@ runTests { expected = { foo = "foo-value-custom"; bar = "bar-value-custom"; + + # Test if `makeOverridable` correctly handles `result.ovrerrideScope`, + # so that `makeScope`-constructed, `callPackageWith`-called scope + # won't lose `.override` after `.overrideScope` overriding. + testMakeOverridableForCallPackageAndScope = + let + scope-orig = callPackageWith (lib // { a = 3; }) ( + { + callPackagesWith, + makeScope, + a, + }: + makeScope callPackagesWith (final: { + inherit a; + b = 5; + c = final.a * final.b; + }) + ) { }; + scope-os = scope-orig.overrideScope ( + final: previous: { + b = 7; + } + ); + scope-os-ov = scope-os.override { a = 11; }; + in + { + expr = { + scope-orig-a = scope-orig.a; + scope-orig-b = scope-orig.b; + scope-orig-c = scope-orig.c; + scope-orig-has-override = scope-orig ? override; + scope-orig-has-overrideScope = scope-orig ? overrideScope; + } + // optionalAttrs (scope-orig ? overrideScope) { + scope-os-a = scope-os.a; + scope-os-b = scope-os.b; + scope-os-c = scope-os.c; + scope-os-has-override = scope-os ? override; + scope-os-has-overrideScope = scope-os ? overrideScope; + } + // optionalAttrs (scope-os ? override) { + scope-os-ov-a = scope-os-ov.a; + scope-os-ov-b = scope-os-ov.b; + scope-os-ov-c = scope-os-ov.c; + scope-os-ov-has-override = scope-os-ov ? override; + scope-os-ov-has-overrideScope = scope-os-ov ? overrideScope; + }; + expected = rec { + scope-orig-a = 3; + scope-orig-b = 5; + scope-orig-c = scope-orig-a * scope-orig-b; + scope-orig-has-override = true; + scope-orig-has-overrideScope = true; + scope-os-a = scope-orig-a; + scope-os-b = 7; + scope-os-c = scope-os-a * scope-os-b; + scope-os-has-override = true; + scope-os-has-overrideScope = true; + scope-os-ov-a = 11; + scope-os-ov-b = scope-os-b; + scope-os-ov-c = scope-os-ov-a * scope-os-ov-b; + scope-os-ov-has-override = true; + scope-os-ov-has-overrideScope = true; }; };