How is fixed point used for overriding nixpkgs packages?

151 Views Asked by At

https://nixos.org/guides/nix-pills/nixpkgs-overriding-packages.html says

17.3. Fixed point

...

nix-repl> fix = f: let result = f result; in result
nix-repl> pkgs = self: { a = 3; b = 4; c = self.a+self.b; }
nix-repl> fix pkgs
{ a = 3; b = 4; c = 7; }

...

17.3.1. Overriding a set with fixed point

Given that self.a and self.b refer to the passed set and not to the literal set in the function, we're able to override both a and b and get a new value for c :

nix-repl> overrides = { a = 1; b = 2; }
nix-repl> let newpkgs = pkgs (newpkgs // overrides); in newpkgs
{ a = 3; b = 4; c = 3; }
nix-repl> let newpkgs = pkgs (newpkgs // overrides); in newpkgs // overrides
{ a = 1; b = 2; c = 3; }

In the first case we computed pkgs with the overrides, in the second case we also included the overriden attributes in the result.

17.4. Overriding nixpkgs packages

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 nixpkgs is a giant set of attributes that depend on each other.

To do this, nixpkgs offers config.packageOverrides . So nixpkgs returns a fixed point of the package set, and packageOverrides is used to inject the overrides.

Create a config.nix file like this somewhere:

{
  packageOverrides = pkgs: {
    graphviz = pkgs.graphviz.override { withXorg = false; };
  };
}

Now we can build e.g. asciidoc-full and it will automatically use the overridden graphviz:

nix-repl> pkgs = import <nixpkgs> { config = import ./config.nix; }
nix-repl> :b pkgs.asciidoc-full

Note how we pass the config with packageOverrides when importing nixpkgs . Then pkgs.asciidoc-full is a derivation that has graphviz input ( pkgs.asciidoc is the lighter version and doesn't use graphviz at all).

Since there's no version of asciidoc with graphviz without X support in the binary cache, Nix will recompile the needed stuff for you.

17.6. Conclusion

Nix applications will depend on specific versions of libraries, hence the reason why we have to recompile asciidoc to use the new graphviz library.

The newly built asciidoc will depend on the new graphviz, and old asciidoc will keep using the old graphviz undisturbed.

What does it mean by "override attributes in a set such that they get recursively picked by dependant attributes"? Could you explain that in terms of the example in 17.3.1?

What does it mean by "nixpkgs returns a fixed point of the package set"?

  • Isn't a fixed point something belonging to a function? How is the package set a function?

  • Why does nixpkgs return a fixed point? Is nixpkgs something similar to the example let newpkgs = pkgs (newpkgs // overrides); in newpkgs in 17.3.1.?

What does it mean by "packageOverrides is used to inject the overrides"?

Why "Then pkgs.asciidoc-full is a derivation that has graphviz input"?

Why "The newly built asciidoc will depend on the new graphviz", and "old asciidoc will keep using the old graphviz undisturbed"?

1

There are 1 best solutions below

2
David Grayson On

First question

The term "dependent attribute" does not have a particularly strict or standard definition. In this context it means an attribute in a set whose value depends on one or more of the other values in the set. Here is the pkgs function from section 17.3 of that document:

pkgs = self: { a = 3; b = 4; c = self.a + self.b; }

Since that attribute c is defined to be the sum of a and b, you could say c is a dependent attribute.

When we have our final set, we could override the value of a using the // operator in Nix, but that would only change a without changing any of the attributes that depend on it, like c.

What we would like to do is find a way to override a in such a way that c still is a + b in the final set. And furthermore, any attribute that depends on c should use the new value of c, and so on. That's why the word "recursively" was used in that sentence you're asking about. We want to find a way to "override attributes in a set such that they get recursively picked by dependent attributes".

This is achieved at the end of section 17.3.1. Note that a and b were successfully changed to 1 and 2, and these overridden new values were used to recompute c (1+2 = 3).

Second question

The sentence "nixpkgs returns a fixed point of the package set" is an example of hasty, inaccurate writing. There is no such thing as a "fixed point" of a set, because a "fixed point" is a property of a function, not a set. I will rephrase what the author was trying to say. nixpkgs is a Nix function that you can get by writing import <nixpkgs> or similar. When you pass a set to this function with your configuration parameters, the function returns a set of packages, usually called pkgs.

The packages set was generated in a special way to help you override parts of it. nixpkgs makes a big complicated function that takes a packages set, modifies it, and returns a new package set. Then nixpkgs finds the fix point of that function, and returns it. Somewhere in nixpkgs there should be some code that will be similar to the last line of section 17.3.1 (let newpkgs = pkgs (newpkgs // overrides); in newpkgs // overrides). That code computes the fix point of a function that takes a set of packages newpkgs and then applies the user-specified overrides and applies the original pkgs function to it.

Third question

I know the passive voice can be confusing in the sentence "packageOverrides is used to inject the overrides", but you just have to read the full context and then it makes sense. When you call nixpkgs, you can pass an argument named config to it, which should be a set. The config set can contain an attribute named packageOverrides that lets you tell nixpkgs how you want to override the packages. There is nothing deep here, this part of the document is just defining the API that allows you to tell Nixpkgs what you want it to do.

There is nothing particularly special or meaningful about the word "inject" here, but maybe that word was used because your function will be integrated into the large complicated function I talked about above, which nixpkgs will compute the fixed point of.

Fourth question

You're asking why "pkgs.asciidoc-full is a derivation that has graphviz input".

Well, a derivation is a piece of software built by Nix. A derivation can depend on other derivations. For instance, an application can depend on a library to do some computations or generate some graphics. Apparently asciidoc-full depends on graphviz. You can see the definition of the asciidoc package here:

https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/typesetting/asciidoc/default.nix

Note that it is a big function, and graphviz is one of the arguments to it.

Fifth question

Suppose you had previously built asciidoc using Nix. Maybe you installed it in your user environment, or maybe you just started a shell with nix-shell -p asciidoc. We will call this the old asciidoc.

Then later you make a new Nix expression where you are overriding the definition of graphviz in the package set. You can build asciidoc using this new Nix expression and you will get a new piece of software we can call new asciidoc. This new asciidoc uses your modified version of graphviz, and the old asciidoc will keep using the normal version of graphviz. Each version of graphviz you made has a unique path in the Nix store, and each version of asciidoc you made has a reference to that path, ensuring that it loads the same version of graphviz that it was built with. That is one of great parts of Nix compared to a lot of other package management systems: it is easy and natural to have multiple versions of a library installed at once, being used by different pieces of software.