Nix (Package Manager) Reference
Kip Landergren
(Updated: )
Contents
- nix
- nix-build
- nix-shell
- nix repl
- Nix Language
- Building Software With Nix
- Resources
- Frequently Asked Questions (FAQs)
- Open Questions
- Nix Terminology
-
nix-study
- 01-first-steps/02-reproducible-interpreted-scripts/all-hrefs.pl
- 01-first-steps/02-reproducible-interpreted-scripts/nixpkgs-releases-enhanced.sh
- 01-first-steps/02-reproducible-interpreted-scripts/nixpkgs-releases-original.sh
- 01-first-steps/02-reproducible-interpreted-scripts/pretty-table.py
- 01-first-steps/03-declarative-shell-environments/shell.nix
- 01-first-steps/04-towards-reproducibility-pinning-nixpkgs/shell-not-reproducible.nix
- 01-first-steps/04-towards-reproducibility-pinning-nixpkgs/shell-reproducible.nix
- 02-nix-language-basics/README.md
- 02-nix-language-basics/addOne.nix
- 02-nix-language-basics/addition.nix
- 02-nix-language-basics/attribute-set-attribute-access.nix
- 02-nix-language-basics/attribute-set-primitive-data-types.nix
- 02-nix-language-basics/file-system-paths.nix
- 02-nix-language-basics/function-libraries.nix
- 02-nix-language-basics/functions.nix
- 02-nix-language-basics/indented-strings.nix
- 02-nix-language-basics/inherit-let.nix
- 02-nix-language-basics/inherit-specific.nix
- 02-nix-language-basics/inherit.nix
- 02-nix-language-basics/let-expression-local-scope.nix
- 02-nix-language-basics/let-expression.nix
- 02-nix-language-basics/lists.nix
- 02-nix-language-basics/lookup-paths.nix
- 02-nix-language-basics/recursive-attribute-set.nix
- 02-nix-language-basics/strict-demo.nix
- 02-nix-language-basics/string-interpolation-no-coercion.nix
- 02-nix-language-basics/string-interpolation.nix
- 02-nix-language-basics/whitespace.nix
- 02-nix-language-basics/with-expression-scope.nix
- 02-nix-language-basics/with-expression.nix
- 03-packaging-existing-software/default.nix
- 03-packaging-existing-software/hello.nix
- 03-packaging-existing-software/icat.nix
- 04-package-parameters-and-overrides-with-callpackage/a.nix
- 04-package-parameters-and-overrides-with-callpackage/alternative.nix
- 04-package-parameters-and-overrides-with-callpackage/b.nix
- 04-package-parameters-and-overrides-with-callpackage/c.nix
- 04-package-parameters-and-overrides-with-callpackage/d.nix
- 04-package-parameters-and-overrides-with-callpackage/default.nix
- 04-package-parameters-and-overrides-with-callpackage/e.nix
- 04-package-parameters-and-overrides-with-callpackage/foo.nix
- 04-package-parameters-and-overrides-with-callpackage/hello.nix
- 04-package-parameters-and-overrides-with-callpackage/lazy.nix
- README.md
nix
| add | installs the specified derivation |
| search | |
| repl | |
| instantiate |
others:
| nix-collect-garbage |
useful:
nix instantiate --eval [FILE]
```text
nix-shell -p nix-info --run "nix-info -m"
nix-prefetch-url --unpack [URL] --type sha256
nix-build
Options
Note: consult nix-build(1) for an authoritative reference.
| --dry-run | show what store paths would be built or downloaded | |
| -A attrPath | --attr attrPath | select the attribute being evaluated |
nix-shell
Does two things:
- builds the dependencies of the specified derivation (but not the derivation itself)
- starts an interactive shell reflecting the environment of the derivation
Metaphor: prepares the kitchen (ingredients, tools laid out, oven preheated) for a planned recipe but lets you decide how that recipe comes together.
Originally built to create an environment for debugging package builds and later adapted for general use.
Options
Note: consult nix-shell(1) for an authoritative reference.
| -p packages | --packages packages | specify the packages needed |
| --command cmd | run the shell command in an interactive shell, with caveats | |
| --run cmd | run the shell command in a non-interactive shell | |
| --pure | discard most environment variables, with caveats | |
| -i interpreter | the script interpreter to be invoked |
Common Options
| -I | --include | list of search paths to resolve lookup paths (for the package and/or attribute declarations) |
On Using Option --pure
Almost entirely clears the existing environment before the shell is started, with some exceptions. Refer to nix-shell(1). Good for situations where isolation is a requirement. Makes it difficult to use existing tooling:
% echo "EDITOR is $EDITOR"
EDITOR is emacs -nw
% nix-shell '<nixpkgs>' --run 'echo "EDITOR is $EDITOR"'
EDITOR is emacs -nw
% nix-shell '<nixpkgs>' --run 'echo "EDITOR is $EDITOR"' --pure
EDITOR is
Note: I discovered that you can pass an empty --packages to get what appears to be a shell as if you invoked --packages stdenv. If you omit --packages (without specifying the path as above) you will get an error if no shell.nix or default.nix is specified. You will also get an error trying to pass --packages ''. Based on error messages whatever you pass gets passed to buildInputs and so to be explicit you could pass --packages stdenv but I think this is redundant. Same redundancy as if you wrote --packages coreutils. I haven’t checked the source to figure out what exactly is going on.
On Reproducibility
Specify a hash, e.g.:
% nix-shell --packages git \
--run 'git --version' \
--pure \
--include nixpkgs=https://github.com/NixOS/nixpkgs/tarball/2a601aafdc5605a5133a2ca506a34a3a73377247
git version 2.33.1
Use Cases
Ad Hoc Environments
Note: for finer-grained control over the environment, and to check the configuration into source control, review Declarative Shell Environment.
For Experimentation
% nix-shell --packages git python3
[nix-shell]$ git --version
git version 2.53.0
[nix-shell]$ python3 --version
Python 3.13.12
For One-Off Commands
% nix-shell --packages git --run "git --version"
git version 2.53.0
Reproducible Scripts
Nix provides a way for any interpreted script (bash, Ruby, python, whatever) to run reproducibly.
Consider this example from the tutorial on reproducible scripts:
#! /bin/bash
curl https://github.com/NixOS/nixpkgs/releases.atom | xml2json | jq .
It has dependencies:
- bash
- curl
- xml2json
- jq
How to ensure these are:
- consistent across developer machines
- installed cleanly
- always invoked for this script
Enter nix-shell usage:
#!/usr/bin/env nix-shell
#! nix-shell -i bash --pure
#! nix-shell --packages bash cacert curl jq python3Packages.xmljson
#! nix-shell --include nixpkgs=https://github.com/NixOS/nixpkgs/archive/2a601aafdc5605a5133a2ca506a34a3a73377247.tar.gz
curl https://github.com/NixOS/nixpkgs/releases.atom | xml2json | jq .
Which is specifying:
| #!/usr/bin/env nix-shell | nix-shell shebang |
| #! nix-shell [INVOCATION] | all lines merged together before execution, allowing for readability and logical grouping |
| -i bash | the interpreter to use |
| --pure | the environment configuration |
| --packages [PACKAGES] | the desired packages |
| --include path | the specific source to look for those packages |
Note: it is important to call out the trick explicitly here:
- this is still a bash script
- the special shebang usage invokes nix-shell
- nix-shell, through the special comments, runs the script with the correct interpreter and dependencies
This is a clever way to take existing interpreted scripts and make them reproducible without modifying the original logic.
Declarative Shell Environment
Allows:
- putting the environment—the variables and packages—into source control
- running specific scripts on shell startup
- ensuring consistent development environments across machines
This is especially great when you are hacking on foo, whose dependencies bar and baz should be built and ready for you to use when testing your changes to foo.
nix repl
How to tell the type of foo:
nix-repl> :p foo
«lambda @ «string»:1:2»
How to find the args of a function:
nix-repl> builtins.functionArgs foo
{
a = false;
b = false;
}
Nix Language
The Nix language enables functional programming with the file system.
A .nix file specifies a single expression. Evaluating this expression results in a value. Often this value is a derivation, which contains the specific instructions of how to build a package. The resulting derivation is written to a .drv file in ATerm format. A derivation is realised (intentional British spelling, matching option --realise and Nix documentation) when its actual outputs are produced. These outputs are referred to as the build result and may be used as inputs for other derivations.
One may find language that refers to .nix files for building packages as “creating derivations”, or being “composed of derivations”. The more precise wording is that these are expressions which evaluate to derivations. Or, additionally, that they are describing derivations.
Understanding Lazy Evaluation
Consider the scenario where you have a package set with many varied and interdependent dependencies:
# a.nix. wants `pkgs.hello` from nixpkgs
{ hello }:
{
# do something with hello
}
and
# b.nix. wants `pkgs.hello` and `a.nix` as `a`
{ hello, a }:
{
# do something with hello and a
}
Let’s start with a default.nix that allows us to invoke nix-build --attr a or nix-build --attr b, making use of recursive attribute sets:
rec {
pkgs = import <nixpkgs> { };
a = import ./a.nix {
hello = pkgs.hello;
};
b = import ./b.nix {
a = a;
hello = pkgs.hello;
};
}
This has a few issues:
- we are offering pkgs as an attribute name when really our goal is to just expose a and b
- both a and b have the same repeated “shape” of importing a file and specifying dependencies
- the dependencies must be manually managed
Let’s take these one by one:
we are offering pkgs as an attribute name when really our goal is to just expose a and b
We can expose just a and b by using a let ... in block:
let
pkgs = import <nixpkgs> { };
hello = pkgs.hello;
in
rec {
a = import ./a.nix {
inherit hello; # syntatic sugar for `hello = hello;`
};
b = import ./b.nix {
inherit a; # syntatic sugar for `a = a;`
inherit hello; # syntatic sugar for `hello = hello;`
};
}
both a and b have the same repeated “shape” of importing a file and specifying dependencies
We can use pkgs.callPackage, which is purpose-built to:
- inspect the necessary attributes of the specified package derivation
- call it with the attribute names assigned to their respective matches within the pkgs namespace
- override any of those attributes with the second arg
let
pkgs = import <nixpkgs> { };
in
rec {
a = pkgs.callPackage ./a.nix { };
b = pkgs.callPackage ./b.nix { inherit a; };
}
This has:
- for a:
- used builtins.functionArgs to check that a.nix needed hello
- found a match in pkgs.hello
- assigned it to the hello attribute
- merged { hello = pkgs.hello; } and { }, with the right-hand attribute set taking precedence
- passed { hello = pkgs.hello; } to import ./a.nix
- for b:
- used builtins.functionArgs to check that b.nix needed hello and a
- found a match in pkgs.hello
- assigned it to the hello attribute
- merged { hello = pkgs.hello; } and { inherit a; }, with the right-hand attribute set taking precedence
- passed { hello = pkgs.hello; a = a; } to import ./b.nix
We have been able to wholly replace our previous example’s boilerplate with pkgs.callPackage.
Let’s look deeper at pkgs.callPackage to understand how it works.
Within splice.nix are the relevant pieces:
let
pkgsForCall = if actuallySplice then splicedPackages else pkgs;
in
{
callPackage = pkgs.newScope { };
newScope = extra: lib.callPackageWith (pkgsForCall // extra);
}
Notes:
- the value of actuallySplice is not relevant here (splicing is a cross-compilation concern), just assume pkgsForCall = pkgs;
- the // operator from (pkgsForCall // extra) says “merge attribute set pkgsForCall with attribute set extra and have extra take precedence)
Substituting through, we see:
callPackage = pkgs.newScope { };
callPackage = (extra: lib.callPackageWith (pkgsForCall // extra)) { };
callPackage = lib.callPackageWith (pkgs // { });
callPackage = lib.callPackageWith pkgs;
So again substituting:
{
a = pkgs.callPackage ./a.nix { };
}
{
a = (lib.callPackageWith pkgs) ./a.nix { };
}
And within customisation.nix we see that the signature of callPackageWith is:
autoArgs: fn: args:
This means that:
- pkgs gets passed as our autoArgs
- ./a.nix gets passed as fn
- { } gets passed as args
Bottom line: the implementation of lib.callPackageWith ensures that a.nix is imported and passed the correct attribute set.
With lazy evaluation:
- expressions are only evaluated when their value is needed
- arguments (which are expressions) are passed unevaluated until their value is needed
- thunks are unevaluated expressions
- attribute names are always knowable without forcing values
Let’s take a practical example of the implications of these from above in building b:
% nix-build --attr b
The operations are:
- the attribute name b is needed and therefore evaluated
- the expression pkgs.callPackage ./b.nix { inherit a; }; is evaluated
- the expression pkgs.callPackage is evaluated
- the expression pkgs.callPackage is called with unevaluated arguments ./b.nix and { inherit a; }
The end result kind of looks kind of like:
./b.nix {
hello = \<THUNK hello\>; # `pkgs.hello`
a = \<THUNK a\>; # `pkgs.callPackage ./a.nix { }`
}
And remember: those THUNK entities are only evaluated when they are needed by b.nix!
Let’s go high level again and look at the original:
let
pkgs = import <nixpkgs> { };
in
rec {
a = pkgs.callPackage ./a.nix { };
b = pkgs.callPackage ./b.nix { inherit a; };
}
The issues are:
- we know that a and b are interdependent
- we know both of them depend on at least one package (hello) from pkgs
- we must keep exact track of what a and b’s other dependencies are to add them to their attribute sets
In essence, we are manually managing the attribute set passed to b.nix.
Consider now an alternative implementation taking advantage of lazy evaluation:
let
pkgs = import <nixpkgs> { };
callPackage = pkgs.lib.callPackageWith (pkgs // packages);
packages = {
a = callPackage ./a.nix { };
b = callPackage ./b.nix { };
};
in
packages
A few things pop out to me:
- what does (pkgs // packages) do again?
- what does pkgs.lib.callPackageWith do?
- how can callPackage be defined in terms of packages, before it is defined?
- how can callPackage’s definition use packages if packages’ definition itself uses callPackage?!
- what happened to b’s { inherit a; }?
Let’s take them one by one.
what does (pkgs // packages) do again?
(pkgs // packages) merges two attribute sets (and the right-hand takes precedence). This allows our definition of packages to override whatever is in pkgs.
what does pkgs.lib.callPackageWith do?
We know from above that:
callPackage = lib.callPackageWith (pkgs // extra);
and so we are creating our own custom version of callPackage that for extra we are passing packages instead of { }.
how can callPackage be defined in terms of packages, before it is defined?
The let ... in brings every binding into scope immediately so there is no “before” associated with the order of keys in the attribute set.
how can callPackage’s definition use packages if packages’ definition itself uses callPackage?!
This and the next observation’s answers heavily involve lazy evaluation and are best explained through the example of building b:
- b is needed, and so b = callPackage ./b.nix { }; is evaluated
- our custom callPackage is evaluated lazily:
b = (pkgs.lib.callPackageWith (pkgs // packages)) ./b.nix { };
- pkgs.lib.callPackageWith (pkgs // packages) is evaluated
Note: the internals of callPackageWith are elided, but let’s assume they require the evaluation of (pkgs // packages)
- (pkgs // packages) is evaluated
- packages is lazily evaluated to: { a = <THUNK a>; b = <THUNK b>; }
Notice that packages is only evaluated to identify its attribute names, not the attribute values! We do not need the fully evaluated version of callPackage (that a and b depend on) to merge the attribute sets and continue. This is lazy evaluation in action.
- ./b.nix is inspected within callPackageWith, seeing it needs a and hello
- the merged attribute set of { a = <THUNK a>; hello = <THUNK hello>; } is created
what happened to b’s { inherit a; }?
It is no longer necessary as our custom version of callPackage with packages lazily evaluated ensures that a is present for merge.
The final substituted result looks something like:
b = (import ./b.nix) { a = \<THUNK a\>; hello = \<THUNK hello\>; };
Which is exactly back to our starting version:
b = import ./b.nix {
a = a;
hello = pkgs.hello;
};
but without manual dependency management!
And remember: this works because lazy evaluation means that the <THUNK>s from above are only evaluated when needed.
This is a clever bit of code highlighting a pattern seen across nix package definitions and a good demonstration of the power of lazy evaluation.
Building Software With Nix
The flow is essentially:
- get the source
- verify the integrity (e.g.
nix-prefetch-url --unpack [URL] --type sha256) - try to build
- on build failure:
- add necessary dependencies (existing derivations for packages) as inputs
- fiddle with environment variables to get build flags correct
- add custom code within the appropriate build phase to work around any deficiencies in the source build process
- go back to try to build
- on build failure:
- use your built software in ./result
Resources
- NixOS Wiki
- NixOS Package Search
- Nix Channel Status
- Documentation:
- nixpkgs documentation
- helpful nixpkgspaths:
- doc/stdenv/stdenv.chapter.md
- doc/build-helpers/special/mkshell.section.md
- Releases:
- The Purely Functional Software Deployment Model, Eelco Dolstra’s PhD thesis
- Nix: A Safe and Policy-Free System for Software Deployment
Frequently Asked Questions (FAQs)
Why aren’t Nix’s man pages working?
If you get:
% nix-shell --help
No manual entry for nix-shell
it is related to a bunch of issues:
and a fix, inspired by a comment in the latter issue, is to essentially:
- install the latest version of nix as a flake (even if, as is my case, that doesn’t support intel macOS)
- copy the man page directories
- modify MANPATH environment variable
e.g.
nix --extra-experimental-features nix-command --extra-experimental-features flakes profile add nixpkgs#nix
mkdir -p ~/.local/share/kl-nix-man-pages-hack
cp -R ~/.nix-profile/share/man/ ~/.local/share/kl-nix-man-pages-hack
/nix/var/nix/profiles/default/bin/nix --extra-experimental-features nix-command --extra-experimental-features flakes profile remove nix
and then in your .zshenv or whatever:
export MANPATH="$HOME/.local/share/kl-nix-man-pages-hack:"
and now this should work:
man nix-shell
Notes:
- I had to use the full path to invoke the working nix (rather than the flake nix)
- this installs manuals from the latest version of nix at time of invocation, meaning there may be differences in documentation and commands that you should be aware of
- nix-shell --help will still not work because iirc nix is looking for the man pages relative to its install path, not using $MANPATH
Open Questions
Understanding nix-shell
I don’t fully understand why the former takes so much longer than the latter:
% time nix-shell '<nixpkgs>' --packages git --run 'git --version' --pure
nix-shell '<nixpkgs>' --packages git --run 'git --version' --pure 4.56s user 9.78s system 9% cpu 2:39.27 total
vs.
% time nix-shell --packages git --run 'git --version' --pure
nix-shell --packages git --run 'git --version' --pure 1.75s user 0.43s system 24% cpu 8.817 total
My intuition is that:
- because the directory does not contain a shell.nix or default.nix, both expressions are using <nixpkgs> behind the scenes
My guess is that it has to do with the latter running mkShell vs whatever the default.nix runs in the former, but I don’t know.
Nix Terminology
- build platform
- where the executable is built
- derivation
- precise description of how contents of existing files are used to derive new files
- evaluate
- transform an expression into a value according to the language rules
- host platform
- where the executable runs
- package
- collection of files and other data; or a nix expression representing such a collection before it comes into being
- realising / realisation
- building a derivation to produce its outputs; as in, realising a derivation
- reproducible
- getting the same results no matter when or where you run the command
- target platform
- synonymous with host platform
- thunk
- a pointer to an unevaluated expression (e.g. a name)
nix-study
01-first-steps/02-reproducible-interpreted-scripts/all-hrefs.pl
#! /usr/bin/env nix-shell
#! nix-shell -i perl
#! nix-shell --packages perl
#! nix-shell --packages perlPackages.HTMLTokeParserSimple
#! nix-shell --packages perlPackages.LWP
#! nix-shell --packages perlPackages.LWPProtocolHttps
use HTML::TokeParser::Simple;
# Fetch nixos.org and print all hrefs.
my $p = HTML::TokeParser::Simple->new(url => 'https://nixos.org/');
while (my $token = $p->get_tag("a")) {
my $href = $token->get_attr("href");
print "$href\n" if $href;
}
01-first-steps/02-reproducible-interpreted-scripts/nixpkgs-releases-enhanced.sh
#!/usr/bin/env nix-shell
#! nix-shell -i bash --pure
#! nix-shell --packages bash cacert curl jq python3Packages.xmljson
#! nix-shell --include nixpkgs=https://github.com/NixOS/nixpkgs/archive/2a601aafdc5605a5133a2ca506a34a3a73377247.tar.gz
curl https://github.com/NixOS/nixpkgs/releases.atom | xml2json | jq .
01-first-steps/02-reproducible-interpreted-scripts/nixpkgs-releases-original.sh
#! /bin/bash
curl https://github.com/NixOS/nixpkgs/releases.atom | xml2json | jq .
01-first-steps/02-reproducible-interpreted-scripts/pretty-table.py
#! /usr/bin/env nix-shell
#! nix-shell -i python3 --packages python3 python3Packages.prettytable
import prettytable
# Print a simple table.
t = prettytable.PrettyTable(["N", "N^2"])
for n in range(1, 10): t.add_row([n, n * n])
print(t)
01-first-steps/03-declarative-shell-environments/shell.nix
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-25.11";
pkgs = import nixpkgs {
config = { };
overlays = [ ];
};
in
# KL NOTE: using `mkShellNoCC` rather than `mkShell` just because we don't need
# a C compiler / tooling
pkgs.mkShellNoCC {
# KL NOTE: `packages` resolves to `nativeBuiltIns`, so you may see that used
# in other examples.
packages = with pkgs; [
cowsay
lolcat
];
# KL NOTE: certain environment variables can be set
GREETING = "hello, nix!";
# KL NOTE: `shellHook` can also be used to override protected environment
# variables. consult the docs.
shellHook = ''
echo $GREETING | cowsay | lolcat
'';
}
01-first-steps/04-towards-reproducibility-pinning-nixpkgs/shell-not-reproducible.nix
let
# does not specify a specific commit hash for pkgs. this expression resolves
# to a lookup path for whatever the system value of nixpkgs is (which could
# be different on your machine vs mine).
pkgs = import <nixpkgs> { };
in
# KL NOTE: using `mkShellNoCC` rather than `mkShell` just because we don't need
# a C compiler / tooling
pkgs.mkShellNoCC {
# KL NOTE: `packages` resolves to `nativeBuiltIns`, so you may see that used
# in other examples.
packages = with pkgs; [
git
cowsay
lolcat
];
# KL NOTE: certain environment variables can be set
GREETING = "hello, nix!";
# KL NOTE: `shellHook` can also be used to override protected environment
# variables. consult the docs.
shellHook = ''
echo $GREETING | cowsay | lolcat
git --version
'';
}
01-first-steps/04-towards-reproducibility-pinning-nixpkgs/shell-reproducible.nix
let
# specifies a specific commit sha, ensuring that all systems running this
# will use the same sources. here we replace the expression `<nixpkgs>` with
# one using `fetchTarball`.
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/archive/0a0bf409f3593f415d0a554f33acc63dc7dccb43.tar.gz";
pkgs = import nixpkgs {
config = { };
overlays = [ ];
};
in
# KL NOTE: using `mkShellNoCC` rather than `mkShell` just because we don't need
# a C compiler / tooling
pkgs.mkShellNoCC {
# KL NOTE: `packages` resolves to `nativeBuiltIns`, so you may see that used
# in other examples.
packages = with pkgs; [
git
cowsay
lolcat
];
# KL NOTE: certain environment variables can be set
GREETING = "hello, nix!";
# KL NOTE: `shellHook` can also be used to override protected environment
# variables. consult the docs.
shellHook = ''
echo $GREETING | cowsay | lolcat
git --version
'';
}
02-nix-language-basics/README.md
# nix language basics
to evaluate:
```text
% nix-instantiate --eval addition.nix
```
and to pretty-print use `nixfmt`:
```text
% nix-instantiate --eval addition.nix | nixfmt
```
02-nix-language-basics/addOne.nix
x: x + 1
02-nix-language-basics/addition.nix
1 + 2
02-nix-language-basics/attribute-set-attribute-access.nix
let
# dot assignment
a.b.c = 1;
in
# dot access
a.b.c + a.b.c # 2
02-nix-language-basics/attribute-set-primitive-data-types.nix
# attribute sets are collections of name-value pairs where names must be unique
{
string = "hello";
integer = 1;
float = 3.141;
bool = true;
null = null;
# list items are separated by whitespace
list = [
1
"two"
false
];
# names do not (??usually??) require quotes
attribute-set = {
a = "hello";
b = 2;
c = 2.718;
d = false;
}; # comments are supported
}
02-nix-language-basics/file-system-paths.nix
[
/absolute/path
./relative/path/from/current
../relative/path/from/parent
# when file system paths are used in string interpolation those file contents
# are copied to the nix store
"${./addOne.nix}"
]
02-nix-language-basics/function-libraries.nix
let
# convention is that `pkgs` refers to nixpkgs
pkgs = import <nixpkgs> { };
in
[
# builtins for common primitive operations
(builtins.toString 2)
(builtins.hasAttr "foo" { bar = "bar"; }) # false
# import is `builtins.import` but available at top level
(import ./addOne.nix 2)
# `lib` within nixpkgs has useful functions
(pkgs.lib.strings.toUpper "lookup paths considered harmful")
]
02-nix-language-basics/functions.nix
[
# all functions take a single argument
(x: x + 1)
# multiple arguments via nesting
(x: y: x + y)
# attribute sets acceptable as arguments
({ a, b }: a + b)
# default values
(
{
a,
b ? 0,
}:
a + b
)
# additional attributes allowed
({ a, b, ... }: a + b)
# named attribute sets also allowed. this allows referencing the attribute
# set as `args` (which is destructured as `{ a, b, ...}`). so we get `a` and
# `b` through destructuring and then can use the name `args` to reference
# `args.c`
(args@{ a, b, ... }: a + b + args.c)
# same as above, just a different form of named attribute sets
({ a, b, ... }@args: a + b + args.c)
# all functions are anonymous but can be assigned to names and called
(
let
f = x: x + 1;
in
f 2 # 3. calls `f` with argument `2`. could be written `f (2)`
)
# consider the different function invocations, taking into consideration that
# list elements are separated by whitespace
(
let
f1 = x: x + 1;
f2 = x: x.a + 1;
f3 = x: y: x + y;
# note: `c` not required as it has a default argument
f4 =
{
a,
b,
c ? 0,
}:
a + b + c;
# allows the passed attribute set to contain attributes not used
f5 = { a, b, ... }: a + b;
arg = {
a = 1;
};
in
[
# all equivalent
((x: x + 1) 2)
(f1 2)
(f1 (2))
# weird but just showing that attribute sets can be args
(f2 arg)
# curried function example: x = 1 and y = 2
(f3 1 2)
# attribute set destructuring / keyword arguments
(f4 {
a = 1;
b = 2;
})
# attribute set flexibility with additional attributes
(f5 {
a = 1;
b = 2;
foo = "bar";
})
]
)
]
02-nix-language-basics/indented-strings.nix
''
one
two
three
''
02-nix-language-basics/inherit-let.nix
let
big_complex_attriubte_set = {
x = 1;
y = 2;
z = 3;
};
in
let
# inherit permitted within `let ... in` block. useful when you only want to
# pull in specific attributes from the surrounding scope.
inherit (big_complex_attriubte_set) x y;
in
[
x
y
]
02-nix-language-basics/inherit-specific.nix
let
a = {
x = 4;
y = 5; # intentionally left off
z = 6;
};
in
{
# inherit specific names into a new attribute set
inherit (a) x z; # equivalent to `x = a.x; z = a.z;`
}
02-nix-language-basics/inherit.nix
let
x = 1;
y = 2;
z = 3; # intentionally left off
in
{
# inherit specific names into a new attribute set
inherit x y; # equivalent to `x = x; y = y;`
}
02-nix-language-basics/let-expression-local-scope.nix
{
a =
let
x = 2;
in
x + x;
b = x; # error: undefined variable 'x'
}
02-nix-language-basics/let-expression.nix
# IMPORTANT: all bindings are in scope for all other bindings (mutual
# recursion)
let
# assignments are placed here between `let` and `in`, and order does not
# matter
b = a + 1;
a = 1;
in
# this is similar to recursive attribute sets except for the fact that _any_
# nix expression can go here (a recursive attribute set evaluates to an
# attribute set)
a + b
02-nix-language-basics/lists.nix
# lists are comprised of atomic expressions, separated by whitespace
[
1
2
3
# when whitespace is part of their definition, they must be wrapped with
# parentheses
(x: x + 1)
]
02-nix-language-basics/lookup-paths.nix
# the angle brackets (`< ... >`) indicate that the containing name (??) should
# be used to look up a path on disk. most common you will see:
<nixpkgs>
02-nix-language-basics/recursive-attribute-set.nix
# elements can be entered in any order but it has no effect on evaluation
# order. elements are ordered during evaluation (?? by some criteria nix
# decides, likely ascending ??)
rec {
one = 1;
two = one + 1;
three = two + 1;
}
02-nix-language-basics/strict-demo.nix
# consider the output difference when evaluating this file with and without the
# `--strict` flag. nix the language is lazy and `--strict` forces recursive
# evaluation.
{ a.b.c = 1; }
02-nix-language-basics/string-interpolation-no-coercion.nix
let
x = 1;
in
"${x} has a problem" # error: cannot coerce an integer to a string: 1
02-nix-language-basics/string-interpolation.nix
let
# any value that can be represented as a string can be used within `${ }`
name = "Nix";
in
"hello ${name}"
02-nix-language-basics/whitespace.nix
# whitespace is insignificant except where used to delimit lexical tokens
# e.g. `let x=1;y=2;in x+y` is equivalent to the following code formatted with
# `nixfmt`:
let
x = 1;
y = 2;
in
x + y
02-nix-language-basics/with-expression-scope.nix
let
a = {
x = 1;
y = 2;
z = 3;
};
in
{
# an attribute set with attribute name b paired with list 1 2 3
b = with a; [
x
y
z
];
c = x; # error: undefined variable 'x'
}
02-nix-language-basics/with-expression.nix
let
a = {
x = 1;
y = 2;
z = 3;
};
in
# allows access to attributes without repeating the attribute set
with a;
[
x # a.x
y # a.y
z # a.z
]
03-packaging-existing-software/default.nix
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-25.11";
pkgs = import nixpkgs {
config = { };
overlays = [ ];
};
in
{
hello = pkgs.callPackage ./hello.nix { };
icat = pkgs.callPackage ./icat.nix { };
}
03-packaging-existing-software/hello.nix
{
stdenv,
fetchzip,
libiconvReal,
}:
stdenv.mkDerivation {
pname = "hello";
version = "2.12.1";
src = fetchzip {
url = "https://ftp.gnu.org/gnu/hello/hello-2.12.1.tar.gz";
sha256 = "sha256-1kJjhtlsAkpNB7f6tZEs+dbKd8z7KoNHyDHEJ0tmhnc=";
};
# KL NOTE: required to get the build to work on x86 darwin. otherwise errors
# for undefined symbols from _iconv and others. I first tried `libiconv`
# paired with `NIX_LDFLAGS = "-libconv";` but then noticed there was also
# `libiconvReal` in the search results.
buildInputs = [ libiconvReal ];
}
03-packaging-existing-software/icat.nix
{
stdenv,
fetchFromGitHub,
imlib2,
libx11,
}:
stdenv.mkDerivation {
pname = "icat";
version = "v0.5";
# https://github.com/atextor/icat
src = fetchFromGitHub {
owner = "atextor";
repo = "icat";
rev = "v0.5";
sha256 = "0wyy2ksxp95vnh71ybj1bbmqd5ggp13x3mk37pzr99ljs9awy8ka";
};
buildInputs = [
imlib2
libx11
];
installPhase = ''
runHook preInstall
mkdir -p $out/bin
cp icat $out/bin
runHook postInstall
'';
}
04-package-parameters-and-overrides-with-callpackage/a.nix
# a.nix. wants `pkgs.hello` from nixpkgs
{ hello }:
{
# do something with hello
}
04-package-parameters-and-overrides-with-callpackage/alternative.nix
let
pkgs = import <nixpkgs> { };
in
rec {
a = pkgs.callPackage ./a.nix { };
b = pkgs.callPackage ./b.nix { inherit a; };
c = pkgs.callPackage ./c.nix { inherit b; };
d = pkgs.callPackage ./d.nix { };
e = pkgs.callPackage ./e.nix { inherit c d; };
}
04-package-parameters-and-overrides-with-callpackage/b.nix
# b.nix. wants `pkgs.hello` and `a.nix` as `a`
{ hello, a }:
{
# do something with hello and a
}
04-package-parameters-and-overrides-with-callpackage/c.nix
{ b }:
{
bar = b.foo;
}
04-package-parameters-and-overrides-with-callpackage/d.nix
{ }:
{
qux = "qux";
}
04-package-parameters-and-overrides-with-callpackage/default.nix
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-25.11";
pkgs = import nixpkgs {
config = { };
overlays = [ ];
};
in
rec {
hello = pkgs.callPackage ./hello.nix { audience = "people"; };
hello-folks = hello.override { audience = "folks"; };
}
04-package-parameters-and-overrides-with-callpackage/e.nix
{ c, d }:
{
x = c.bar;
y = d.qux;
}
04-package-parameters-and-overrides-with-callpackage/foo.nix
let
pkgs = import <nixpkgs> { };
in
rec {
a = import ./a.nix {
hello = pkgs.hello;
};
b = import ./b.nix {
a = a;
hello = pkgs.hello;
};
}
04-package-parameters-and-overrides-with-callpackage/hello.nix
{
writeShellScriptBin,
audience ? "world",
}:
writeShellScriptBin "hello" ''
echo 'hello ${audience}!'
''
04-package-parameters-and-overrides-with-callpackage/lazy.nix
let
pkgs = import <nixpkgs> { };
callPackage = pkgs.lib.callPackageWith (pkgs // packages);
packages = {
a = callPackage ./a.nix { };
b = callPackage ./b.nix { };
c = callPackage ./c.nix { };
d = callPackage ./d.nix { };
e = callPackage ./e.nix { };
};
in
packages
README.md
# nix-study
work from [nix.dev](https://nix.dev)