Why is the Clang resource dir split in a separate package?

I’m trying to make a dev shell (nix develop) for TinyGo. This is a Go compiler that uses LLVM libraries and tools.

This compiler needs clang (the command) to build C files, and libclang (the library) to parse C header files (for CGo). Both of these need the Clang resource directory to be available in the include path, but nothing else from the environment.

In all other non-Nix systems, Clang is able to find the resources directory itself. However, in Nix it is separated out in a separate package:

$ clang -c -o test.o tmp/test.c -v
ignoring nonexistent directory "/nix/store/1zjby6f3127pckrv8vwcccjgg5d1p7ry-clang-16.0.6/lib/clang/16/include"
#include "..." search starts here:
End of search list.
tmp/test.c:1:10: fatal error: 'stdbool.h' file not found
#include <stdbool.h>
1 error generated.

As you can see, Clang looked for the resource dir at /nix/store/1zjby6f3127pckrv8vwcccjgg5d1p7ry-clang-16.0.6/lib/clang/16 but couldn’t find it. The actual resource dir appears to be /nix/store/mhvngsqzjsly7i7wskgrxrcr48mxcj13-clang-16.0.6-lib/lib/clang/16.

I understand why things like the libc are separated out and must be explicitly added as a dependency, but the resources dir is kind of special and belongs with the compiler. So why is it separate? Is this intentional? Because it really breaks my particular use case.

Using the compiler wrapper is not really an option for me, because it has bugs (1, 2), and because it doesn’t solve the issue with libclang.

Some more detail: while I do need the resource dir for headers like stdint.h that are compiler-specific, I don’t need any other include path. TinyGo bundles things like the libc so that cross compilation becomes a non-issue. In fact, TinyGo aims to produce reproducible binaries independent of the host system (it is a cross compiler by default, even for the current host).

My full flake file at the moment:

  inputs = {
    nixpkgs.url = "nixpkgs/nixos-23.05";
    flake-utils.url = "github:numtide/flake-utils";
  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
        pkgs = nixpkgs.legacyPackages.${system};
      with pkgs;
        devShells.default = mkShell {
          buildInputs = [
1 Like