development-environmentnix

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


Context

Our team is currently struggling with development environments.

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:

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.