I wrote a very simple default.nix file with which I should be able to build the gnu hello package (similiar to nix-pills).
But now i've come across an error:
[jane@nixos:~/graphviz]$ nix-build -A hello
error: undefined variable 'pkgs' at /home/jane/graphviz/default.nix:3:47
this is the source code :
[jane@nixos:~/graphviz]$ cat default.nix
{
pkgs = import <nixpkgs> {};
mkDerivation = import ./autotools.nix pkgs;
hello = import ./hello.nix { inherit mkDerivation ;};
}
which makes absolutely no sense (to me) as right the line above i defined pkgs.
As i couldn't see what's wrong i opened nix repl and entered the lines.
nix-repl> pkgs = import <nixpkgs> {}
nix-repl> mkDerivation = import ./autotools.nix pkgs
nix-repl> hello = import ./hello.nix { inherit mkDerivation ;}
nix-repl> hello
«derivation /nix/store/g2y6sf5q236icvv2gwyg0lnij3mkr36j-hellooo.drv»
And voila there it works. So i don't understand why it fails with default.nix. I could only imagine that default.nix is somewhat special but syntax wise it must be fine otherwise nix repl wouldn't work as well.
Can anyone explain why i get this undefined variable error ?
Edit: Just after asking the question i found one way to solve the undefined variable error, if I put it like this:
let pkgs = import <nixpkgs> {}; mkDerivation = import ./autotools.nix pkgs;
in
{
hello = import ./hello.nix { inherit mkDerivation ;};
}
it works.
But my original question still remains.
The { }
syntax only defines a value. It does not bring the attributes in it into scope. You can use the rec { }
syntax which does do both.
rec {
pkgs = import <nixpkgs> {};
mkDerivation = import ./autotools.nix pkgs;
hello = import ./hello.nix { inherit mkDerivation ;};
}
What the nix repl
does is essentially create a let
binding every time name something. Your repl session can be thought of as this expression being built interactively:
let pkgs = import <nixpkgs> {};
in /* nixlang scope on 2nd prompt */
let mkDerivation = import ./autotools.nix pkgs
in /* nixlang scope on 3rd prompt */
let hello = import ./hello.nix { inherit mkDerivation ;}
/* nixlang scope on 4th prompt */
in hello
To illustrate the distinction between attribute set creation and name binding, you could name the attribute set and use it in its own definition
let
myScope = {
# myScope is like any other attribute set
pkgs = import <nixpkgs> {};
# laziness permits the attributes inside to reference myScope
mkDerivation = import ./autotools.nix myScope.pkgs;
hello = import ./hello.nix { inherit (myScope) mkDerivation ;};
};
in
# the value of this file is the myScope attribute set
myScope
This is equivalent to the rec
example, but brings only a single name into scope: myScope
.