Nix is a package manager, born form a research project, with interesting design and properties.
Not to be confused with NixOS, which is:
- an operating system
- with a Linux kernel
- based on nix as a package manager
- with a descriptive configuration system from which actual configuration is generated
On macOS, there is currently an issue with /
being made entirely read only since Catalina. But there is an install script approved and to be merged upstream.
Here's how to install Nix in general:
# This will work in the future but currently fails on Catalina after nixbld* user creation
$ sh <(curl https://nixos.org/nix/install) --daemon
If the above fails on Catalina after the user creation step, proceed as follows:
$ curl -O https://raw.githubusercontent.com/NixOS/nix/1c6706167844014d2084c332a0829e40100f281c/scripts/create-darwin-volume.sh
$ bash create-darwin-volume.sh
$ sh <(curl https://nixos.org/nix/install) --daemon
You should be good!
nix-shell
evaluates nix expressions and commands in an environment of your control:
The following command runs the shell command python3 --version
within an isolated and ephemeral enviroment where all dependencies for the python3
are satisfied, with environment variables, paths, etc... set, but anhy other package invisible. THis means that any dependency of python3 and python3 itself will be fetched as needed.
$ nix-shell --packages python3 --run 'python3 --version'
At the end of the nix-shell
command, the environment disappears, but the packages remain in the so-called nix store. This looks eerily like docker run --rm
.
Nix downloads and unpacks packages in that store. This store is content-addressed via a hash, a bit like git objects or docker layers. This hash is created from the package version and its dependencies, so if any of those change, the hash changes. You can see it as an obscure prefix here:
$ ls /nix/store | grep python3-3
0hi6adsig2nnp0dxw4wdiswlqfamqvfs-python3-3.8.2
1s66apwyh9ia021xigf19bvjw7krhqxf-python3-3.7.6
This is cool, but sometimes you just want to expose the package contents outside of a nix-shell
.
But if you want more casual access, you can install packages into a profile. What is a profile, you ask? It's a file hierarchy not unlike the FHS, where relevant files and directories get symlinked, so that you have direct access to them, the usual way. A user's default profile lies at ~/.nix-profile
.
You could do this, but it would pick an arbitrary version (like an alpha one), which may not be what you want, so let's pretend with a dry-run:
$ nix-env --install --dry-run python3
Instead, you can specify an exact version (called "derivations" in nix parlance):
$ nix-env --install python3-3.7.6
If the version exists, it will check whether a prebuilt binary package exists, otherwise it will attempt to build it from source (and will recursively do so for any required dependency).
After that, the python3
command will be available right in in your user's path, and more:
$ rehash # zsh: refresh command hash table, just in case
$ hash -r # bash: refresh command hash table, just in case
$ which python3
/Users/<user>/.nix-profile/bin/python3
$ ls -l .nix-profile/lib/python3.8
lrwxr-xr-x 1 root wheel 71 1 Jan 1970 .nix-profile/lib/python3.8 -> /nix/store/0hi6adsig2nnp0dxw4wdiswlqfamqvfs-python3-3.8.2/lib/python3.8
$ python3
Python 3.7.6 (default, Mar 11 2020, 12:53:39)
[Clang 7.1.0 (tags/RELEASE_710/final)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
$ nix-env --uninstall python3
$ which python3
/usr/bin/python3
So, from a pragmatic point of view, "installing" is basically just setting up some symlinks from the profile to the store, and "uninstalling" is just removing them.
Now how to find out what you have installed in your profile?
$ nix-env --query
And how to find what to install? This queries the current "channel", which is the source of derivations.
$ nix-env --query --available
But those names are quite opaque, a description might help discriminate:
$ nix-env --query --available --description
What are the available versions, say, for python3?
$ nix-env --query --available 'python3'
python3-3.5.9
python3-3.5.9
python3-3.6.10
python3-3.6.10
python3-3.7.6
python3-3.7.6
python3-3.7.6
python3-3.7.6
python3-3.8.2
python3-3.8.2
python3-3.8.2
python3-3.9.0a4
Do I have any installed or in the store?
$ nix-env --query --available --status 'python3'
--S python3-3.5.9
--S python3-3.5.9
--S python3-3.6.10
--S python3-3.6.10
-PS python3-3.7.6
--S python3-3.7.6
IPS python3-3.7.6
--S python3-3.7.6
-PS python3-3.8.2
--S python3-3.8.2
--S python3-3.8.2
--- python3-3.9.0a4
I stands for installed, P stands for present (i.e in the local nix store), S stands for substitute available.
But why is there the same version multiple times? Attribute paths:
nix-env --query --attr-path --status --available python3
--S nixpkgs.python35 python3-3.5.9
--S nixpkgs.python35Full python3-3.5.9
--S nixpkgs.python36Full python3-3.6.10
--S nixpkgs.python36 python3-3.6.10
-PS nixpkgs.python3 python3-3.7.6
--S nixpkgs.python37Full python3-3.7.6
-PS nixpkgs.sourcehut.python python3-3.7.6
--S nixpkgs.python3Full python3-3.7.6
IPS nixpkgs.python38 python3-3.8.2
--S nixpkgs.python39Full python3-3.8.2
--S nixpkgs.python38Full python3-3.8.2
--- nixpkgs.python39 python3-3.9.0a4
Attributes is a fairly advanced feature in itself, but here this allows differentating between variants of the the same version:
$ nix-env --install --attr nixpkgs.python35
Now, maybe because I ran a nix-shell
command, or I uninstalled a package, I have some unneeded cruft in the nix store. Time for some garbage collection:
$ nix-collect-garbage
This will remove anything from the nix store not currently in use by a profile or a nix-shell command.
Except not! Because each change to installations creates a "generation", which allows you to rollback to a previous installation state easily:
$ nix-env --rollback
You can clean up all non-current generations at once and clean up the leftovers:
$ nix-env --delete-generations old
$ nix-collect-garbage
That should be enough for basic usage of the Nix package manager! If you want to know more, including about channels, profiles, generations, and building your own derivations from nix expressions, read the official guide!