How do I set up nixos for C programming?

Hello! I have been using nixos for the past few months after using windows for most of my life, and now that my fall courses have begin, I need to be able to program in C for a few of my classes.

I am well versed in programming in general, and I know the C language. I have a very basic understanding of flakes, just enough to install a non-nixpkg program on my machine. I am attempting to use Neovim with lazy as a plugin manager and coc as my autocomplete and LSP system. Up until now, i have used VScode for most of my development work on my windows machine.

I have coc-clangd installed in neovim. I also have nix-ld installed as some small amount of research led me to believe that it would be needed in order for me to run non-nix programs on my machine for the purposes of development. The relevant section of my config is here:

programs.nix-ld = {
    enable = true;
    libraries = with pkgs; [
      # List by default
      zlib
      zstd
      stdenv.cc.cc
      curl
      openssl
      attr
      libssh
      bzip2
      libxml2
      acl
      libsodium
      util-linux
      xz
      systemd
      
      # My own additions
      xorg.libXcomposite
      xorg.libXtst
      xorg.libXrandr
      xorg.libXext
      xorg.libX11
      xorg.libXfixes
      libGL
      libva
      pipewire
      xorg.libxcb
      xorg.libXdamage
      xorg.libxshmfence
      xorg.libXxf86vm
      libelf

      # Required
      glib
      gtk2

      # Inspired by steam
      # https://github.com/NixOS/nixpkgs/blob/2d5317c1a036fbcf091c2a29f89c11ef456ef2cd/pkgs/by-name/st/steam/package.nix#L36-L85
      networkmanager      
      vulkan-loader
      libgbm
      libdrm
      libxcrypt
      coreutils
      pciutils
      zenity
      # glibc_multi.bin # Seems to cause issue in ARM
      
      # # Without these it silently fails
      xorg.libXinerama
      xorg.libXcursor
      xorg.libXrender
      xorg.libXScrnSaver
      xorg.libXi
      xorg.libSM
      xorg.libICE
      gnome2.GConf
      nspr
      nss
      cups
      libcap
      SDL2
      libusb1
      dbus-glib
      ffmpeg
      # Only libraries are needed from those two
      libudev0-shim
      
      # needed to run unity
      gtk3
      icu
      libnotify
      gsettings-desktop-schemas
      # https://github.com/NixOS/nixpkgs/issues/72282
      # https://github.com/NixOS/nixpkgs/blob/2e87260fafdd3d18aa1719246fd704b35e55b0f2/pkgs/applications/misc/joplin-desktop/default.nix#L16
      # log in /home/leo/.config/unity3d/Editor.log
      # it will segfault when opening files if you don’t do:
      # export XDG_DATA_DIRS=/nix/store/0nfsywbk0qml4faa7sk3sdfmbd85b7ra-gsettings-desktop-schemas-43.0/share/gsettings-schemas/gsettings-desktop-schemas-43.0:/nix/store/rkscn1raa3x850zq7jp9q3j5ghcf6zi2-gtk+3-3.24.35/share/gsettings-schemas/gtk+3-3.24.35/:$XDG_DATA_DIRS
      # other issue: (Unity:377230): GLib-GIO-CRITICAL **: 21:09:04.706: g_dbus_proxy_call_sync_internal: assertion 'G_IS_DBUS_PROXY (proxy)' failed
      
      # Verified games requirements
      xorg.libXt
      xorg.libXmu
      libogg
      libvorbis
      SDL
      SDL2_image
      glew110
      libidn
      tbb
      
      # Other things from runtime
      flac
      freeglut
      libjpeg
      libpng
      libpng12
      libsamplerate
      libmikmod
      libtheora
      libtiff
      pixman
      speex
      SDL_image
      SDL_ttf
      SDL_mixer
      SDL2_ttf
      SDL2_mixer
      libappindicator-gtk2
      libdbusmenu-gtk2
      libindicator-gtk2
      libcaca
      libcanberra
      libgcrypt
      libvpx
      librsvg
      xorg.libXft
      libvdpau
      # ...
      # Some more libraries that I needed to run programs
      pango
      cairo
      atk
      gdk-pixbuf
      fontconfig
      freetype
      dbus
      alsa-lib
      expat
      # for blender
      libxkbcommon

      libxcrypt-legacy # For natron
      libGLU # For natron

      # Appimages need fuse, e.g. https://musescore.org/fr/download/musescore-x86_64.AppImage
      fuse
      e2fsprogs
    ];

};

When writing an include line, the following is displayed:

With a distinct lack of stdlib and stdio I amm unsure how to continue or where to go from here. I would like to keep my setup as simple as possible and be able to write programs that will run on the machines of my professors for their ability to grade my work.

Please let me know where i should go from here in order for my machine to behave a little closer to how my old windows machine worked in VS Code.

Thanks!

My tips:

  • Don’t expect nix-ld to help here.
  • Compile statically so that the binary is self-contained and will actually run on other machines. (Obviously this depends on how you’re being graded, static compilation via nixpkgs will use musl as libc.)
  • Use nix shells (Declarative shell environments with shell.nix — nix.dev documentation)
  • Don’t add compilers to the shell, the stdenv already handles that.

I’ll have to do more research on static compilation. I will check out nix shells, as well as i do not fully understand their purpose.

If possible. Could someone explain to me why stdlib and stdio are not showing up in more simple terms in order to broaden my understanding of the issue i am having?

nix-ld is for running executables that are already compiled by modifying the dynamic linker to point at the list of libraries that you specify. It’s essentially LD_LIBRARY_PATH but with more precision, but since you’re using the programs.nix-ld module, you’re throwing that precision away and making it as bad as setting LD_LIBRARY_PATH globally. (Which is why I think this whole thing is a hack that shouldn’t exist.) It’s also a hack that you have to repeat on other machines, and if someone else is not using NixOS, getting them to adopt said hack is unrealistic and kind of defeats the purpose of your using nix.

Why that’s relevant to your situation is specifically that it’s not - it does nothing to help expose development headers for your dev environment. So, what does that? Nix shells.

As far as getting started with a specific shell, I can share a bit more later today.

Please do! That would be extremely helpful! Thank you so much for your time and effort here!

C programming

# shell.nix
stdenv.mkShell { }
$ nix-shell

I have figured out how to use the nix-shell command, however, from inside the shell, i still do not have the ability to autofill stdlib inside neovim. Is my clangd configuration the problem?

export CLANGD_FLAGS="--query-driver=$(which $CC)"

Make sure the --query-driver flag points to the full path of the compiler in your compile_commands.json or it will not be able to resolve system paths.

You can add that export to the shellHook in a mkShell
System headers - clangd

1 Like

Per the NixOS Wiki article on clang, it seems like this should work:

# in file ./shell.nix
with import <nixpkgs> {};
clangStdenv.mkDerivation {
  name = "clang-nix-shell";
  nativeBuildInputs = [ clang-tools /* add libraries here */ ];
}

It might seem weird to put a derivation instead of a call to mkShell in the shell.nix, but from my understanding, nix-shell was originally developed as a way to test derivation builders and so drops you into a interactive shell that just sets up the dependencies of a derivation (nix-instantiate/nix-build would build the actual derivation)

From Discourse - how to make mkShell use clang’s stdenv, it seems like the way to do it with mkShell involves overriding the stdenv.

let
  pkgs = (import <nixpkgs> { });
in
pkgs.mkShell.override { stdenv = pkgs.clangStdenv; } {
  nativeBuildInputs = [ clang-tools ];
}

Do either of these shell.nix fix your problem?

EDIT: see query driver answer byKeatonClark above, which should be a workable fix. There is weirdness in how clangd is wrapped that should probably be addressed upstream but will need someone with the knowledge and time to fix it in a more properly engineered fashion. See a PR that wraps clangd, but only in pkgs.clang-tools and an abandoned PR to implement similar in pkgs.clang for more information

EDIT2: added clang-tools to the shell.nix above, which then should put the clangd from the package earlier in the path. It’s a bit of an ugly hack for now.

It’s because clangd wants to ask clang what include paths it uses by default. If it can’t find clang, or the clang it does find has no system headers or the wrong system headers it will fail or find the wrong ones. Query driver tells clangd to ask some other compiler what includes it is using to resolve headers and use those paths. But it will only query that compiler if you tell it you are using it in a .clangd file or compile_commands.json. --query-driver is more like an allow list

Edit: if you follow the includes from clangd in your flake you might see that clangd resolves them to headers in clang’s headers, which may or may not be the same as the headers your compiler is actually using. This is important for cross compiling

I switched to helix editor and it made my setup process much more simple. Thank you all for the suggestions!

1 Like