Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,29 @@ endif()

if(MP_ENABLE_CLANG_TIDY OR MP_ENABLE_IWYU)
# Workaround for nix from https://gitlab.kitware.com/cmake/cmake/-/issues/20912#note_793338
# Nix injects header paths via $NIX_CFLAGS_COMPILE; CMake tags these as
# CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES and omits them from the compile
# database, so clang-tidy, which ignores $NIX_CFLAGS_COMPILE, can't find capnp
# headers. Setting them as standard passes them to clang-tidy.
#
# On non-nix systems, compilers find C++ standard library headers in standard
# locations like /usr/include, and find dependency headers through -I flags on
# the compiler command line. On nix neither is true: compiler binaries are
# wrapped by scripts that inject the standard library header locations
# themselves, and dependency header locations are passed to the wrapper through
# $NIX_CFLAGS_COMPILE instead of on the command line. So CMake records all these
# paths as CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES and leaves them off the
# command lines it writes to the compile database.
#
# This normally does not matter, because analysis tools have their own wrapper
# scripts that re-add these paths the same way. But the nixpkgs
# include-what-you-use wrapper only re-adds -isystem flags and drops the
# -cxx-isystem flags used for the C++ standard library, so IWYU cannot find
# standard headers like <cstddef>. Setting the implicit include directories as
# standard directories puts them back on every command line, which is what lets
# the buggy IWYU wrapper find them.
#
# This workaround is temporary:
# - clang-tidy no longer needs it: the nixpkgs clang-tools wrapper was fixed in
# https://github.com/NixOS/nixpkgs/pull/462747.
# - IWYU will stop needing it once its wrapper is fixed the same way (see
# https://github.com/bitcoin-core/libmultiprocess/pull/238#discussion_r3342093865).
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
endif()

Expand Down
38 changes: 35 additions & 3 deletions shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,23 @@ let
})).override (lib.optionalAttrs enableLibcxx { clangStdenv = llvm.libcxxStdenv; });
clang = if enableLibcxx then llvm.libcxxClang else llvm.clang;
clang-tools = llvm.clang-tools.override { inherit enableLibcxx; };
# IWYU parses source files with its own embedded clang frontend, locating
# standard library headers through CPATH/CPLUS_INCLUDE_PATH variables set by
# its nixpkgs wrapper script:
# https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/tools/analysis/include-what-you-use/wrapper
# The wrapper derives those variables from the clang recorded in the
# derivation's `clang` attribute, which defaults to the toolchain IWYU was
# built against (libstdc++-flavored on Linux), not the toolchain this shell
# uses. Rebind it to this shell's compiler so IWYU resolves the same
# standard library the build uses.
#
# Mixing pkgs and crossPkgs here is intentional: IWYU comes from pkgs
# because it runs on the build machine, while `clang` comes from crossPkgs
# (via llvm) so that in a cross shell IWYU is baked with the cross
# toolchain's target headers — what it needs to analyze a cross build.
include-what-you-use = pkgs.include-what-you-use.overrideAttrs (old: {
inherit clang;
});
cmakeHashes = {
"3.12.4" = "sha256-UlVYS/0EPrcXViz/iULUcvHA5GecSUHYS6raqbKOMZQ=";
};
Expand All @@ -71,16 +88,31 @@ in crossPkgs.mkShell {
];
nativeBuildInputs = with pkgs; [
cmakeBuild
include-what-you-use
ninja
] ++ lib.optional (gcc != null) gcc ++ lib.optionals (!minimal) [
clang
# List clang-tools before clang so its wrapped tools take PATH priority
# (the first package in the list wins). Both packages provide the same
# tools (clangd, clang-tidy, clang-check, ...), but the clang package's
# copies are unwrapped and cannot find standard library headers like
# <cstddef>, while the clang-tools copies are wrapper scripts that add
# the C and C++ standard library include paths.
# 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
clang-tools
clang
include-what-you-use
];

CC = if gcc == null then null else "${gcc}/bin/gcc";
CXX = if gcc == null then null else "${gcc}/bin/g++";

# Tell IWYU where its libc++ mapping lives
# Tell IWYU where its libc++ mapping file lives. libcxx.imp maps
# libc++-internal detail headers (e.g. <__vector/vector.h>) to the public
# headers IWYU should suggest instead. IWYU only applies mapping tables
# compiled into its binary unless a mapping file is passed explicitly — it
# does not discover the libcxx.imp shipped alongside the headers it parses —
# so CMakeLists.txt forwards this variable via -Xiwyu --mapping_file. Taking
# the file from llvm.libcxx keeps it consistent with the headers IWYU parses
# via the include-what-you-use override above.
IWYU_MAPPING_FILE = if enableLibcxx then "${llvm.libcxx.dev}/include/c++/v1/libcxx.imp" else null;
}
Loading