1
0
Fork 0
mirror of https://github.com/NixOS/nix-pills synced 2024-09-19 04:00:13 -04:00
nix-pills/pills/17-nixpkgs-overriding-packages.xml
Samuel Leathers 8015a74ea2 port pill #17
2017-08-16 14:25:59 -04:00

142 lines
8.2 KiB
XML

<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="nixpkgs-overriding-packages">
<title>nixpkgs overriding packages</title>
<para>
Welcome to the 17th Nix pill. In the previous <link linkend="nixpkgs-parameters">16th</link> pill we have started to dive into the <link xlink:href="http://github.com/NixOS/nixpkgs">nixpkgs repository</link>. <literal>Nixpkgs</literal> is a function, and we've looked at some parameters like <literal>system</literal> and <literal>config</literal>.
</para>
<para>
Today we'll talk about a special attribute: <literal>config.packageOverrides</literal>. Overriding packages in a set with fixed point can be considered another design pattern in nixpkgs.
</para>
<section>
<title>Overriding a package</title>
<para>
I recall the override design pattern from the <link linkend="override-design-pattern">nix pill 14</link>. Instad of calling a function with parameters directly, we make the call (function + parameters) overridable.
</para>
<para>
We put the override function in the returned attribute set of the original function call.
</para>
<para>
Take for example <package>graphviz</package>. It has an input parameter <package>xlibs</package>. If it's null, then <package>graphviz</package> will build without X support.
</para>
<screen><xi:include href="./17/graphviz-override.txt" parse="text" /></screen>
<para>
This will build <package>graphviz</package> without X support, it's as simple as that.
</para>
<para>
However let's say a package <literal>P</literal> depends on <package>graphviz</package>, how do we make <literal>P</literal> depend on the new <package>graphviz</package> without X support?
</para>
</section>
<section>
<title>In an imperative world...</title>
<para>
...you could do something like this:
</para>
<screen><xi:include href="./17/p-graphviz-override.txt" parse="text" /></screen>
<para>
Given <literal>pkgs.P</literal> depends on <literal>pkgs.graphviz</literal>, it's easy to build <literal>P</literal> with the replaced <package>graphviz</package>. On a pure functional language it's not that easy because you can assign to variables only once.
</para>
</section>
<section>
<title>Fixed point</title>
<para>
The fixed point with lazy evaluation is crippling but about necessary in a language like Nix. It lets us achieve something similar to what we'd do imperatively.
</para>
<para>
Follows the definition of fixed point in <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/lib/trivial.nix#L21">nixpkgs</link>:
</para>
<screen><xi:include href="./17/fix-function.txt" parse="text" /></screen>
<para>
It's a function that accepts a function <literal>f</literal>, calls <literal>f result</literal> on the result just returned by <literal>f result</literal> and returns it. In other words it's <literal>f(f(f(....</literal>
</para>
<para>
At first sight, it's an infinite loop. With lazy evaluation it isn't, because the call is done only when needed.
</para>
<screen><xi:include href="./17/fix-pkgs-function.txt" parse="text" /></screen>
<para>
Without the <literal>rec</literal> keyword, we were able to refer to <literal>a</literal> and <literal>b</literal> of the same set.
<itemizedlist>
<listitem><para>First <literal>pkgs</literal> gets called with an unevaluated thunk <literal>(pkgs(pkgs(...)</literal></para></listitem>
<listitem><para>To set the value of <literal>c</literal> then <literal>self.a</literal> and <literal>self.b</literal> are evaluated.</para></listitem>
<listitem><para>The <literal>pkgs</literal> function gets called again to get the value of <literal>a</literal> and <literal>b</literal>.</para></listitem>
</itemizedlist>
</para>
<para>
The trick is that <literal>c</literal> is not needed to be evaluated in the inner call, thus it doesn't go in an infinite loop.
</para>
<para>
Won't go further with the explanation here. A good post about fixed point and Nix can be <link xlink:href="http://r6.ca/blog/20140422T142911Z.html">found here</link>.
</para>
<section>
<title>Overriding a set with fixed point</title>
<para>
Given that <literal>self.a</literal> and <literal>self.b</literal> refer to the passed set and not to the literal set in the function, we're able to override both <literal>a</literal> and <literal>b</literal> and get a new value for <literal>c</literal>:
</para>
<screen><xi:include href="./17/newpkgs-override-set.txt" parse="text" /></screen>
<para>
In the first case we computed pkgs with the overrides, in the second case we also included the overriden attributes in the result.
</para>
</section>
</section>
<section>
<title>Overriding nixpkgs packages</title>
<para>
We've seen how to override attributes in a set such that they get recursively picked by dependant attributes. This approach can be used for derivations too, after all <literal>nixpkgs</literal> is a giant set of attributes that depend on each other.
</para>
<para>
To do this, <literal>nixpkgs</literal> offers <literal>config.packageOverrides</literal>. So <literal>nixpkgs</literal> returns a fixed point of the package set, and <literal>packageOverrides</literal> is used to inject the overrides.
</para>
<para>
Create a <filename>config.nix</filename> file like this somewhere:
</para>
<screen><xi:include href="./17/config-nix.txt" parse="text" /></screen>
<para>
Now we can build e.g. <package>asciidocFull</package> and it will automatically use the overridden <package>graphviz</package>:
</para>
<screen><xi:include href="./17/build-asciidoc-graphviz-override.txt" parse="text" /></screen>
<para>
Note how we pass the <literal>config</literal> with <literal>packageOverrides</literal> when importing <literal>nixpkgs</literal>. Then <literal>pkgs.asciidocFull</literal> is a derivation that has <package>graphviz</package> input (<literal>pkgs.asciidoc</literal> is the lighter version and doesn't use <package>graphviz</package> at all).
</para>
<para>
Since there's no version of <package>asciidoc</package> with <package>graphviz</package> without X support in the binary cache, Nix will recompile the needed stuff for you.
</para>
</section>
<section>
<title>The ~/.nixpkgs/config.nix file</title>
<para>
In the previous pill we already talked about this file. The above <filename>config.nix</filename> that we just wrote could be the content of <filename>~/.nixpkgs/config.nix</filename>.
</para>
<para>
Instead of passing it explicitly whenever we import <literal>nixpkgs</literal>, it will be automatically <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/pkgs/top-level/all-packages.nix#L54">imported by nixpkgs</link>.
</para>
</section>
<section>
<title>Conclusion</title>
<para>
We've learned about a new design pattern: using fixed point for overriding packages in a package set.
</para>
<para>
Whereas in an imperative setting, like with other package managers, a library is installed replacing the old version and applications will use it, in Nix it's not that straight and simple. But it's more precise.
</para>
<para>
Nix applications will depend on specific versions of libraries, hence the reason why we have to recompile <package>asciidoc</package> to use the new <package>graphviz</package> library.
</para>
<para>
The newly built <package>asciidoc</package> will depend on the new <package>graphviz</package>, and old <package>asciidoc</package> will keep using the old <package>graphviz</package> undisturbed.
</para>
</section>
<section>
<title>Next pill</title>
<para>
...we will stop diving <literal>nixpkgs</literal> for a moment and talk about store paths. How does Nix compute the path in the store where to place the result of builds? How to add files to the store for which we have an integrity hash?
</para>
</section>
</chapter>