image of me

A Practical Nix Reference

2020-08-10

I have trouble remembering the exact syntax needed to do stuff in nix. Here's a little reference on how to do those things (WITH A STRONG FOCUS ON PYTHON).

Making a shell

This is the base for a nix-shell file.

let pkgs = import <nixpkgs> {};
in
pkgs.mkShell rec {
  name = "SOME SHELL NAME";
  buildInputs = [];
}

With python...

Just add the expression

(pkgs.python3.withPackages (ps: with ps; [PYTHON PACKAGES HERE]))

into the buildInputs list in mkShell.

Creating a nix derivation for a python package

The file is a function which creates a python package derivation.

{buildPythonPackage, *other libraries required to build the package, eg. numpy, matplotlib...*}:
buildPythonPackage rec {
  pname = "MYPACKAGENAME";
  version = "0.IDUNNO.1";
  src = *WHATEVER THE SOURCE IS*;

  # Inputs required to run the tests
  checkInputs = [];
  # Compile-time input requirements
  nativeBuildInputs = [];
  buildInputs = [];
  # Run-time input requirements eg. scipy needs numpy in the propagatedBuildInputs to run
  propagatedBuildInputs = [];

  # Bool for whether to run tests
  doCheck = true;

  meta = {
    description = "Some description for the package";
    homepage = "www.somewebsite.com";
  };
}

Pypi source

If you're taking the source from Pypi add the function fetchPypi to the function arguments and set the src variable to:

src = fetchPypi {
  inherit pname version;
  sha256 = "the sha256 value from the pypi package page";
}

Github source

If the source is in a git repo, use the builtins.fetchGit function:

src = builtins.fetchGit {
  url = "the git url";
  rev = "the commit revision";
  # ref allows you to fetch from a branch
  ref = "my-branch";
};

Local source

If it's a local source, just use the file (without string quotes):

src = ./my_file_src

If it's compressed or something, nix can handle that too.

Adding the derivation as an overlay

If you don't want to have these derivations in individual projects, they can be added into your <nixpkgs> with an overlay. I like to have the derivations inside the folder ~/.config/nixpkgs/pkgs/, and you should put your overlays in the folder ~/.config/nixpkgs/overlays/. Every overlay you add in that folder is automatically applied to your available <nixpkgs>.

An example overlay to add a python package is:

self: super:
{
  python3 = super.python3.override {
    packageOverrides = python-self: python-super: {
      new_python_package = super.callPackage ../pkgs/new_python_package.nix { };
    };
  };
}

Then new_python_package will be available in the python3Packages.

This is using the pkgs.callPackage function to fill in the derivation arguments. There's a nix pill on how this function works here. It just takes the intersection between all of the package repository's derivations and the arguments and then fills in the arguments with this.

Adding a local package to your nix-shell

Just use the pkgs.callPackage function to load the locally defined package like this:

let
  pkgs = import <nixpkgs> {};
  test_package = pkgs.callPackage ./test_package.nix { };
in pkgs.mkShell rec {
  name = "Cool guy shell";
  buildInputs = [test_package];
}

If the imported package contains a python package that uses buildPythonPackage and some other stuff, they probably won't be in scope at the <nixpkgs>.callPackage function. Luckily there's a python specific scope in the <nixpkgs>.python3Packages.callPackage version. There's a few different callPackages like this that you can use.