diff --git a/lib/customisation.nix b/lib/customisation.nix index 42d711cf5fb9..cb3a4b561151 100644 --- a/lib/customisation.nix +++ b/lib/customisation.nix @@ -213,7 +213,14 @@ rec { outputSpecified = true; drvPath = assert condition; drv.${outputName}.drvPath; outPath = assert condition; drv.${outputName}.outPath; - }; + } // + # 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) { + # TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing. + overrideAttrs = f: (passthru.overrideAttrs f).${outputName}; + }; }; outputsList = map outputToAttrListElement outputs; diff --git a/pkgs/development/interpreters/python/python-packages-base.nix b/pkgs/development/interpreters/python/python-packages-base.nix index d5b02223fd6c..de38fb136320 100644 --- a/pkgs/development/interpreters/python/python-packages-base.nix +++ b/pkgs/development/interpreters/python/python-packages-base.nix @@ -16,17 +16,22 @@ let # This function introduces `overridePythonAttrs` and it overrides the call to `buildPythonPackage`. makeOverridablePythonPackage = f: origArgs: let - ff = f origArgs; - overrideWith = newArgs: origArgs // (if pkgs.lib.isFunction newArgs then newArgs origArgs else newArgs); + args = lib.fix (lib.extends + (_: previousAttrs: { + passthru = (previousAttrs.passthru or { }) // { + overridePythonAttrs = newArgs: makeOverridablePythonPackage f (overrideWith newArgs); + }; + }) + (_: origArgs)); + result = f args; + overrideWith = newArgs: args // (if pkgs.lib.isFunction newArgs then newArgs args else newArgs); in - if builtins.isAttrs ff then (ff // { + if builtins.isAttrs result then result + else if builtins.isFunction result then { overridePythonAttrs = newArgs: makeOverridablePythonPackage f (overrideWith newArgs); - }) - else if builtins.isFunction ff then { - overridePythonAttrs = newArgs: makeOverridablePythonPackage f (overrideWith newArgs); - __functor = self: ff; + __functor = self: result; } - else ff; + else result; buildPythonPackage = makeOverridablePythonPackage (lib.makeOverridable (callPackage ./mk-python-derivation.nix { inherit namePrefix; # We want Python libraries to be named like e.g. "python3.6-${name}" diff --git a/pkgs/stdenv/generic/make-derivation.nix b/pkgs/stdenv/generic/make-derivation.nix index c53eed3dfa12..5d272d9eb0c8 100644 --- a/pkgs/stdenv/generic/make-derivation.nix +++ b/pkgs/stdenv/generic/make-derivation.nix @@ -18,33 +18,34 @@ let # separate lines, because Nix would only show the last line of the comment. # An infinite recursion here can be caused by having the attribute names of expression `e` in `.overrideAttrs(finalAttrs: previousAttrs: e)` depend on `finalAttrs`. Only the attribute values of `e` can depend on `finalAttrs`. - args = rattrs (args // { inherit finalPackage; }); + args = rattrs (args // { inherit finalPackage overrideAttrs; }); # ^^^^ - finalPackage = - mkDerivationSimple - (f0: - let - f = self: super: - # Convert f0 to an overlay. Legacy is: - # overrideAttrs (super: {}) - # We want to introduce self. We follow the convention of overlays: - # overrideAttrs (self: super: {}) - # Which means the first parameter can be either self or super. - # This is surprising, but far better than the confusion that would - # arise from flipping an overlay's parameters in some cases. - let x = f0 super; - in - if builtins.isFunction x - then - # Can't reuse `x`, because `self` comes first. - # Looks inefficient, but `f0 super` was a cheap thunk. - f0 self super - else x; + overrideAttrs = f0: + let + f = self: super: + # Convert f0 to an overlay. Legacy is: + # overrideAttrs (super: {}) + # We want to introduce self. We follow the convention of overlays: + # overrideAttrs (self: super: {}) + # Which means the first parameter can be either self or super. + # This is surprising, but far better than the confusion that would + # arise from flipping an overlay's parameters in some cases. + let x = f0 super; in - makeDerivationExtensible - (self: let super = rattrs self; in super // f self super)) - args; + if builtins.isFunction x + then + # Can't reuse `x`, because `self` comes first. + # Looks inefficient, but `f0 super` was a cheap thunk. + f0 self super + else x; + in + makeDerivationExtensible + (self: let super = rattrs self; in super // f self super); + + finalPackage = + mkDerivationSimple overrideAttrs args; + in finalPackage; # makeDerivationExtensibleConst == makeDerivationExtensible (_: attrs), diff --git a/pkgs/test/default.nix b/pkgs/test/default.nix index 39039c5950e4..71d065179d17 100644 --- a/pkgs/test/default.nix +++ b/pkgs/test/default.nix @@ -61,6 +61,8 @@ with pkgs; nixos-functions = callPackage ./nixos-functions {}; + overriding = callPackage ./overriding.nix { }; + patch-shebangs = callPackage ./patch-shebangs {}; texlive = callPackage ./texlive {}; diff --git a/pkgs/test/overriding.nix b/pkgs/test/overriding.nix new file mode 100644 index 000000000000..edc1b27cf4f1 --- /dev/null +++ b/pkgs/test/overriding.nix @@ -0,0 +1,56 @@ +{ lib, pkgs, stdenvNoCC }: + +let + tests = + let + p = pkgs.python3Packages.xpybutil.overridePythonAttrs (_: { dontWrapPythonPrograms = true; }); + in + [ + ({ + name = "overridePythonAttrs"; + expr = !lib.hasInfix "wrapPythonPrograms" p.postFixup; + expected = true; + }) + ({ + name = "repeatedOverrides-pname"; + expr = repeatedOverrides.pname == "a-better-hello-with-blackjack"; + expected = true; + }) + ({ + name = "repeatedOverrides-entangled-pname"; + expr = repeatedOverrides.entangled.pname == "a-better-figlet-with-blackjack"; + expected = true; + }) + ]; + + addEntangled = origOverrideAttrs: f: + origOverrideAttrs ( + lib.composeExtensions f (self: super: { + passthru = super.passthru // { + entangled = super.passthru.entangled.overrideAttrs f; + overrideAttrs = addEntangled self.overrideAttrs; + }; + }) + ); + + entangle = pkg1: pkg2: pkg1.overrideAttrs (self: super: { + passthru = super.passthru // { + entangled = pkg2; + overrideAttrs = addEntangled self.overrideAttrs; + }; + }); + + example = entangle pkgs.hello pkgs.figlet; + + overrides1 = example.overrideAttrs (_: super: { pname = "a-better-${super.pname}"; }); + + repeatedOverrides = overrides1.overrideAttrs (_: super: { pname = "${super.pname}-with-blackjack"; }); +in + +stdenvNoCC.mkDerivation { + name = "test-overriding"; + passthru = { inherit tests; }; + buildCommand = '' + touch $out + '' + lib.concatMapStringsSep "\n" (t: "([[ ${lib.boolToString t.expr} == ${lib.boolToString t.expected} ]] && echo '${t.name} success') || (echo '${t.name} fail' && exit 1)") tests; +}