I want to run a nix-shell with the following packages installed:
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?
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With