Search code examples
nixnixos

composing local nix packages


I want to run a nix-shell with the following packages installed:

  • aspell
  • aspellDicts.en
  • hello

I can't simply do: nix-shell -p aspell aspellDicts.en hello --pure as this will not correctly install the aspell dictionaries. Nix provides a aspellWithDict function that can be used to build aspell with dictionaries:

nix-build -E 'with import <nixpkgs> {}; aspellWithDicts (d: [d.en])'

I want to use the result of this build as a dependency in another local package (foo). This is how I'm currently achieving this:

./pkgs/aspell-with-dicts/default.nix:

with import <nixpkgs> {};

aspellWithDicts (d: [d.en])

./pkgs/foo/default.nix:

{stdenv, aspellWithDicts, hello}:

stdenv.mkDerivation rec {
    name = "foo";
    buildInputs = [ aspellWithDicts hello ];
}

./custom-packages.nix:

{ system ? builtins.currentSystem }:

let
  pkgs = import <nixpkgs> { inherit system; };

in

rec {
  aspellWithDicts = import ./pkgs/aspell-with-dicts;

  foo = import ./pkgs/foo {
    aspellWithDicts = aspellWithDicts;
    hello = pkgs.hello;
    stdenv = pkgs.stdenv;
  };
}

Running the shell works as expected: nix-shell ./custom-packages.nix -A foo --pure

So my solution works, but could this outcome be achieved in a more succinct idiomatic way?


Solution

  • To make this code more idiomatic, I have the following suggestions:

    callPackage

    Use the pkgs.callPackage function. It will take care of passing the arguments that your derivation needs. This is why many files in NixPkgs look like { dependency, ...}: something. The first argument is the function you want to inject dependencies into and the second argument is an attribute set that you can use to pass some dependencies manually.

    By using callPackage you do not need to import <nixpkgs> {}, so your code will be easier to use in new contexts <nixpkgs> can't be used and it will evaluate a bit faster because it has to evaluate the NixPkgs fix-point only once.

    (Of course you have to import <nixpkgs> once to get started, but after that, there should be no need.)

    with

    In pkgs/aspell-with-dicts/default.nix you use a with keyword, which is ok, but in this case it does not really add value. I prefer to refer to variables explicitly, so prefer to read pkgs.something when it's used once or twice, or inherit (pkgs) something if it's use more often. This way the reader can easily determine where a variable comes from.

    I do use it when experimenting with unfamiliar packages or functions, because maintenance is not an issue then.

    pkgs/aspell-with-dicts/default.nix

    Unless you expect that your instantiation of aspell is something you want to reuse, it's probably easier to just construct it where you use it.

    If you do expect to reuse a specific configuration of a package, you might want to make it a first class package by constructing it in an overlay.


    That's it. I think the most important point is avoiding <nixpkgs> and apart from that it's already pretty idiomatic.

    I don't know what your mysterious foo is, but if it's open source, please consider upstreaming it into NixPkgs. Nix has a very welcoming community in my experience.