Make C++ library & headers available globally

Hi everyone,

I’m creating a NixOS VM which will be used by CS students as a C++ development environment. It should include gcc and the gtest library. Since the students are not familiar with Nix, I want these to work out of the box and not require entering a nix-shell (the VM will not be used for anything else anyways). Because installing gtest and gtest.dev does not make the library available to gcc, my current approach is to manually set the environment variables, i.e:

{pkgs, ...}: {
    environment.systemPackages = [ pkgs.gcc ];
    environment.variables = {
        CPLUS_INCLUDE_PATH = "${pkgs.gtest.dev}/include";
        LIBRARY_PATH = "${pkgs.gtest}/lib";
    };
    # ...
}

Although this works well, it seems a bit hacky. Is there a more idiomatic way to achieve this (similarly to how nix-shell -p gtest makes gcc find the headers and shared objects)?

While searching for this, I’ve only found the advice to just use nix-shell (e.g. in the NixOS FAQ and this Discourse thread). I agree that this makes sense for most use cases, but here I just want everything to be as frictionless as possible for the students.
Thanks.

Just enter into a nix-shell in each shell logins?

2 Likes

Thanks for the suggestion, I guess that should work. I’m not sure if it’s really less janky than setting environment variables:

  • One would have to ensure that the exact same version of the library used by nix-shell is already on the system, since otherwise it needs to be downloaded on Login. This would be undesirable because the machine might not have internet access, particularly when first booted, and even if it does, it adds unnecessary delay. This issue also applies to things like stdenv which are AFAIK fetched by nix-shell.
  • At least according to this post, there is not really a way to guarantee that something is started on login (which is independent of the Desktop Environment).
  • As far as I’m aware, nix-shell is not supposed to be run in non-interactive shells, so there might also be additional issues when running it on login.
1 Like

You can use system.extraDependencies to ensure the shell dependencies are available: NixOS Search

I think you are confusing things. If your users are going to use shell to work on that project and they don’t modify their shell or anything, you can guarantee that upon shell logins, this will be sourced.

You can run it in interactive shell login if you want to.

1 Like

Yes, but this does not necessarily guarantee that the versions match, right? That should also be possible (with Flakes for example) but having to ensure that the 2 versions of nixpkgs are always in sync makes the whole setup a bit convoluted.

Sorry, I misread your comment above. Some people prefer to use an IDE instead of working in a shell, so this is also not ideal. It’s definitely not the best beginner experience when things work in a terminal but inexplicably break outside of it.

I am not sure I understand what version sync have to do here in this situation.

I would expect you to use one nixpkgs for such a setup, it does not make sense to have 2 nixpkgs. So you would import the shell.nix declarations with the current nixpkgs you are evaluating NixOS.

Therefore, you would have only one nixpkgs and everything would be in sync?

No Flakes has to be involved FWIW. This is just dependency management.

Gotcha. But how are those IDEs installed? I surmise that most if not all IDEs you cover will probably have a hook to load an additional environment, or you could fool them into sourcing the environment variables that the nix-shell provides.

But we are entering into custom realm, I don’t know if trying to figure out the global environment variables and reinventing nix-shell will be faster, more robust than figuring out all IDEs entrypoints and injecting a nix-shell in there. YMMV.

1 Like

I’ve done some terribly awful/hacky things in the past to make this work. https://github.com/dolphin-emu/sadm/blob/ae419bd5a54323ecc2651330cfac0ba4d3cc9b74/roles/fifoci-worker/default.nix is an example of a service running as a user with a shell equivalent to being a nix-shell, but declaratively configured. It’s basically copying the internal implementation of nix-shell, so ofc this is subject to break when said implementation changes. So far it’s been pretty stable though :slight_smile:

1 Like

Yeah, I think for now it’s easier for me to just manually replicate nix-shell. I’ll think about it a bit more though since it’s not something I’m 100% happy with and maybe there is an elegant way to use nix-shell without making it visible to the user. Thanks for all the suggestions :smiley:

Thanks a lot, I’ll definitely have a look at that. It doesn’t look less hacky than what I’m doing right now but maybe it can be encapsulated in a nice way. Having a NixOS module that does this would be pretty neat for example.

But also it’s perfectly viable to pin both the global <nixpkgs> and the "nixpkgs" reference in the flake registry in your configuration.nix, that they resolve into the same nixpkgs revision you build your NixOS with, if that’s what you meant by synchronization