Clang, clang++, and clangd can't find headers, even with compile_commands.json

Hi,

I am trying to get basic C and C++ development working on NixOS 24.05. I’m using Neovim for development, but I’m running into some issues compiling anything and with getting clangd working in

I think I have two separate but related problems right now.
First, I can’t reliably compile a source file using clang or clang++. For any source file, compilation fails when the linker can’t find stdlib headers, like <stdio.h> using clang or <cstdio> using clang++. However, on the C++, side, using c++ instead of clang++ works. I don’t understand why this is, because I was under the impression that c++ is aliased to clang++ but I guess not. I know that I have a working standard library, because compilation with gcc and g++ both work.

Second, clangd cannot resolve any standard library headers.

I think this might be related to missing a compile_commands.json file at the project root. I tried generating it using bear, like

$ bear -- c++ main.cpp

which didn’t fail, but also didn’t fix the problem. As an additional note, VSCode also cannot find the headers.

Here’s the list of packages I have installed relevant to C/C++:
- clang
- bear
- clang-tools
- libclang
- libgcc
- gdb
- llvmPackages_latest.lldb
- llvmPackages_latest.libllvm
- llvmPackages_latest.libcxx

I apologize if these are dumb issues, but I have a feeling that these are NixOS specific, since I have a nearly identical setup working on a Debian machine.

Thanks in advance

Update:
Ok, I’m able to build using this flake and nix develop:

{
  description = "C and C++";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils, ... }:
    flake-utils.lib.eachDefaultSystem (
      system:
        let
          pkgs = import nixpkgs { inherit system; };
          llvm = pkgs.llvmPackages_latest;
          lib = nixpkgs.lib;

        in
          {
            devShell = pkgs.mkShell {
              nativeBuildInputs = [
                pkgs.cmake
                llvm.lldb

                pkgs.clang-tools
                llvm.clang 

                pkgs.gtest
              ];

              buildInputs = [
                llvm.libcxx
              ];

              CPATH = builtins.concatStringsSep ":" [
                (lib.makeSearchPathOutput "dev" "include" [ llvm.libcxx ])
                (lib.makeSearchPath "resource-root/include" [ llvm.clang ])
              ];
            };
          }
    );
}

I’m seeing that this flake explicitly sets my CPATH variable to where all of my headers are. However, this doesn’t fix my original problem. There has to be some way to build C/C++ with clang without flakes. It also doesn’t fix clangd.

Ok found a fix on reddit. I’ll try and understand it better once I know more about clangd but I just don’t have the time rn.
Shove this in your config, for now I’ve just put it at the very end of my plugins.lua file (after the lazy setup).
The commented lines are from Getting started, calling the setup function is apparently necessary but I’m not sure about the content itself so I just left it there for now.
The mason = false bit makes it so that Neovim doesn’t use the Mason clangd but the global, system wide which for some reason doesn’t give the missing headers errors. For it to work you need the clang-tools (not necessarily clangd for some reason) globally installed in your nix config.

local lspconfig = require("lspconfig")
lspconfig.clangd.setup({
	-- 	cmd = { "clangd", "--background-index", "--clang-tidy", "--log=verbose" },
	-- 	init_options = {
	-- 		fallbackFlags = { "-std=c++17" },
	-- 	},
})
lspconfig.opts = {
	servers = {
		clangd = {
			mason = false,
		},
	},
}

Ok, now we are getting somewhere. This fixed LSP support for Neovim, most things seem to be working on that front.

However, I am still confused about building source files. Here’s what’s working and what’s not:

  • gcc/g++ can build source.
  • clang cannot build C source, can’t find headers
  • clang++ cannot build C++ source, can’t find headers.
  • c++ can build C++ source. If I run c++ with no arguments, it looks like it is just calling clang++. However, whereis reports two different binaries for clang++ and c++. Not sure what is going on here.

It looks like gcc provides a c++ binary, but I don’t think that’s what’s being used here…


image

I think I’ve figured out the issue. I had multiple installations of the toolchain. Here’s the combination of packages that I found work:

        llvmPackages_latest.lldb
        llvmPackages_latest.libllvm
        llvmPackages_latest.libcxx
        llvmPackages_latest.clang

For the small projects I’m working with, I didn’t have to generate a compile_commands.json but YMMV.

Few additional things, these are required

  • Install clang-tools (direct, not from llvmPackages).
  • uninstall clangd through mason on Neovim if it was previously installed. In my neovim config, removing clangd from the server list does not automatically uninstall mason’s version of clangd.
  • I am not 100% sure on this, but you may also need gcc and a glibc version. Compiling with clang seems to find libc from the llvm libc, but the LSP seems to look at glibc first.

What you’re meant to do is override the stdenv if you want it to use clang:

  pkgs.mkShell.override { stdenv = clang19Stdenv; } {
    packages = [
      pkgs.cmake
      # ... whatever else, but NOT C compilers though!
    ];
  }

(or inherit (llvmPackages_19) stdenv;, if you prefer)

and instead of the deprecated devShell.<system>, use devShells.<system>.default.

Hey, thanks for this.

Is this what you would do if you are trying to build a C or C++ project as a nix package or using the nix build system? I think I know how to do that, but that’s not exactly what I’m trying to do. I am trying to get a system-wide development environment working without a nix shell or flakes. I want to be able to have the same environment between NixOS and non-NixOS machines, without the extra step of nix develop or direnv or something. Is there an idiomatic way to do this? What I have is working now and doesn’t require flakes or setting any environment variables.

To clarify, I’m not trying to build a package with clang (or at least to what my understanding of what a nix package is), I’m just trying to be able to compile and edit C/C++ with my preferred toolchain.

What you want is not really advisable. Create a shell per project.

(And anything involving mkShell is inherently not building it with nix, it’s just creating a shell.)

This makes sense for projects that I want to build, sure. But, I want to be able to browse or view C/C++ source in my editor without having to enter into a dev shell. For instance, I have to grade project submissions that I don’t necessarily have any need to build. I don’t want to have to enter a special dev shell just to have LSP work when I open a source file. I might be totally off base here… let me know.