Dev environment with minimal footprint

Hi,

I am new to the Nix (as package manager only) and I want to try to use it as the development environment.

I have a next use case and “expectations” of how it will work:

  1. Create dev environment for some project X (let’s say C++ project)
  2. This project X depends on some dynamic library A
  3. The expected dev environment: gcc (compiler), make (build tool), library A.so ( aka “lib” output), library A headers (aka “dev” output), and runtime dependencies of library A

For simplicity I’ve decided to build shell environment with only “hello” binary program in it. But whatever I’ve tried (“nix-shell”, “nix shell”, “flake”, etc) the “/nix/store” in the end still contains “hello.tar.gz” (source), GNU autotools (build dependencies), and “hello.drv”.

Ok, I understand that “nix-shell” was designed to develop and debug derivations, so no questions here, it’s reasonable why it pulls everything.

But I can’t grasp why “nix shell nixpkgs#hello” still downloads all the build dependencies and source files. My understanding is that the closure of the (cached) built “hello” package is self-contained and it requires the binary itself and its runtime dependencies (“glibc”). And my expectation is that “/nix/store” will additionally contain (after command execution) only “hello”, “glibc”, and maybe “hello.drv” (though strictly speaking it’s not needed, AFAIK).

My question:
Is it possible to create a dev environment with some library A.so as a dependency and have in the “/nix/store” (besides basic stuff that is already there) only the “A-lib” and “A-dev” folders?

NOTE: I’m not planning to change library A in any way, it’s just a dependency for me.

NOTE: Library A already exists (like “hello” package) in “nixpkgs” and its cache.

The option with pulling everything and then “gc” removing the “build dependencies” only would be fine too.

I hope the description above is clear, my apologies for any misunderstandings on my side.

Thanks for the help!

It does not for me. It will though, of course, if for some reason your hello can not be substituted.

As you can see, hello does not have a runtime dependency on its sources.

$ nix path-info -r nixpkgs#hello
/nix/store/p7jg95rzvfalb95k3mskk0jqxc9d724n-libunistring-1.4.1
/nix/store/1ga782ml07vy0h503ac4cin0h8d7q6yh-libidn2-2.3.8
/nix/store/vpxblivamvic1p5r5zny934jvg33m50r-xgcc-15.2.0-libgcc
/nix/store/jms7zxzm7w1whczwny5m3gkgdjghmi2r-glibc-2.42-51
/nix/store/10s5j3mfdg22k1597x580qrhprnzcjwb-hello-2.12.3

Though for development, you do not use nix shell, instead you write a singular shell.nix or create a flake and use nix-shell/nix develop.

2 Likes

Note also that what /nix/store contains is mostly irrelevant. What’s in /nix/store does not directly relate to what it’s in your build or runtime environment.

Yes, in this case it probably indicates an issue with your substituters (perhaps you’re using an uncached release branch or something?), so looking at it can be kinda useful for debugging if you’re not confident with other utilities yet, but it’s not a good way to inspect what your shells contain.

It just means that, at some point while using nix since your last garbage collection, nix has had to make a build or runtime environment with what’s in the store. This can happen for many reasons during practical use.

2 Likes

You’re correct, the command “nix shell nixpkgs#hello” indeed does not fetch source. My bad, it’s either because I confused “tar.gz.drv” with source archive or because I had some “leftovers” from previous experiments. My apologies for confusion here.

Yes, in this case it probably indicates an issue with your substituters

It was my mistake, it fetches from cache correctly and does not try to build from scratch. Sorry for confusion.

Note also that what /nix/store contains is mostly irrelevant.

Yes, it’s true but… The simple “hello” package bumps memory (disk) consumption form ~500Mb to ~1Gb, and “SDL2” library bumps up to ~4.7Gb (mostly due to enabling support for a lot of stuff by default).

It’s not critical (disk memory) but it was unexpected from my side as usually C++ projects are small (in comparison to languages with bigger runtime and/or standard library).

But I think I’m starting slowly to understand why it’s happening (at least with “nix-shell”).

  1. “nix-shell” realizes derivation dependencies (so I would get build-time dependencies of the library but such transitive dependencies are unnecessary (for me) because I’m not trying to build the library I’m trying to build my project which uses library itself as a dependency)
  2. (slightly unrelated) “keep-derivations” is “true” by default which means that derivations (and its references which are other derivations used for build-time) are kept if the “output” (binary package) exists. So once I have the library in “/nix/store”, the derivation of the library won’t be garbage collected (which may be a good thing for most of use cases, IMO).

I’m still trying to figure out how to make “minimal necessary dev environment” (if it’s possible and if it’s needed at all).

If you have problems with the size of a shell, you have to inspect why it is big.

Please be aware that “/nix/store grew by 5 GiB” is not a valid metric to check for a shells “footprint”.

Instead you should inspect its (buildtime) closure size, using nix path-info -S (experimental) and play with options. Also nix-tree (3rd party) is a nice tool.

2 Likes