Oooookay.

I said I was gonna read the manual, right? I’ll do it. I’ll read it. I already read all those functions, didn’t I? It’s time for Part VI: Command Reference.

Chapter 20. Common Options

Shoutout to --fallback:

Whenever Nix attempts to build a derivation for which substitutes are known for each output path, but realising the output paths through the substitutes fails, fall back on building the derivation.

The most common scenario in which this is useful is when we have registered substitutes in order to perform binary distribution from, say, a network repository. If the repository is down, the realisation of the derivation will fail. When this option is specified, Nix will build the derivation instead. Thus, installation from binaries falls back on installation from source. This option is not the default since it is generally not desirable for a transient failure in obtaining the substitutes to lead to a full build from source (with the related consumption of resources).

Makes sense! Fail early.

I learn about --arg name value, which is interesting:

This option is accepted by nix-env, nix-instantiate and nix-build. When evaluating Nix expressions, the expression evaluator will automatically try to call functions that it encounters. It can automatically call functions for which every argument has a default value (e.g., { argName ? defaultValue }: ...). With --arg, you can also call functions that have arguments without a default value (or override a default value). That is, if the evaluator encounters a function with an argument named name, it will call it with value value.

I saw before why the expression import <nixpkgs> was a function – so I can change the system if I want to, and probably other things.

--argstr lets me get away from quoting value in double-quotes, which is nice.

In the description of --attr:

In addition to attribute names, you can also specify array indices. For instance, the attribute path foo.3.bar selects the bar attribute of the fourth element of the array in the foo attribute of the top-level expression.

First time I’ve heard the term “array.” First time I’ve seen this syntax.

nix-repl> [1 2 3].2
error: attempt to call something which is not a function but a list, at (string):1:1

nix-repl> [1 2 3].foo
error: value is a list while a set was expected, at (string):1:1

I guess I don’t know what an array is? No idea.

--expr is akin to bash’s -c. Neat.

Chapter 21. Common Environment Variables

IN_NIX_SHELL:

Indicator that tells if the current environment was set up by nix-shell. Since Nix 2.0 the values are "pure" and "impure"

Huh. I wonder what that means.

I learn that NIX_PATH has a special shorthand way to refer to the “official” Nix channels: nixpkgs=channel:nixos-15.09 is a valid NIX_PATH element.

I don’t understand how the colon is an element separator but also a part of that syntax? I am confused. Oh well.

This seems to be the “prefix” thing that I saw in builtins.nixPath.

Chapter 22. Main Commands

This looks like just… the man pages. For all the main commands. Or like… the man pages as they existed at some point in time? But augmented with hypertext.

nix-env

Alright. I read about nix-env. When installing a package using “symbolic name” mode (the thing that takes 30 seconds):

If there are multiple derivations matching a name in args that have the same name (e.g., gcc-3.3.6 and gcc-4.1.1), then the derivation with the highest priority is used. A derivation can define a priority by declaring the meta.priority attribute. This attribute should be a number, with a higher value denoting a lower priority. The default priority is 0.

If there are multiple matching derivations with the same priority, then the derivation with the highest version will be installed.

Okay. I won’t use that, ever, because it takes 30 seconds to do anything with it, but sure.

To find out the attribute paths of available packages, use nix-env -qaP.

I run that, and am delighted to find, hidden amongst the tens of thousands of lines of uninteresting output:

ianpkgs.hello       my-hello-1.0

We made it.

Alright, continuing through nix-env… we already saw --upgrade and --uninstall. We have --set? Which “modifies the current generation of a profile so that it contains exactly the specified derivation, and nothing else.” Okay? I am curious what the point of that is. Can I make a derivation that expresses my entire profile? I would totally do that so that I could have the same environment on multiple machines, if I knew how.

Then --set-flag. Hmm. This is weird. I can… change properties of installed derivations? Like override things? The documentation says that priority, keep, and active are “useful” things.

Hmm. Let’s see what this does. I first remove hello to make sure I get the “official” one:

$ nix-env -e hello

$ nix-env -iA nixpkgs.hello
installing 'hello-2.10'
these paths will be fetched (0.02 MiB download, 0.07 MiB unpacked):
  /nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10
copying path '/nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10' from 'https://cache.nixos.org'...
building '/nix/store/zf5nn4ad6g1s8w252ja5zrn7mvy45b26-user-environment.drv'...
error: packages '/nix/store/rfd0d3kxdvj1z281bs96baa8fx5zl83z-my-hello-1.0/bin/hello' and '/nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10/bin/hello' have the same priority 5; use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' to change the priority of one of the conflicting packages (0 being the highest priority)
builder for '/nix/store/zf5nn4ad6g1s8w252ja5zrn7mvy45b26-user-environment.drv' failed with exit code 1
error: build of '/nix/store/zf5nn4ad6g1s8w252ja5zrn7mvy45b26-user-environment.drv' failed

Oh wow! Weird coincidence. I forgot that I need to use nix-env -e my-hello. I wonder why I can’t just say nix-env -eA ianpkgs.hello. The lack of symmetry is very upsetting.

$ nix-env -e my-hello
uninstalling 'my-hello-1.0'
building '/nix/store/m3ppbfhjrjr6mbzk12hd552fnggl37hf-user-environment.drv'...
created 40 symlinks in user environment

$ nix-env -iA nixpkgs.hello
installing 'hello-2.10'
building '/nix/store/s223rbnw7kg5mp873vpayaqsr69bnqms-user-environment.drv'...
created 43 symlinks in user environment

Okay. Back to normal. Now let’s see how this works…

$ nix-env --set-flag name hello-renamed nixpkgs.hello
error: selector 'nixpkgs.hello' matches no derivations

Ugh.

$ nix-env --set-flag name hello-renamed hello
setting flag on 'hello-2.10'
building '/nix/store/rx46k8gb3as8b3jwy5id5zjxb8dp8bfa-user-environment.drv'...
created 43 symlinks in user environment

Okay.

$ hello-renamed
zsh: command not found: hello-renamed

Duh; renaming the derivation wouldn’t change the name of its output.

$ nix-env -q | cat
hello-2.10
keen4
nix-2.3.10
nss-cacert-3.60
sl-5.05

Hmm. Does not seem to have done anything.

Ah, but looking back in the manual, it says “allows meta attributes of installed packages to be modified” (emphasis mine). Whoops. Well okay – let’s try that.

$ nix-env --set-flag priority 1234 hello
setting flag on 'hello-2.10'
building '/nix/store/d8hjw2znsh2f3y3p6ihyr91v1p1xn0vl-user-environment.drv'...
created 43 symlinks in user environment

Did it work? Did it do anything? I don’t know. I don’t know how to “view” this derivation. I am mostly curious about where this information is stored. I assume, you know, this isn’t rewriting the hello.nix file; that would be crazy. But this doesn’t fit into my current mental model of Nix expression evaluation. Derivations are found, and then… patched? On a per-user basis? Presumably only when I’m using nix-env? What about nix-build? I do not know.

The manual says “they affect the behaviour of nix-env or the user environment build script.” What’s the “user environment build script”?

The manual doesn’t say anything about how this works. Maybe I can figure it out:

$ rg 1234 /nix/var

$ rg 1234 ~/.nix-profile

Hmmmmmmmm. Okay. No idea. Must be magic.

$ nix-env --set-flag priority 90424915 hello

$ rg --files-with-matches 90424915 /nix/store
/nix/store/gdq1348ld0qcgf7csf91331wqbls93yw-env-manifest.nix
/nix/store/9jmsijgqm9flpp37q9v43lpfp974zhmv-user-environment.drv

Okay. Oh. I’m a dum-dum: rg needs -L to actually follow ~/.nix-profile. It’s right there:

$ rg -L --files-with-matches 90424915 ~/.nix-profile/
/Users/ian/.nix-profile/manifest.nix

Really wish I know how to pretty print this file.

$ nix repl
Welcome to Nix version 2.3.10. Type :? for help.

nix-repl> manifest = import /Users/ian/.nix-profile/manifest.nix

nix-repl> :p manifest
[ «derivation ???» «derivation ???» «derivation ???» «derivation ???» «derivation ???» ]

Cool thanks. There doesn’t seem to be any Nix pretty printer. I see nix eval, and try that:

$ nix eval 'import /Users/ian/.nix-profile/manifest.nix'
error: getting status of '/Users/ian/scratch/import /Users/ian/.nix-profile/manifest.nix': No such file or directory

What?? I read nix eval --help. The example uses a normal expression:

$ nix eval '(1 + 2)'
3

$ nix eval '1 + 2'
error: don't know what to do with argument '1 + 2'
Try 'nix --help' for more information.

Are you kidding me? Why? What? In case you were wondering: there is nothing in nix eval --help about this. But you know what, sure, fine; whatever.

$ nix eval '(import /Users/ian/.nix-profile/manifest.nix)'
(incredibly long single line of un-prettified output elided)

Cool. Okay. Cool. Sure.

This is very upsetting, but I think if I want to actually read this file, I have to do this:

$ nix-env -i jq
^Cerror: interrupted by the user

$ nix-env -iA nixpkgs.jq
installing 'jq-1.6'
...

$ nix eval --json '(import /Users/ian/.nix-profile/manifest.nix)' | jq .
[
  "/nix/store/vz6aphin9j1nkcmd6wcyi0hbk20kbxx1-jq-1.6-bin",
  "/nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10",
  "/nix/store/0ma07n8g55g2gxsbk917p2s0x6d9yhn1-keen4",
  "/nix/store/ps4yifn6srsqb2x1g3r3h7v0k91h88j1-sl-5.05",
  "/nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10",
  "/nix/store/ki7gssifc0xracrah8ygm63xj23wkjdz-nss-cacert-3.60"
]

You have got to be kidding me. Ugh. Yes; I understand; I know what’s happening here. I just… I just want to read this file. This is the best I can come up with:

$ cat ~/.nix-profile/manifest.nix | tr ';' '\n'

Are you happy, Nix? Are you okay this this? Anyway. It’s a list of what I think might be inputs to the derivation function? I find hello, and painstakingly beautify it by hand:

{
  name = "hello-2.10";
  out = { outPath = "/nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10"; };
  outPath = "/nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10";
  outputs = [ "out" ];
  system = "x86_64-darwin";
  type = "derivation";
  meta = {
    available = true;
    broken = false;
    changelog = "https://git.savannah.gnu.org/cgit/hello.git/plain/NEWS?h=v2.10";
    description = "A program that produces a familiar, friendly greeting";
    homepage = "https://www.gnu.org/software/hello/manual/";
    insecure = false;
    license = { ... };
    longDescription = "GNU Hello is a program that prints \"Hello, world!\" when you run it.\nIt is fully customizable.\n";
    maintainers = [ ... ];
    name = "hello-renamed";
    outputsToInstall = [ "out" ];
    platforms = [ ... ];
    position = "/nix/store/ys2x968psphylqb0gri928km5bny25ww-nixpkgs-21.05pre271444.9816b99e71c/nixpkgs/pkgs/applications/misc/hello/default.nix:15";
    priority = "90424915";
    unfree = false;
    unsupported = false;
  };
}

Hmm. It’s clearly not all the inputs. No builder, for example. I have no idea what this is. But I guess that when I use nix-env it like… evaluates expressions… and then looks at manifest.nix… to overwrite the meta field? Or something? I don’t know. Note that meta.name = "hello-renamed", although I don’t think that that field is meaningful.

Moving on.

nix-env --query. Not really anything interesting here. When does Nix use the term “package” and when does it use the term “derivation”? I still don’t understand that. It has an example to “show available packages in the Nix expression foo.nix,” but isn’t that really listing derivations? I assume? I don’t know.

The rest of nix-env is profile generation stuff. Nothing new here.

nix-build

We move on to nix-build, and learn this interesting tidbit:

nix-build is essentially a wrapper around nix-instantiate (to translate a high-level Nix expression to a low-level store derivation) and nix-store --realise (to build the store derivation).

Well I’ll be.

Nix build says it takes a path, but the first example is this:

$ nix-build '<nixpkgs>' -A firefox

So it means that it takes a Nix path, not a file path. Good to know, as I had assumed, you know, the thing that I’m used to.

I learn that nix-build will only build the default output by default, but that I can say:

$ nix-build '<nixpkgs>' -A openssl.all

To build all outputs.

Huh? Is (derivation).all a thing? Is that special syntax to nix-build? Let’s find out.

$ nix-build '<nixpkgs>' -A openssl.all
... lots of build output ...
$ ls result*
result@     result-bin@ result-dev@ result-doc@ result-man@
$ nix eval '(import <nixpkgs> {}).openssl.all'
trace: Warning: `stdenv.lib` is deprecated and will be removed in the next release. Please use `lib` instead. For more information see https://github.com/NixOS/nixpkgs/issues/108938
trace: lib.crossLists is deprecated, use lib.cartesianProductOfSets instead
trace: `lib.nixpkgsVersion` is deprecated, use `lib.version` instead!
trace: lib.zip is deprecated, use lib.zipAttrsWith instead
trace: warning: lib.readPathsFromFile is deprecated, use a list instead
trace: `mkStrict' is obsolete; use `mkOverride 0' instead.
trace: Warning: `showVal` is deprecated and will be removed in the next release, please use `traceSeqN`
trace: Warning: `stdenv.lib` is deprecated and will be removed in the next release. Please use `lib` instead. For more information see https://github.com/NixOS/nixpkgs/issues/108938
trace: Warning: `stdenv.lib` is deprecated and will be removed in the next release. Please use `lib` instead. For more information see https://github.com/NixOS/nixpkgs/issues/108938
trace: Warning: `stdenv.lib` is deprecated and will be removed in the next release. Please use `lib` instead. For more information see https://github.com/NixOS/nixpkgs/issues/108938
trace: Warning: `stdenv.lib` is deprecated and will be removed in the next release. Please use `lib` instead. For more information see https://github.com/NixOS/nixpkgs/issues/108938
trace: Warning: `stdenv.lib` is deprecated and will be removed in the next release. Please use `lib` instead. For more information see https://github.com/NixOS/nixpkgs/issues/108938'
^Cerror: interrupted by the user

It just hung for like ten seconds so I gave up.

Huuuuh. No idea. Okay. No idea. Not gonna… just no idea.

nix-shell

I continue on to nix-shell.

I think that nix-shell is kind of Nix’s killer feature, but I’ve only seen it once in the manual so far: when we were using nix-shell -p, all the way back in the quick start guide. The description in the manual does not really line up with what we observed:

The command nix-shell will build the dependencies of the specified derivation, but not the derivation itself. It will then start an interactive shell in which all environment variables defined by the derivation path have been set to their corresponding values, and the script $stdenv/setup has been sourced. This is useful for reproducing the environment of a derivation for development.

Okay. So it gives me an environment similar to the one in which my builder.sh ran. Makes sense: that’s what I would want, if I were iteratively working on a project.

If the derivation defines the variable shellHook, it will be evaluated after $stdenv/setup has been sourced. Since this hook is not executed by regular Nix builds, it allows you to perform initialisation specific to nix-shell.

Neat. So I could, if I wanted, set PS1 to some custom thing for my project. Or something; I dunno.

Let’s try this out. It doesn’t take long for me to get a shell that works for my-hello:

$ cat shell.nix
let stdenv = (import <nixpkgs> {}).stdenv; in
import ./hello.nix { inherit stdenv; }
$ nix-shell
(downloading a million dev dependencies)

[nix-shell:~/scratch]$ exit

Neat.

$ cat hello.nix
{ stdenv }:

derivation {
  inherit stdenv;
  system = "x86_64-darwin";
  name = "my-hello-1.0";
  builder = stdenv.shell;
  args = [ "-e" ./builder.sh ];
  messagefile = ./message.txt;
  outputs = ["foo" "out"];
  shellHook = "PS1='sup$ '";
}
$ nix-shell
sup$

Excellent.

More realistically, I could see myself setting env vars or something to enable more verbose/debug output only while in a shell. I dunno exactly what the use case is supposed to be here.

All options not listed here are passed to nix-store --realise, except for --arg and --attr / -A which are passed to nix-instantiate.

nix-build had this same sentence – I assume nix-shell is also just a wrapper around those, although that isn’t stated explicitly this time.

I am curious what the shell actually looks like, and how much control I have over it.

$ nix-shell
sup$ echo $SHELL
/nix/store/a1dq324pcgq8gm47q1xjnagj9vdx5k8i-bash-interactive-4.4-p23/bin/bash

Could I, like, configure it to use zsh, somehow? What “normal” things are available to me? What does this environment look like?

sup$ echo $PATH | tr : '\n'
/nix/store/a1dq324pcgq8gm47q1xjnagj9vdx5k8i-bash-interactive-4.4-p23/bin
/nix/store/kxx4n980xm8qwkk145jidx8s04005yq7-clang-wrapper-7.1.0/bin
/nix/store/1b8cbk68p7j7fn17z43rlwcf8ks4sa2i-clang-7.1.0/bin
/nix/store/4wfhsy75bs6lsw8c3nyxm869z4w5g8vd-coreutils-8.32/bin
/nix/store/x952ac06bg38jl9f5jgkg085vw6d21yh-cctools-binutils-darwin-wrapper-949.0.1/bin
/nix/store/kiclfrcdg2lxifq68q7d354dlaqk8136-cctools-binutils-darwin-949.0.1/bin
/nix/store/4wfhsy75bs6lsw8c3nyxm869z4w5g8vd-coreutils-8.32/bin
/nix/store/4wfhsy75bs6lsw8c3nyxm869z4w5g8vd-coreutils-8.32/bin
/nix/store/vaznvc9x54qsgrz8iial44ski19q5b00-findutils-4.7.0/bin
/nix/store/53mx7zdf69h7212879a6sl377zsy52k9-diffutils-3.7/bin
/nix/store/a7v6hnav786g120mbaqq6518bdinrnj5-gnused-4.8/bin
/nix/store/z30bw9b4chcvyyy1d8595ilhrgp1y40z-gnugrep-3.6/bin
/nix/store/18k3k2pnqj0xqlnw9nk3mxldi939byx4-gawk-5.1.0/bin
/nix/store/c1rdliivs0gvckc96v6mfbi7c111nkrf-gnutar-1.32/bin
/nix/store/f5ax81lvzbyyj7agvwynl1gh8y3vh204-gzip-1.10/bin
/nix/store/a9k58a6y353nkdp3zfwnkbaaqk2x5knd-bzip2-1.0.6.0.1-bin/bin
/nix/store/na3wscjazx8cwy9y1g2wv63yxvrnjh77-gnumake-4.3/bin
/nix/store/5y6lcfjghk5kbv4782vi7w79vz60gsyn-bash-4.4-p23/bin
/nix/store/zk4bd77s3m06rywwar3xj7h8yg6zlq7b-patch-2.7.6/bin
/nix/store/0drrznwp316knss36m177bk3r3pgj31i-xz-5.2.5-bin/bin
/Users/ian/.nix-profile/bin
/Users/ian/.opam/default/bin
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin
/Library/Apple/usr/bin
/Users/ian/.nodenv/shims
/Users/ian/.local/bin
/Users/ian/.pyenv/shims
/Users/ian/.rbenv/shims
/Users/ian/bin
/usr/local/sbin
./node_modules/.bin
/Users/ian/.cabal/bin
/Users/ian/src/httprintf/scripts

Okay. So it didn’t clear my PATH, as you can see from the things that you can see there. My private things. I still have httprintf installed? Man. Good times. What is ~/.local/bin? I don’t even know. Not sure why coreutils shows up three times here.

So the nix-shell environment is not supposed to be a clean-room, totally identical environment for every user on every machine. So it sure seems like I shouldn’t have to use bash just to use nix-shell. Maybe if I read on, I will learn more.

Hmm:

If [--pure] is specified, the environment is almost entirely cleared before the interactive shell is started, so you get an environment that more closely corresponds to the “real” Nix build. A few variables, in particular HOME, USER and DISPLAY, are retained. Note that ~/.bashrc and (depending on your Bash installation) /etc/bashrc are still sourced, so any variables set there will affect the interactive shell.

Yeah, okay, one of the first things it says is:

NIX_BUILD_SHELL: Shell used to start the interactive environment. Defaults to the bash found in PATH.

That’s… that sounds wrong?

$ which bash
/bin/bash

$ nix-shell

sup$ echo $SHELL
/nix/store/a1dq324pcgq8gm47q1xjnagj9vdx5k8i-bash-interactive-4.4-p23/bin/bash

sup$ suspend
[1]  + 83101 suspended (signal)  nix-shell

$ ps -p 83101
  PID TTY           TIME CMD
83101 ttys002    0:01.74 bash --rcfile /private/tmp/nix-shell-83101-0/rc

Argh. I don’t know the macOS equivalent of readlink /proc/83101/exe, so I have no idea what bash means there. But I can figure it out empirically:

$ export PATH=$HOME/bin:$PATH

$ echo 'echo "okay yeah"' > ~/bin/bash

$ chmod +x ~/bin/bash

$ which bash
/Users/ian/bin/bash

$ nix-shell
sup$

Lies!

Anyway:

You can use nix-shell as a script interpreter to allow scripts written in arbitrary languages to obtain their own dependencies via Nix. This is done by starting the script with the following lines:

#! /usr/bin/env nix-shell
#! nix-shell -i real-interpreter -p packages

Neat! That’s actually… kind of awesome. The weird double shebang is because on some operating systems – like mine, for example – you can only specify a single argument to the program in your shebang, for some reason. If you’ve never been bitten by that before: it’s super annoying.

Okay; that’s all it has to say about nix-shell.

Every time I run nix-shell it takes like two full seconds before I actually get to the prompt. That’s too many seconds. I guess realistically I should only start this once and I’m in it for a long time, but since I’m messing with it I keep jumping in and out. Still, anyone trying to write a shell.nix file is probably going to be annoyed by that.

Now: how do I use zsh?

$ NIX_BUILD_SHELL=zsh nix-shell
bash: no such option: rcfile

Hmmmmmmm. Well that’s… a very surprising message.

No level of verbosity actually tells me what it’s trying to do.

But I remember from the ps output that it’s calling bash --rcfile ..., so presumably… it’s doing something to invoke my NIX_BUILD_SHELLzsh – with $0 set to bash, and what I’m seeing is actually this error from zsh:

$ zsh --rcfile foo
zsh: no such option: rcfile

“Shaking my head.”

Well hmmmm. I find it rather painful to use bash interactively, and this seems like a bizarre dependency/restriction to place on nix-shell. I bet that I can make like… a wrapper script? Around zsh? There doesn’t seem to be any way to make it not pass --rcfile. Sigh.

nix-store

I move on to nix-store.

You generally do not need to run this command manually.

Okay. There are a few things it can do:

--realise, which I’ve seen before. There’s an example here:

$ nix-store -r $(nix-instantiate ./test.nix)
/nix/store/31axcgrlbfsxzmfff1gyj1bf62hvkby2-aterm-2.3.1

It says “This is essentially what nix-build does.” Neat.

Next we have --serve:

The operation --serve provides access to the Nix store over stdin and stdout, and is intended to be used as a means of providing Nix store access to a restricted ssh user.

This is different than the --builders thing we saw before. But maybe that’s implemented with this? No idea. There’s an example of adding an authorized_key that can only run nix-store --serve. Alright.

nix-store --gc is next. Has suboperations for printing live paths, dead paths, and GC roots.

$ nix-store --gc --print-roots
/Users/ian/scratch/result -> /nix/store/x98m491civwbjzwg5jbp7sgk16sd43gq-openssl-1.1.1i
/Users/ian/scratch/result-bin -> /nix/store/37xs5h3j5nw7ir9zm4g9wqm3i0dfmqpm-openssl-1.1.1i-bin
/Users/ian/scratch/result-dev -> /nix/store/x2n1j80f8gz2r34gpns3dfm5bfw30pk8-openssl-1.1.1i-dev
/Users/ian/scratch/result-doc -> /nix/store/wpjhgpchwcj0lj1lvbkf8sr5k0z4v8pn-openssl-1.1.1i-doc
/Users/ian/scratch/result-man -> /nix/store/94dqsl7hqy72bdji8bwajrq8q3l6v9hd-openssl-1.1.1i-man
/nix/var/nix/profiles/per-user/ian/channels-1-link -> /nix/store/98kbm9c2p7m8h1mjppvc0sam1vvkpz85-user-environment
/nix/var/nix/profiles/per-user/ian/profile-36-link -> /nix/store/0hd6v2qg6yb6vq0rswwcss2bmb5q7hpl-user-environment
/nix/var/nix/profiles/per-user/ian/profile-37-link -> /nix/store/1ycbnky2bp8sqa0lmglzshvc4db7hy6v-user-environment
/nix/var/nix/profiles/per-user/ian/profile-38-link -> /nix/store/d8ib4dy00afk7f5k1bxr3mys2n9nb6h3-user-environment
{lsof} -> /nix/store/2s8x59z3cly97fyc3hvlngl637snpwcq-swift-corefoundation
{lsof} -> /nix/store/4f5dzhsf7g1iyxgpgkaz5xlw1j7wwlcn-bzip2-1.0.6.0.1
{lsof} -> /nix/store/541lzmhppr82600vz7ap54n22dz7gydf-ICU-66108
{lsof} -> /nix/store/66xjqfj62sgdasiwqg9ynnv2w29v7761-editline-1.17.0
{lsof} -> /nix/store/7x357k8k3rwbq52j44mc11v1mv9hr4b7-libssh2-1.9.0
{lsof} -> /nix/store/80lqr4rajdip60b7ln0w9bg5kcjn1vzc-curl-7.74.0
{lsof} -> /nix/store/85sgz75qnywgcxsn0y41bykwkkfsxyl2-aws-c-cal-0.4.5
{lsof} -> /nix/store/8y9r21h0aqh3j84ipagsy8q1b5zv50cg-aws-checksums-0.1.11
{lsof} -> /nix/store/aqf78c2x1kwv9i123p55m3f4iyx9fz3c-openssl-1.1.1i
{lsof} -> /nix/store/awgjr2jdyr2gqhkd8nb03yh8bzcg1zci-xz-5.2.5
{lsof} -> /nix/store/c1ln5y1p009wn44j03z1r7rdhmjx3k2j-curl-7.74.0
{lsof} -> /nix/store/cgswwsl39w991i92f09mxbc58rlqr9x9-aws-c-common-0.4.64
{lsof} -> /nix/store/cz5fh11s985jncxmy0dwnxk079ln7mq3-brotli-1.0.9-lib
{lsof} -> /nix/store/i5pzypw0d97knbnq6sisvf1wkkbbb91g-libkrb5-1.18
{lsof} -> /nix/store/kpz8mj9iya2api2xkpjvbl71qnfjyz4l-aws-c-event-stream-0.2.6
{lsof} -> /nix/store/lzx704yj5p6rvwm44jrb20d4gsm4xfr4-aws-sdk-cpp-1.8.121
{lsof} -> /nix/store/m99vmj6i29lr6b66lqr7p5a3ji1z6zrc-sqlite-3.34.1
{lsof} -> /nix/store/mw590ax8x2n9161hwvnl9j5156di6d47-libc++abi-7.1.0
{lsof} -> /nix/store/phxiw7s3r2qc8q51a5rkcgdkx23x2mlm-libiconv-50
{lsof} -> /nix/store/pqajcmw6jmq2i8ka001z53r1a09w4y67-libssh2-1.9.0
{lsof} -> /nix/store/py64zn5n0vmcv1irdyr7vgn2pp7wx52s-nghttp2-1.41.0-lib
{lsof} -> /nix/store/q0z2kvkgrpvaipa87jl98qh7g5pym5fj-nix-2.3.10
{lsof} -> /nix/store/q1rhw9f30fm0yzx1hic1hgj31sfc6k4p-libkrb5-1.18
{lsof} -> /nix/store/rlbvxb9cjb15ify4i5rpg7yk1wpsiqsd-libsodium-1.0.18
{lsof} -> /nix/store/s5rd3hgdirrdwdfbn7m4hd0i3d2zqpz5-libxml2-2.9.10
{lsof} -> /nix/store/swm0jwbcn54rzkv8264gv7774lijmzpv-boost-1.69.0
{lsof} -> /nix/store/vrmgqjl51gwf47i5i1rbs7dnkb1g1pvf-libc++-7.1.0
{lsof} -> /nix/store/wphpzw2swy36pm4ph3r1zfvwfj2njxjf-zlib-1.2.11
{lsof} -> /nix/store/x98m491civwbjzwg5jbp7sgk16sd43gq-openssl-1.1.1i
{lsof} -> /nix/store/yi9klhxd1243l7inrkn12f6lwzp9bki4-Libsystem-1238.60.2
{lsof} -> /nix/store/ypj3w1f8lpk9gqlvbnb4dydggir28wnp-aws-c-io-0.7.1
{lsof} -> /nix/store/z716g9svwi8gxwc3ddbyh9jkr6xma3z2-boehm-gc-8.0.4
{lsof} -> /nix/store/zxya6i6ncqs8q6fq3mcl0igflmy2219n-nghttp2-1.41.0-lib

Huh! I wonder what I have running that’s using those paths. I don’t think I have anything except the nix-store process itself. Are those all the runtime dependencies of nix-store? That seems… like a lot. But maybe! I wonder how I can ask Nix to show me runtime dependencies of a specific package output.

Huh. nix-store --delete allows me to delete unreachable paths one by one, if I want to.

nix-store --query has a lot of subcommands. Let’s try them:

$ nix-store --query --outputs /nix/store/rsrwc2m01bh8ai0jfwjx1qlgvwshcm23-hello-2.10.drv
/nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10

Okay. Had to pass a drv file. Don’t really know what those are.

$ nix-store --query --requisites /nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10
/nix/store/mw590ax8x2n9161hwvnl9j5156di6d47-libc++abi-7.1.0
/nix/store/vrmgqjl51gwf47i5i1rbs7dnkb1g1pvf-libc++-7.1.0
/nix/store/541lzmhppr82600vz7ap54n22dz7gydf-ICU-66108
/nix/store/aqf78c2x1kwv9i123p55m3f4iyx9fz3c-openssl-1.1.1i
/nix/store/wphpzw2swy36pm4ph3r1zfvwfj2njxjf-zlib-1.2.11
/nix/store/pqajcmw6jmq2i8ka001z53r1a09w4y67-libssh2-1.9.0
/nix/store/5y6lcfjghk5kbv4782vi7w79vz60gsyn-bash-4.4-p23
/nix/store/yi9klhxd1243l7inrkn12f6lwzp9bki4-Libsystem-1238.60.2
/nix/store/q1rhw9f30fm0yzx1hic1hgj31sfc6k4p-libkrb5-1.18
/nix/store/zxya6i6ncqs8q6fq3mcl0igflmy2219n-nghttp2-1.41.0-lib
/nix/store/c1ln5y1p009wn44j03z1r7rdhmjx3k2j-curl-7.74.0
/nix/store/s5rd3hgdirrdwdfbn7m4hd0i3d2zqpz5-libxml2-2.9.10
/nix/store/2s8x59z3cly97fyc3hvlngl637snpwcq-swift-corefoundation
/nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10

Are these runtime dependencies of hello? That’s… no. These are build dependencies. Right? I still don’t really know what “the closure of a store path” means. The manual explains why I might use this:

This query can be used to implement various kinds of deployment. A source deployment is obtained by distributing the closure of a store derivation. A binary deployment is obtained by distributing the closure of an output path. A cache deployment (combined source/binary deployment, including binaries of build-time-only dependencies) is obtained by distributing the closure of a store derivation and specifying the option --include-outputs.

Okay. I sort of zone out, and have trouble following this. I don’t know what “the closure of a store derivation” means. Is the thing I printed above “the closure of an output path”? It sure doesn’t seem like those should be runtime dependencies. I don’t know.

Oh wow, but if I run it with a derivation:

$ nix-store --query --requisites /nix/store/rsrwc2m01bh8ai0jfwjx1qlgvwshcm23-hello-2.10.drv | head
/nix/store/1i5y55x4b4m9qkx5dqbmr1r6bvrqbanw-multiple-outputs.sh
/nix/store/bnj8d7mvbkg3vdb07yz74yhl3g107qq5-patch-shebangs.sh
/nix/store/cickvswrvann041nqxb0rxilc46svw1n-prune-libtool-files.sh
/nix/store/cl3qd985p1yxyfkj96v0hqxiy3w69xq5-compress-man-pages.sh
/nix/store/dsyj1sp3h8q2wwi8m6z548rvn3bmm3vc-builder.sh
/nix/store/fyaryjvghbkpfnsyw97hb3lyb37s1pd6-move-lib64.sh
/nix/store/h54dzwd7rdh2jlcv91424csl6d0ccgjy-strip.sh
/nix/store/71d9qf26s1nzrp34cqw1hh0yrx0h1kbx-cpio.drv
/nix/store/7jl6nhfsc0mchccg5w40rfm4zfshpsag-bzip2.drv
/nix/store/bci5x35014bbpc87x7ds2n2ffh5syns1-sh.drv

I get way more. So I guess GNU hello has a runtime dependency on openssl. Who knew.

I can see the reverse, and see anything that refers to hello:

$ nix-store --query --referers /nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10
/nix/store/284k5vqfd305b3gwsi5b812mi0r5y14d-env-manifest.nix
/nix/store/0hd6v2qg6yb6vq0rswwcss2bmb5q7hpl-user-environment
/nix/store/gdq1348ld0qcgf7csf91331wqbls93yw-env-manifest.nix
/nix/store/1ycbnky2bp8sqa0lmglzshvc4db7hy6v-user-environment
/nix/store/47srnbccwfzn29psab0h6ca7vrl9bjm1-env-manifest.nix
/nix/store/lb1xkhvrdrsjhfmxlgdqz19nvg6aay3v-env-manifest.nix
/nix/store/47xq5hw013axr41nhay5fjb4ns9mjj0k-user-environment
/nix/store/ghcmiqqd9dy5bm465b5q18fwblakycza-env-manifest.nix
/nix/store/5mhy1r42mg3qmb749gr633cb7r437vw7-user-environment
/nix/store/ghyl8jvypws36wga7pd2if5km36m6irw-env-manifest.nix
/nix/store/d8ib4dy00afk7f5k1bxr3mys2n9nb6h3-user-environment
/nix/store/k68v9b59l2gyiikfwpj1yj7pvxs194zz-env-manifest.nix
/nix/store/hvshm1v6g4d1k5k8k02jk0386850q0qj-user-environment
/nix/store/wwls7pkgj6ndflhg8mxgslyx9fjax93b-env-manifest.nix
/nix/store/vdgl8anh9g0nmwx7zb3q244bbgm1nknb-user-environment

Is that really how we’re going to spell “referers”? What is this, HTTP? (It seems to accept the correct spelling also.)

Neat. I can find a “deriver” – a term that is in the glossary but which I had forgotten:

$ nix-store --query --deriver /nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10
/nix/store/6rcmdg8cxrx2wj86v7h9hzgppcpbbxgh-hello-2.10.drv

That file does not actually exist, which is… weird?

I can print a cute tree:

$ nix-store --query --tree /nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10 | head
/nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10
+---/nix/store/2s8x59z3cly97fyc3hvlngl637snpwcq-swift-corefoundation
    +---/nix/store/541lzmhppr82600vz7ap54n22dz7gydf-ICU-66108
    |   +---/nix/store/vrmgqjl51gwf47i5i1rbs7dnkb1g1pvf-libc++-7.1.0
    |   |   +---/nix/store/mw590ax8x2n9161hwvnl9j5156di6d47-libc++abi-7.1.0
    |   |   |   +---/nix/store/mw590ax8x2n9161hwvnl9j5156di6d47-libc++abi-7.1.0 [...]
    |   |   +---/nix/store/vrmgqjl51gwf47i5i1rbs7dnkb1g1pvf-libc++-7.1.0 [...]
    |   +---/nix/store/541lzmhppr82600vz7ap54n22dz7gydf-ICU-66108 [...]
    +---/nix/store/c1ln5y1p009wn44j03z1r7rdhmjx3k2j-curl-7.74.0
    |   +---/nix/store/aqf78c2x1kwv9i123p55m3f4iyx9fz3c-openssl-1.1.1i

Alright. I’m bored of nix-store --query. But I push on.

nix-store --query --graphml? Hmm. I don’t know how to consume such a format. I google and find something called graph-easy.

$ nix-env -iA nixpkgs.graph-easy
installing 'perl5.32.0-Graph-Easy-0.76'
...
created 55 symlinks in user environment

$ nix-store --query --graphml /nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10 > deps.xml

$ graph-easy deps.xml deps.svg
zsh: /Users/ian/.nix-profile/bin/graph-easy: bad interpreter: /nix/store/49wx1mcvigd35dd8ha7280k9b4ixd6mj-perl-5.32.0/bin/perl -w -I/nix/store/49wx1mcvigd35dd8ha7280k9b4ixd6mj-perl-5.32.0/: exec format error

Hey yeah look! It’s the thing I was talking about earlier. macOS can’t deal with that. How would I… fix this? I don’t know. I don’t know how to patch it to be compatible. For the record:

$ head -n1 $(which graph-easy)
#!/nix/store/49wx1mcvigd35dd8ha7280k9b4ixd6mj-perl-5.32.0/bin/perl -w -I/nix/store/49wx1mcvigd35dd8ha7280k9b4ixd6mj-perl-5.32.0/lib/perl5/site_perl -I/nix/store/49wx1mcvigd35dd8ha7280k9b4ixd6mj-perl-5.32.0/lib/perl5/site_perl -I/nix/store/49wx1mcvigd35dd8ha7280k9b4ixd6mj-perl-5.32.0/lib/perl5/site_perl -I/nix/store/49wx1mcvigd35dd8ha7280k9b4ixd6mj-perl-5.32.0/lib/perl5/site_perl -I/nix/store/49wx1mcvigd35dd8ha7280k9b4ixd6mj-perl-5.32.0/lib/perl5/site_perl -I/nix/store/9a3i3lmx6j4d9my3z0i7qzlbda73axqr-perl5.32.0-Graph-Easy-0.76/lib/perl5/site_perl

That’s… it seems like we could probably get away without all of those. The same search path is repeated five times. Not that it matters here.

So… I give up visualizing my runtime dependencies. I suppose that I don’t know how to render GraphML output.

$ nix-store --query --binding name /nix/store/rsrwc2m01bh8ai0jfwjx1qlgvwshcm23-hello-2.10.drv
hello-2.10

$ nix-store --query --binding meta /nix/store/rsrwc2m01bh8ai0jfwjx1qlgvwshcm23-hello-2.10.drv
error: derivation '/nix/store/rsrwc2m01bh8ai0jfwjx1qlgvwshcm23-hello-2.10.drv' has no environment binding named 'meta'

Okay; this is interesting. The manual says that nix-store --query --binding name paths:

Prints the value of the attribute name (i.e., environment variable) of the store derivations paths. It is an error for a derivation to not have the specified attribute.

So I guess that only some paths are passed on as environment variables to the derivation’s builder – and meta is not, as I would expect. Any others? I don’t know – there doesn’t seem to be a way to list all bindings.

Just want to point out that this is yet a third definition of the word “path.” I wish that the Nix manual would use the term “store path” when it means “store path.”

Oh, there’s an example here that shows me how to render GraphML. Except… no. There’s just a plain --graph. I just… missed that. I just skipped that suboperation completely. Okay. It’s normal. You can use graphviz and everything is fine. I apologize for ever making you think about GraphML.

$ nix-env -iA nixpkgs.graphviz
...

$ nix-store --query --graph /nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10 | dot -Tsvg > deps.svg

And here it is!

hello depedency graph

It’s not not most beautiful. But it does explain why hello depends on so much stuff: because I’m on a Mac.

Okay.

nix-store --add lets me add individual files to the store. Don’t know why I would want that, but that’s okay – the manual did warn me that I probably don’t care about most of this stuff.

nix-store --add-fixed is a separate command instead of an option on --add, and it lets me add stuff with a hash. Okay.

nix-store --verify will check that everything is okay with the Nix database and fix stuff if it isn’t.

$ nix-store --verify
reading the Nix store...
checking path existence...

Phew. Seems like I’m okay. I even collect garbage and try nix-store --verify --check-contents. Clean bill of health.

I don’t care about the next few commands.

nix-store --dump and nix-store --restore seems like the way to copy stuff from one computer to another – although presumably nix copy is the high-level wrapper around them that I actually want to use.

Oh hmm no – I go on to learn nix-store --export:

This is like nix-store --dump, except that the NAR archive produced by that command doesn’t contain the necessary meta-information to allow it to be imported into another Nix store (namely, the set of references of the path).

Wait, what? What’s “the set of references of the path”? What does that mean? Why do I need it?

nix-store --optimise replaces duplicated files with hardlinks. I remember doing this on my NixOS box at some point, before I figured out how to set up NixOS to automatically collect garbage and I was always running out of disk space (because I did have automatic updates set up). Ha: “Use -vv or -vvv to get some progress indication.” I’m glad they say that, but it’s not great that they have to say that, right?

Aha! nix-store --read-log. That’s how I can look at logs.

$ nix-store --read-log  /nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10
error: build log of derivation '/nix/store/pakmb65sf3g2hkbm1fdgk2fh6hiij720-hello-2.10' is not available

Boo. I did collect garbage recently. Maybe that wiped my logs? Hmm.

No, but it seems like it did delete the .drv. And maybe only .drvs have logs?

$ nix-env -iA nixpkgs.hello
replacing old 'hello-2.10'
installing 'hello-2.10'
building '/nix/store/y4v15zqm0vl8dcgqn9lin2h83jzm817a-user-environment.drv'...
created 161 symlinks in user environment

$ nix-store --read-log /nix/store/rsrwc2m01bh8ai0jfwjx1qlgvwshcm23-hello-2.10.drv
error: build log of derivation '/nix/store/rsrwc2m01bh8ai0jfwjx1qlgvwshcm23-hello-2.10.drv' is not available

Boo. Oh:

if the path was downloaded as a pre-built binary through a substitute, then the log is unavailable.

Fair enough.

$ nix-env -iA ianpkgs.hello
...

$ nix-store --read-log /nix/store/ksf5zvkxln1n8la0ix936rsj9xbpzjjb-my-hello-1.0-foo

It worked! But… it didn’t print anything. But it worked! I guess!

nix-store --dump-db and nix-store --load-db. Probably not gonna use those.

nix-store --print-env sounds interesting.

$ nix-store --print-env /nix/store/xhcsmjw41i2yhh569visqsz0cvipl897-my-hello-1.0.drv
export builder; builder='/nix/store/5y6lcfjghk5kbv4782vi7w79vz60gsyn-bash-4.4-p23/bin/bash'
export foo; foo='/nix/store/ksf5zvkxln1n8la0ix936rsj9xbpzjjb-my-hello-1.0-foo'
export messagefile; messagefile='/nix/store/nmmdalhj24wsz2zi342ymavzqklywk97-message.txt'
export name; name='my-hello-1.0'
export out; out='/nix/store/15a167ahlp23a6djv35n9mr2brz1al0l-my-hello-1.0'
export outputs; outputs='foo out'
export shellHook; shellHook='PS1='\''sup$ '\'''
export stdenv; stdenv='/nix/store/sp860v02a8j8s7vqhjrc94hgsklqdvdw-stdenv-darwin'
export system; system='x86_64-darwin'
export _args; _args=''-e' '/nix/store/6bqabm9hb9l3c0kh8hnvbdbc7s26jiwz-builder.sh''

Neat! Okay. I like that; that feels good. That command just made things a lot more concrete for me.

Lastly we have nix-store --generate-binary-cache-key which feels a little different than the other commands here but okay sure.

Chapter 23. Utilities

nix-channel. Already covered that thoroughly; doesn’t look like there’s anything new or interesting here. Same for nix-collect-garbage.

nix-copy-closure; seems fine. Connects to the remote host twice, once to do the diff and once again to do the copy. No objections.

This has a really interesting example:

$ nix-copy-closure --from alice@itchy.labs \
   /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4
$ nix-env -i /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4

I thought nix-env -i took a “symbolic name” or whatever. It can take a store path too? This is definitely not documented. I try it out:

$ nix-env -i /nix/store/15a167ahlp23a6djv35n9mr2brz1al0l-my-hello-1.0
installing 'my-hello-1.0'
building '/nix/store/vfwngc3260y17nx3k5bqhhcj9gpjbnc7-user-environment.drv'...
warning: not including '/nix/store/15a167ahlp23a6djv35n9mr2brz1al0l-my-hello-1.0' in the user environment because it's not a directory
created 158 symlinks in user environment

Skeptical side eyes. Don’t know. I wish that man nix-env would have something to say about this.

Next we have nix-daemon. The manual says this:

The Nix daemon is necessary in multi-user Nix installations. It performs build actions and other operations on the Nix store on behalf of unprivileged users.

And nothing more. Shortest command yet.

nix-hash – I don’t know if there’s I reason I would ever want to use this.

nix-instantiate. Okay. So this command is in the “utility” section but it seems like kind of a major, fundamental piece of Nix on top of which many other commands are built.

$ nix-instantiate hello.nix
error: cannot auto-call a function that has an argument without a default value ('stdenv')

Sigh yes fair enough. Wait a minute – is this how I can pretty-print?

$ nix-instantiate --parse hello.nix
({ stdenv }: (derivation  { name = "my-hello-1.0"; system = "x86_64-darwin"; outputs = [ ("foo") ("out") ]; builder = (stdenv).shell; args = [ ("-e") (/Users/ian/scratch/builder.sh) ]; messagefile = /Users/ian/scratch/message.txt; inherit stdenv ; shellHook = "PS1='sup\$ '"; }))

Sigh. Of course not.

$ nix-instantiate --eval hello.nix
<LAMBDA>

Alright. I happen to remember that my shell.nix is just the application of the hello.nix function with stdenv, so:

$ nix-instantiate shell.nix
warning: you did not specify '--add-root'; the result might be removed by the garbage collector
/nix/store/xhcsmjw41i2yhh569visqsz0cvipl897-my-hello-1.0.drv!foo

Huh! !foo. Weird. That is the default output for my package. The filename is just the part before the !, though.

I’m not sure why nix-instantiate --eval exists. It feels very different from the “compile a derivation into a .drv file,” which seems like its main job.

Anyway.

nix-prefetch-url – “This command is just a convenience for Nix expression writers.” It will tell me the hash to put in my fetchurl command. That’s nice!

And it’s also the last command! Nice. Okay. We did it. We know all of Nix.

Obviously the manual… completely omitted the nix command. So. We don’t know that one. But… probably nothing important in there, right?


  • What’s up with that weird array situation?
  • What is a pure/impure shell?
  • What is the use case for nix-env --set?
  • Why can’t I say nix-env -eA nixpkgs.hello? Why do I need to use the “symbolic name”?
  • What’s the “user environment build script”
  • How does nix-env --set-flag work?
  • How do I pretty print Nix expressions?
  • Why does nix eval need its argument to be in parentheses?
  • What is manifest.nix?
  • How can I make nix-shell use zsh?
  • What’s the difference between a package and a derivation?
  • What is openssl.all actually?
  • How can I ask Nix to show me runtime dependencies of an output?
  • Are runtime dependencies known per-derivation, or per-output?
  • How can I inspect / pretty print a store derivation?
  • What’s the difference between nix-store --dump and nix-store --export?
  • Why doesn’t nix-store --read-log work?
  • What does nix-env -i /nix/store/store-path do?
  • How do I (locally) “fork” a derivation?