One presentation at NixCon 2017 that especially drew my attention was Nicolas Pierron‘s talk about Nixpkgs overlays (video, slides). I’d like to give a quick summary here for future reference. All the credits go to Nicolas, of course.
What are overlays?
Overlays are the new standard mechanism to customize nixpkgs. They replace constructs like packageOverride and overridePackages.
How do I use overlays?
Put your overlays into ~/.config/nixpkgs/overlays. All overlays share a basic structure:
self: super: { … }
Arguments:
- self is the result of the fix point calculation. Use it to access packages which could be modified somewhere else in the overlay stack.
- super one overlay down in the stack (and base nixpkgs for the first overlay). Use it to access the package recipes you want to customize, and for library functions.
Good examples
Add default proxy to Google Chrome
self: super: { google-chrome = super.google-chrome.override { commandLineArgs = "--proxy-server='https=127.0.0.1:3128;http=127.0.0.1:3128'"; }; }
From Alexandre Peyroux.
Fudge Nix store location
self: super: { nix = super.nix.override { storeDir = "${<nix-dir>}/store"; stateDir = "${<nix-dir>}/var"; }; }
From Yann Hodique.
Add custom package
self: super: { VidyoDesktop = super.callPackage ./pkgs/VidyoDesktop { }; }
From Mozilla.
Bad examples
Recursive attrset
self: super: rec { fakeClosure = self.writeText "fake-closure.nix" '' … ''; fakeConfig = self.writeText "fake-config.nix" '' (import ${fakeClosure} {}).config.nixpkgs.config ''; }
Two issues:
- Use
super
to access library functions. - Overlays should not be recursive. Use
self
.
Surplus arguments
{ python }: self: super: { "execnet" = python.overrideDerivation super."execnet" (old: { buildInputs = old.buildInputs ++ [ self."setuptools-scm" ]; }); }
The issue:
- Overlays should depend just on
self
andsuper
in order to be composeable.
Awkward nixpkgs import
{ pkgs ? import <nixpkgs> {} }: let projectOverlay = self: super: { customPythonPackages = (import ./requirements.nix { inherit pkgs; }).packages; }; in import pkgs.path { overlays = [ projectOverlay ]; }
Two issues:
- Unnecessary double-import of nixpkgs. This might break cross-system builds.
- requirements.nix should use
self
notpkgs
.
Improved version
shell.nix:
{ pkgsPath ? <nixpkgs> }: import pkgsPath { overlays = [ import ./default.nix; ]; }
default.nix:
self: super: { customPythonPackages = (import ./requirements.nix { inherit self; }).packages; }
Incorrectly referenced dependencies
self: super: let inherit (super) callPackage; in { radare2 = callPackage ./pkgs/radare2 { inherit (super.gnome2) vte; lua = super.lua5; }; }
The issue:
- Other packages should be taken from
self
notsuper
. This way they
can be overridden by other overlays.
Overridden attrset
self: super: { lib = { firefoxVersion = … ; }; latest = { firefox-nightly-bin = … ; firefox-beta-bin = … ; firefox-bin = … ; firefox-esr-bin = … ; }; }
The issue:
- Other attributes present in
lib
andlatest
from down the overlay stack are
erased.
Improved version
Always extend attrsets in overlays:
self: super: { lib = (super.lib or {}) // { firefoxVersion = … ; }; latest = (super.latest or {}) // { … }; }
Summary
I hope this condensed guide helps you to write better overlays. For in-depth discussion, please go watch Nicolas’ talk and read the Nixpkgs manual. Many thanks to Nicolas for putting this together!
Cover image: ⓒ 2009 studio tdes / Flickr / CC-BY-2.0
Thanks, I didn’t really understand overlays before but reading this post makes it much clearer!
LikeLike
I believe it would be great to add such section into the manual https://nixos.org/nixpkgs/manual/#chap-overlays
Readers who don’t want these may easily skip the section, but I think there are many users that will like that.
LikeLike
Double imports of nixpkgs is required if you want to allow your derivations to layer overrides one by one. I could have derivations that took the nixpkgs function, but I needed to be able to take a pkgs, override it, and return back the overridden pkgs.
LikeLike
Minor typo, but `overlays = [ import ./default.nix; ];` is invalid. First there’s no need for a semicolon inside the list, and secondly `[ import ./default.nix ]` will be treated as a list of two elements, the `import` function and the path `./default.nix`.
This should probably read `overlays = [ (import ./default.nix) ];` or, since `default.nix` is the default, `overlays = [ (import ./.) ];`
LikeLike