Search code examples
development-environmentnix

How does `nix` package manager integrate into the classical linux filesystem and `$PATH`?


Context

Our team is currently struggling with development environments.

  • We develop C++/Python applications, for which we need all sorts of tools: gcc, clang, cmake, conan different python modules (even interpreters with different versions), etc. So far, every developer gets a computer with Ubuntu and they are responsible for setting up their own environment. There are some lose guidelines (i.e. gcc-12.0.0, clang-14.0.1, conan-1.59.0, etc.), but it happened already more than once that some had installed gcc-11 instead of 12, etc. which then further down in the build process caused issues.
  • What further complicates the situation is that build servers are RedHat or CentOS, some specific parts of the team use macOS, etc. Our software compiles on all those platforms just fine, but getting to that point can be a quite a bit of work.

So we are basically looking for a way to have reproducible development environments, that still leave enough flexibility to each developer (i.e. our build system is based around conan and cmake, so any modern IDE can be used to build, debug, and run the projects).

Now this is not a new problem, but the information on Stack Overflow is unfortunately not that easy to find and maybe even outdated (e.g. here).

Question

From reading on the internet, it seems that one possible solution to the problem is called nix. What I don't understand is the role of nix in the context of an OS and package manager. Assuming I have a Ubuntu computer in front of me. This has all the standard locations on the filesystem (i.e. /, /bin, /home, etc.). I can now:

  • install nix on said computer
  • use the language to write a "config".
  • Use nix and this config to build and install all those packages into a separate location, i.e. /nix/store, (i.e. the gcc build there does not go into /bin or /usr/bin).

Now presumably I can then later use these installed packages to build the software I want to build, but how exactly? If they are not in $PATH, then how do I use them? If I add them to $PATH, then whats the point of having them and how do I guarantee that they don't clash with what is already installed and inside $PATH? Can I use this setup with any IDE of my choosing?

Sidenote

If nix is the wrong tool for the problem I described above, I am more than happy to consider others. docker seems to be one alternative, but this has its own downsides...


Solution

  • Nix is a great tool for this sort of thing. But don't think about a "config" (that term usually refers to NixOS, which you are not using and don't need to use).

    What you should do is write a file named shell.nix that contains an expression in the Nix language that calls mkShell to define all the dependencies you need. Then you run nix-shell in that directory and Nix will automatically build/install all the dependencies that you need, and then launch a special shell with those dependencies on your PATH, in front of the default stuff from your system. Then you can do your development work in that shell.

    Here's an example shell.nix I wrote recently when I wanted to compile the LLVM project from source:

    let
      pkgs = import <nixpkgs> {};
    in
      pkgs.mkShell {
        buildInputs = [
          pkgs.cmake
          pkgs.pkg-config
          pkgs.gdb
          pkgs.ninja
          pkgs.python3
        ];
        NIX_HARDENING_ENABLE = "";
      }
    

    What I did above was just import the user's current nixpkgs channel, but for reproducibility you'll want to pin a specific version of Nixpkgs in your expression so that everyone gets the same version of GCC and all the other packages.

    To really understand what Nix is doing and what it gets you, I recommend reading Eelco Dolstra's thesis.