The Nix package manager has a number of unique features that differentiate it from Homebrew. These enable it to provide a much more stable and smoother installation experience when managing different environments and dependencies.
The smoother experience is due to Nix using binaries when installing software. This eliminates the need to compile from source.
Increased stability is supported through a generation system. Each new package installation creates a new generation of your environment which can be easily rolled back should an error occur.
Nix also supports creating isolated development environments. These isolated development environments also have their own generation roll back system. This allows you to completely compartmentalize your system into many stable custom environments.
Nix Package Manager Features
The following are some of the differences compared to Homebrew and how they can make the user experience a lot more pleasant.
Nix channels contain previously compiled binary versions of a package from where downloads are sourced. This means that you do not need to download and compile a package, its dependencies, and pray, but instead you download and untar a working tarball from a channel. Use the following command to view your download channels:
brian@rhodecode:~# nix-channel --list nixpkgs http://nixos.org/channels/nixpkgs-unstable
Nix --rollback and --switch-generation functions provide the additional stability. Each package installation creates a new generation of your Nix environment. This means that if a download breaks a build you simply rollback to the last previous working generation.
The
nix-shell
is used to provide isolated environments.
Making the Switch to Nix
To switch package handling from Homebrew to Nix, use the following steps:
Find out which packages you currently manage with homebrew:
$ brew list openssl python treadline sqlite
Remove these packages from your system.
brew remove package-name
Remove Homebrew using the following steps:
cd brew --prefix rm -rf Cellar brew prune rm git ls-files rm -r Library/Homebrew Library/Aliases Library/Formula Library/Contributions rm -rf .git rm -rf ~/Library/Caches/Homebrew`
Install Nix on your operating system with the following command:
$ bash <(curl https://nixos.org/nix/install)
- For more information, see Getting Nix
Note: the installer sources a Nix profile in the
~/.profile
or~./bash_profile
file by adding the following line:if [ -e /Users/brian/.nix-profile/etc/profile.d/nix.sh ]; then . /Users/brian/.nix-profile/etc/profile.d/nix.sh; fi # added by Nix installer
If during installation this is not created, you should add it manually to the appropriate file.
Once Nix is downloaded. Reinstall the packages you require with the Nix install command:
# download package-name nix-env --install package-name # see all available packages nix-env --query --available
For more information, see Nix Installing Packages
Note: Package names in Homebrew and Nix may not be the exact same, but should be similar. You can view all package names using this command:
brian$ nix-env -qaP
Installing with Nix
Once you have reconfigured your package manager to Nix, you can see where the default packages are using the which
command, and rollback nix-env --rollback
or switch generation nix-env --switch-generation generation-number
.
brian@rhodecode:~# which python/usr/bin/python
brian@rhodecode:~# nix-env -i python
installing `python-2.7.8'
building path(s) /nix/store/iqm4bnb1assfdwka4xmmky96yrnhj91w-user-environment'
created 263 symlinks in user environment
brian@rhodecode:~# which python
/root/.nix-profile/bin/python
When Installs Go Bad
This is where Nix comes into a league of its own. If an install wrecks your development environment, you can simply rollback to the previous working one.
Additionally, the Nix Garbage collection function allows you to quickly clean up your environment once you have carried out a number of changes:
nix-collect-garbage -d
See the following example for an explanation of these functions:
[nix-shell:~]$ nix-env -i python3 # Install Python 3
replacing old `python3-3.4.1'\n
installing `python3-3.4.1'\n
[nix-shell:~]$ which python3 # based on latest gen\n/root/.nix-profile/bin/python3\n
[nix-shell:~]$ nix-env --list-generations\n
1 2014-07-27 10:18:50
2 2014-07-27 10:31:27
3 2014-07-27 14:06:42
4 2014-07-27 14:09:27
5 2014-07-27 14:16:44
6 2014-07-27 14:16:53
7 2014-07-28 08:13:07
8 2014-07-28 08:20:00
9 2014-07-28 08:21:42
10 2014-07-28 08:22:31 (current)
[nix-shell:~]$ nix-env --switch-generation 4
switching from generation 10 to 4
[nix-shell:~]$ which python3 # based on gen 4
/usr/bin/python3
[nix-shell:~]$ nix-env --switch-generation 10
switching from generation 4 to 10
[nix-shell:~]$ nix-collect-garbage -d
removing old generations of profile /nix/var/nix/profiles/default
removing generation 1
.
.
removing generation 9
removing old generations of profile /nix/var/nix/profiles/per-user/root/channels\nfinding garbage collector roots...
deleting garbage...
875 store paths deleted, 552.07 MiB freed
[nix-shell:~]$ nix-env --list-generations
10 2014-07-28 08:22:31 (current)
[nix-shell:~]$ exit
brian@rhodecode:~/nix-project#
Uninstalling with Nix
To remove a package with Nix, use the -e
flag. For example:
nix-env -e package-name
Nix will gracefully handle the uninstall and also remove any symlinks created to support the package:
brian@rhodecode:~# nix-env -e git
uninstalling `git-2.0.2'\nbuilding path(s) `/nix/store/vz37m6k23r7j5mnvmij92bnym6j16cbl-user-environment'
created 6 symlinks in user environment
Setting Up an Isolated Environment
The default.nix
file is used to define an environment. Each system can have multiple default.nix
files which create independant work areas. This enables multiple different package versions to be installed and run on the same machine without introducing dependency conflicts.
Additionally, this allows very easy environment sharing because you can share your default.nix
file, and colleagues will have an exact copy of your setup.
Configuring default.nix
Configuring a default.nix
file requires declaring your setup within the file. This is why it is called a declarative setup.
Every time you run nix-shell
, Nix will ensure that you get a consistent set of binaries corresponding to your specification.
For specific projects you should install the packages you need in a project folder that also contains a default.nix
file. This way project specific packages do not confllict with system packages.
To create a project specific work area use the following steps:
- Create a new folder
mkdir new-project
Navigate to that folder and create a
default.nix
file. Within this file you must specify the particular packages for that work environment. Use the following examples to create your owndefault.nix
file. This exampledefault.nix
file specifies the required packages in thebuildInputs
section.In this example, the
rhodecode-docs
project requires:{ system ? builtins.currentSystem }: let pkgs = import <nixpkgs> { inherit system; }; inherit (pkgs) fetchurl fetchgit; buildPythonPackage = pkgs.python27Packages.buildPythonPackage; python = pkgs.python27Packages.python; Jinja2 = buildPythonPackage rec { name = "Jinja2-2.7.3"; src = fetchurl { url = "http://pypi.python.org/packages/source/J/Jinja2/${name}.tar.gz"; md5 = "b9dffd2f3b43d673802fe857c8445b1a"; }; propagatedBuildInputs = [ MarkupSafe ]; }; MarkupSafe = buildPythonPackage rec { name = "MarkupSafe-0.19"; src = fetchurl { url = "https://pypi.python.org/packages/source/M/MarkupSafe/${name}.tar.gz"; md5 = "ccb3f746c807c5500850987006854a6d"; }; }; Pygments = buildPythonPackage rec { name = "Pygments-1.6"; src = fetchurl { url = "https://pypi.python.org/packages/source/P/Pygments/${name}.tar.gz"; md5 = "a18feedf6ffd0b0cc8c8b0fbdb2027b1"; }; }; Sphinx = buildPythonPackage (rec { name = "Sphinx-1.2.2"; src = fetchurl { url = "http://pypi.python.org/packages/source/S/Sphinx/${name}.tar.gz"; md5 = "3dc73ccaa8d0bfb2d62fb671b1f7e8a4"; }; propagatedBuildInputs = [ docutils Jinja2 Pygments ]; }); docutils = buildPythonPackage rec { name = "docutils-0.8.1"; src = fetchurl { url = "https://pypi.python.org/packages/source/d/docutils/${name}.tar.gz"; md5 = "2ecf8ba3ece1be1ed666150a80c838c8"; }; }; in python.buildEnv.override { inherit python; extraLibs = [ Sphinx ]; }
Using nix-shell
Functionality
Once your environment is declared in the default.nix
file you can use the nix-shell
functionality to create your isolated development environments. Both of the following examples carry out the same function of creating a new Sphinx installation.
To issue a single command from the
nix-shell
you can issue it like this:brian$ nix-shell --command=sphinx-quickstart
To develop within the
nix-shell
, switch into it and execute the specific environment commands from within.brian@rhodecode:~/nix-project# vi default.nix brian@rhodecode:~/nix-project# nix-shell these paths will be fetched (2.59 MiB download, 14.76 MiB unpacked): /nix/store/0nh2bmaf0d3h0m16mggw2ipji2symb9n-python2.7-markupsafe-0.15 Downloading `http://cache.nixos.org/nar/0xhnlcz9f1wak7dhhrnznbgbjxqimkk0wip7q9w38xnllw7dmhgn.nar.xzā to ā/nix/store/q486ypcjr6kgkw8c62574dqp97sgh9d0-python2.7-Sphinx-1.2ā... [nix-shell:~/nix-project]$ sphinx-quickstart Welcome to the Sphinx 1.2 quickstart utility . . . [nix-shell:~/nix-project]$ exit brian@rhodecode:~/nix-project#
Note: You need to navigate to where you have the
default.nix
file stored.
Conclusion
Once you have carried out these steps you will be able to manage your environments much more easily.