Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

composing local nix packages

Tags:

nix

nixos

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?

like image 955
b73 Avatar asked Dec 01 '17 10:12

b73


2 Answers

Do you need to build foo? What in foo you will use?

Assume you only want to use the shell via nix-shell and not want to build/install anything using nix-build or nix-env -i, this should works.

The following shell.nix

with import <nixpkgs> {};
with pkgs;

let
  myAspell = aspellWithDicts (d: [d.en]);
in
  stdenv.mkDerivation {
    name = "myShell";

    buildInputs = [myAspell hello];

    shellHooks = ''
      echo Im in $name.
      echo aspell is locate at ${myAspell}
      echo hello is locate at ${hello}
    '';
  }

will give you a shell with aspell and hello

$ nix-shell
Im in myShell.
aspell is locate at /nix/store/zcclppbibcg4nfkis6zqml8cnrlnx00b-aspell-env
hello is locate at /nix/store/gas2p68jqbzgb7zr96y5nc8j7nk61kkk-hello-2.10

If it is the case that foo have some code to build and install.

mkDerivation in foo/default.nix must have src field which could be src = ./.; or something like fetchurl or fetchFromGithub (see document for examples).

Then you can use callPackages or import (depends on how the nix expression was written) with foo/default.nix as argument to bring what foo provided to use in this shell.

If you try to build this shell.nix (or foo/default.nix) it will failed with missing src

$ nix-build shell.nix 
these derivations will be built:
  /nix/store/20h8cva19irq8vn39i72j8iz40ivijhr-myShell.drv
building path(s) ‘/nix/store/r1f6qpxz91h5jkj7hzrmaymmzi9h1yml-myShell’
unpacking sources
variable $src or $srcs should point to the source
builder for ‘/nix/store/20h8cva19irq8vn39i72j8iz40ivijhr-myShell.drv’ failed with exit code 1
error: build of ‘/nix/store/20h8cva19irq8vn39i72j8iz40ivijhr-myShell.drv’ failed
like image 99
wizzup Avatar answered Sep 23 '22 02:09

wizzup


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.

like image 24
Robert Hensing Avatar answered Sep 26 '22 02:09

Robert Hensing