The clang-tools package provides clangd, clang-tidy, clang-check and about
20 other clang-tools-extra programs, wrapped so they can find the standard
library headers on Nix. The clang package provides the same programs too,
but as raw binaries that cannot. Both end up on the PATH, and
nativeBuildInputs order sets PATH priority (the earlier entry wins), so
list clang-tools first to make the working copies win. This lets clangd
(in an editor) and clang-tidy or clang-check (run by hand) resolve
<cstddef> and the rest of the standard library. Without it, the raw
clang-check fails immediately over any source file:
include/mp/util.h:8:10: fatal error: 'array' file not found
The wrapping is a Nix quirk. On a normal system the standard library lives
in a default location like /usr/include that clang searches automatically,
so these tools work out of the box. Nix has no such default: glibc and
libstdc++ live in isolated store paths, and only the clang compiler wrapper
knows where. It injects the right -isystem paths when it compiles, but
standalone tools like clang-check and clangd run clang's parser directly,
never going through the compiler wrapper, so they need another way to learn
the paths.
That is what the clang-tools wrappers do. Before running the real tool,
each reads the libc-cflags and libcxx-cxxflags files from the clang
compiler wrapper, which hold the -idirafter and -cxx-isystem flags for the
glibc and libstdc++ header directories. The wrapper copies those
directories into the C_INCLUDE_PATH and CPLUS_INCLUDE_PATH environment
variables, which clang reads and adds to its header search path. The raw
binaries do none of this.
Why does the clang package ship these tools at all? It exposes them only
as a side effect: its cc-wrapper setup-hook adds the whole unwrapped-clang
bin/ to the PATH so the compiler driver and adjacent programs are
reachable, and upstream LLVM installs the clang-tools-extra programs into
that same bin/.
Why not fix this in CMake instead? CMakeLists.txt already does, as an
alternative workaround: it adds the compiler's implicit include
directories to the compile database via CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES,
so tools that read compile_commands.json find the standard headers
regardless of package order. But it is only enabled alongside IWYU or
clang-tidy, so it is not always present, and it is more fragile and
nonstandard: it bakes the detected store paths into the compile commands as
explicit -isystem flags that would not normally be there. Ordering
clang-tools first fixes the tools themselves, so they work independently of
the CMake configuration.
This does not affect include-what-you-use, which is a separate package
with no PATH collision and its own wrapper script.
https://web.archive.org/web/20260311024938/https://blog.kotatsu.dev/posts/2024-04-10-nixpkgs-clangd-missing-headers/
https://github.com/NixOS/nixpkgs/issues/76486
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>