Nix (Package Manager) Reference

Kip Landergren

(Updated: )

Contents

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:

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:

How to ensure these are:

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 a clever way to take existing interpreted scripts and make them reproducible without modifying the original logic.

Declarative Shell Environment

Allows:

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

<expr> Evaluate and print expression
<x> = <expr> Bind expression to variable
:a :add <expr> Add attributes from resulting set to scope
:b <expr> Build a derivation
:bl <expr> Build a derivation, creating GC roots in the working directory
:e :edit <expr> Open package or function in $EDITOR
:i <expr> Build derivation, then install result into current profile
:l :load <path> Load Nix expression and add it to scope
:lf :load-flake Load Nix flake and add it to scope
:ll :last-loaded Show most recently loaded variables added to scope
:p :print <expr> Evaluate and print expression recursively. Strings are printed directly, without escaping.
:q :quit Exit nix-repl
:r :reload Reload all files
:sh <expr> Build dependencies of derivation, then start nix-shell
:t <expr> Describe result of evaluation
:u <expr> Build derivation, then start nix-shell
:doc <expr> Show documentation of a builtin function
:log <expr> Show logs for a derivation
:te :trace-enable [bool] Enable, disable or toggle showing traces for errors
:? :help Brings up this help menu

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:

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:

let
  pkgs = import <nixpkgs> { };
in
rec {
  a = pkgs.callPackage ./a.nix { };
  b = pkgs.callPackage ./b.nix { inherit a; };
}

This has:

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:

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:

Bottom line: the implementation of lib.callPackageWith ensures that a.nix is imported and passed the correct attribute set.

With lazy evaluation:

Let’s take a practical example of the implications of these from above in building b:

% nix-build --attr b

The operations are:

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:

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:

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 = (pkgs.lib.callPackageWith (pkgs // packages)) ./b.nix { };

Note: the internals of callPackageWith are elided, but let’s assume they require the evaluation of (pkgs // packages)

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.

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:

Resources

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:

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:

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:

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)