nix: Make clang tooling and IWYU find the right standard library headers #333

pull ryanofsky wants to merge 4 commits into bitcoin-core:master from ryanofsky:pr/iwyumatch changing 2 files +58 −7
  1. ryanofsky commented at 4:27 PM on August 6, 2026: collaborator

    On Nix the LLVM analysis tools (clangd, clang-tidy, clang-check, include-what-you-use and others) need two things to work correctly: they need find the standard library headers at all, and they need to find same standard library the build uses (libc++ vs libstdc++). This PR makes the tools resolve the right headers on their own in every context they run in: editors, command line, and CI.

    PR was motivated by recent discussion: #296#pullrequestreview-4839484696 and includes documentation improvements, new IWYU changes, and an old clang-tools change I've been using for a long time locally.

    Specifically the PR:

    • puts the header-aware clang-tools binaries ahead of the raw clang ones on PATH, so clangd/clang-tidy/clang-check find the standard library on their own;
    • binds IWYU to the shell's compiler so it analyzes the same standard library the build uses in any context;
    • provides IWYU only in the non-minimal shells that actually run it; and
    • updates the comment on the build's CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES workaround — which injects these header paths into the compile database when clang-tidy or IWYU are enabled — to note that clang-tidy no longer needs it and IWYU still does.

    Details are in commit messages.

  2. doc: Update comment on CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES workaround
    The workaround was added in 977d721 so clang-tidy could find headers that nix
    compiler wrappers inject internally. Its comment has since become inaccurate and
    misleading:
    
    - It names clang-tidy as the tool that needs the workaround, but clang-tidy no
      longer does: the nixpkgs clang-tools wrapper was fixed upstream
      (https://github.com/NixOS/nixpkgs/pull/462747). IWYU is the tool that needs it
      now.
    - It says the tool "ignores $NIX_CFLAGS_COMPILE." In fact the nixpkgs
      analysis-tool wrappers do read $NIX_CFLAGS_COMPILE and re-add its paths.
    - It says the missing headers are capnp (dependency) headers. That is backwards:
      dependency headers are passed via -isystem in $NIX_CFLAGS_COMPILE and are
      found. The headers that go missing are the C++ standard library headers (e.g.
      <cstddef>), because the include-what-you-use wrapper drops the -cxx-isystem
      flags those use.
    - It pins the whole mechanism on $NIX_CFLAGS_COMPILE, omitting that standard
      library paths are injected through the compiler wrapper's own flag files, and
      it neither explains why the tools normally cope nor that the workaround only
      compensates for a temporary wrapper bug.
    
    Rewrite the comment to describe the nix header-injection mechanism from first
    principles, identify IWYU's dropped -cxx-isystem flags as the specific reason
    the workaround is still needed, and note that it is removable once that wrapper
    bug is fixed upstream.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    520b348ad3
  3. ci: Make IWYU use the same standard library as the build
    Since cbb1e43 the llvm CI job is intended to test libc++ instead of
    libstdc++, in the build and in IWYU. That works, but only accidentally: the
    nixpkgs include-what-you-use wrapper bakes in the include paths of the
    toolchain IWYU was built against (libstdc++ on Linux), and IWYU only sees
    libc++ headers because the explicit -isystem flags generated by the
    CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES workaround in CMakeLists.txt take
    precedence over the wrapper's environment variables. Relying on that is
    fragile: IWYU invoked outside the CMake build (or after that workaround is
    removed) silently analyzes libstdc++, with a mapping file that only matches
    libc++.
    
    Make shell.nix responsible for this instead: rebind the clang recorded in
    the IWYU wrapper to the shell's compiler, so IWYU resolves the same standard
    library the build uses in any context. Also expand the IWYU_MAPPING_FILE
    comment to explain how the mapping file is consumed and kept consistent.
    
    The override changes the IWYU derivation, so shells rebuild it from source
    once per channel instead of fetching it from the binary cache.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
    36373a5855
  4. ci: Provide IWYU only in non-minimal shells
    Move include-what-you-use into the non-minimal tool group alongside clang
    and clang-tools. Minimal shells, used by the cross-compiling gnu32 job, do
    not run analysis tools, and after the previous commit shipping IWYU there
    would pull the cross clang closure into the shell and rebuild IWYU per cross
    target for no benefit. Cross shells that do provide IWYU get the cross
    toolchain's target headers baked in, which is what analyzing a cross build
    requires (the embedded clang frontend parses in target mode given a matching
    --target flag).
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
    30363632d7
  5. nix: list clang-tools before clang so its tools find standard headers
    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>
    03329f69d5
  6. DrahtBot commented at 4:27 PM on August 6, 2026: none

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Concept ACK hebasto

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #231 (Add windows support by ryanofsky)
    • #212 (ci: add newdeps job testing newer versions of cmake and capnproto by ryanofsky)
    • #209 (cmake: Increase cmake policy version by ryanofsky)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  7. hebasto commented at 4:31 PM on August 6, 2026: member

    Concept ACK.

  8. ryanofsky force-pushed on Aug 6, 2026
  9. ryanofsky commented at 5:26 PM on August 6, 2026: collaborator

    <!-- begin push-8 -->

    Updated 505c3587762693cbbd4747db7637b0e587f9ad24 -> 03329f69d5a1095117485903d604bacd596f9b2c (pr/iwyumatch.7 -> pr/iwyumatch.8, compare)<!-- end --> fixing more problems in CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES comment.


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin-core/libmultiprocess. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-08-23 22:30 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me