Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions doc/functions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,71 @@
</note>
</section>

<section xml:id="sec-pkg-overrideWithScope">
<title>&lt;pkg&gt;.override</title>

<para>
The function <varname>overrideWithScope</varname> is usually available for all the
derivations in the nixpkgs expression (<varname>pkgs</varname>).
</para>

<para>
It is used to override the arguments passed to a function, by extracting the
arguments from a larger set, similar to the default argument provided by the
<varname>callPackage</varname> function.
</para>

<para>
This function is useful to deeply override a package within a set of
packages, such as making compilation stages or to build packages which rely
on having the same interpreter (i-e: python, haskell, emacs, …).
</para>

<para>
Example usages:
<programlisting>pkgs.foo.overrideWithScope pkgs.fooDependencies</programlisting>
<programlisting>import pkgs.path { overlays = [
(self: super: {
# The interpreter would be overriden in the following packages too.
myInterpreterPackages = super.lib.mapAttrs (n: v: v.overrideWithScope self.myInterpreterPackages) {
interpreter = super.interpreter.overrideDerivation (prev: { ... });
inherit (super) foo bar baz qux;
};
})
(self: super: {
# baz changes would be visible by all packages within myInterpreterPackages set.
myInterpreterPackages = super.myInterpreterPackages // {
baz = super.myInterpreterPackages.baz.override { ... };
};
})
]};</programlisting>
</para>

<para>
In the first example, <varname>pkgs.foo</varname> is the result of a
function call with some default arguments, usually a derivation. Using
<varname>pkgs.foo.overrideWithScope</varname> will call the same function
with the given new arguments. This is similar to how
<varname>pkgs.foo.override</varname> would behave, except that
<varname>overrideWithScope</varname> will not raise an error if an argument
is given, but not expected by the package.
</para>

<para>
In the second example, two overlays are added. The first overlay create a
custom version of an interpreter, and replaces it in all packages included
in the <varname>myInterpreterPackages</varname> set, as well as replacing
<varname>foo</varname> in <varname>myInterpreterPackages.bar</varname>. The
second overlay replaces <varname>baz</varname> in all packages under
<varname>myInterpreterPackages</varname>, such as
<varname>myInterpreterPackages.qux</varname>. Note, the second overlay did
not had a re-use <varname>overrideWithScope</varname> as the argument of it
in the first overlay is the fix-point result of
<varname>myInterpreterSet</varname>, which includes the custom
<varname>baz</varname>.
</para>
</section>

<section xml:id="sec-lib-makeOverridable">
<title>lib.makeOverridable</title>

Expand Down
32 changes: 20 additions & 12 deletions lib/customisation.nix
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ rec {
else { }));


/* `makeOverridable` takes a function from attribute set to attribute set and
injects `override` attibute which can be used to override arguments of
the function.
/* `makeOverridable` takes a function from attribute set to attribute set, a
list of expected arguments and injects `override`, `overrideWithScope` and
`overrideDerivation` attibutes in the result of the function which can be
used to re-call the function with an overrided set of arguments.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, just for the sake of completeness (this nitpick is so minor that it isn't even one), maybe add overrideWithScope in the output for the nix repl examples below.


nix-repl> x = {a, b}: { result = a + b; }

Expand All @@ -64,20 +65,25 @@ rec {
"<pkg>.overrideDerivation" to learn about `overrideDerivation` and caveats
related to its use.
*/
makeOverridable = f: origArgs:
makeOverridable = f: origArgs: makeOverridableWithArgs f (lib.functionArgs f) origArgs;
makeOverridableWithArgs = f: fnArgs: origArgs:
let
ff = f origArgs;
overrideWith = newArgs: origArgs // (if lib.isFunction newArgs then newArgs origArgs else newArgs);
intersectArgs = if fnArgs != {} then builtins.intersectAttrs fnArgs else (a: a);
overrideWithScope = newScope: overrideWith (intersectArgs newScope);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool, now I can finally get rid of a bit of churn in some of my Nix expressions, thanks :-)

One minor nitpick however:

If my assumption is correct that newScope should always be an attribute set (intersectAttrs would bail out if not), there is no need to go through isFunction in overrideWith above again, so removing intersectArgs and changing overrideWithScope to the following would avoid that:

newScope: origArgs // (if fnArgs != {} then builtins.intersectAttrs fnArgs newScope else newScope)

in
if builtins.isAttrs ff then (ff // {
override = newArgs: makeOverridable f (overrideWith newArgs);
override = newArgs: makeOverridableWithArgs f fnArgs (overrideWith newArgs);
overrideWithScope = newScope: makeOverridableWithArgs f fnArgs (overrideWithScope newScope);
overrideDerivation = fdrv:
makeOverridable (args: overrideDerivation (f args) fdrv) origArgs;
makeOverridableWithArgs (args: overrideDerivation (f args) fdrv) fnArgs origArgs;
${if ff ? overrideAttrs then "overrideAttrs" else null} = fdrv:
makeOverridable (args: (f args).overrideAttrs fdrv) origArgs;
makeOverridableWithArgs (args: (f args).overrideAttrs fdrv) fnArgs origArgs;
})
else if lib.isFunction ff then {
override = newArgs: makeOverridable f (overrideWith newArgs);
override = newArgs: makeOverridableWithArgs f fnArgs (overrideWith newArgs);
overrideWithScope = newScope: makeOverridableWithArgs f fnArgs (overrideWithScope newScope);
__functor = self: ff;
overrideDerivation = throw "overrideDerivation not yet supported for functors";
}
Expand Down Expand Up @@ -108,8 +114,9 @@ rec {
callPackageWith = autoArgs: fn: args:
let
f = if lib.isFunction fn then fn else import fn;
auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs;
in makeOverridable f (auto // args);
fnArgs = lib.functionArgs f;
auto = builtins.intersectAttrs fnArgs autoArgs;
in makeOverridableWithArgs f fnArgs (auto // args);


/* Like callPackage, but for a function that returns an attribute
Expand All @@ -118,10 +125,11 @@ rec {
callPackagesWith = autoArgs: fn: args:
let
f = if lib.isFunction fn then fn else import fn;
auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs;
fnArgs = lib.functionArgs f;
auto = builtins.intersectAttrs fnArgs autoArgs;
origArgs = auto // args;
pkgs = f origArgs;
mkAttrOverridable = name: pkg: makeOverridable (newArgs: (f newArgs).${name}) origArgs;
mkAttrOverridable = name: pkg: makeOverridableWithArgs (newArgs: (f newArgs).${name}) fnArgs origArgs;
in lib.mapAttrs mkAttrOverridable pkgs;


Expand Down