From 67f366055e92df9f6c9d392540683166218925a9 Mon Sep 17 00:00:00 2001 From: Scott White Date: Wed, 1 Jul 2026 19:07:02 -0700 Subject: [PATCH] Strip /usr/include from apt pybind11's interface include dirs The Debian/Ubuntu `pybind11-dev` package ships a CMake config in which `pybind11::pybind11_headers` has INTERFACE_INCLUDE_DIRECTORIES = ${_IMPORT_PREFIX}/include and, for the apt install, `_IMPORT_PREFIX` resolves to `/usr`, so `/usr/include` is propagated to every target that consumes pybind11 via `pybind11_add_module` (in this repo, `_edgellm_runtime`). nvcc translates that into `-isystem /usr/include` on the host g++ command line for each CUDA source in the target. g++ then reorders its system include chain, placing `/usr/include` before the libstdc++ header directory `/usr/include/c++/`. libstdc++'s `` relies on `#include_next ` to reach the C library header; with `/usr/include` moved to the front, nothing after `` in the search order still contains `math.h`, and the build fails during device_link_stub.cu.o with: /usr/include/c++/13/cmath: fatal error: math.h: No such file or directory #include_next Reproduced on Ubuntu 24.04 aarch64 (Jetson Thor) with `pybind11-dev` 2.11.1 and CUDA 13.2 + gcc 13.3. Pip-installed pybind11 is unaffected because its `_IMPORT_PREFIX` points at the venv's site-packages, not `/usr`. Fix: after `find_package(pybind11 CONFIG REQUIRED)`, strip `/usr/include` from the `pybind11::pybind11_headers` target's INTERFACE_INCLUDE_DIRECTORIES. The real pybind11 headers live in a subdirectory of `_IMPORT_PREFIX/include` (specifically `pybind11/`) and are still exposed via the include-path resolution rules; only the bogus root include is removed. No-op for pip installs of pybind11. Verified by: - `rm -rf build && cmake ..` (no -Dpybind11_DIR override, so apt pybind11 at /usr/lib/cmake/pybind11 is picked): configure succeeds and the generated experimental/pybind/CMakeFiles/_edgellm_runtime.dir/includes_CUDA.rsp no longer contains a bare `-isystem /usr/include`. - `make -j _edgellm_runtime`: device_link_stub.cu.o now compiles and the `_edgellm_runtime.cpython-312-aarch64-linux-gnu.so` links successfully. Co-Authored-By: Claude Opus 4.7 --- experimental/pybind/CMakeLists.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/experimental/pybind/CMakeLists.txt b/experimental/pybind/CMakeLists.txt index 855f8bce..daa94de3 100644 --- a/experimental/pybind/CMakeLists.txt +++ b/experimental/pybind/CMakeLists.txt @@ -111,6 +111,31 @@ find_package( REQUIRED) find_package(pybind11 CONFIG REQUIRED) +# ── Work around Debian/Ubuntu pybind11-dev exporting /usr/include ───────── +# The apt-installed `pybind11-dev` package ships a CMake config whose +# `pybind11::pybind11_headers` target sets +# INTERFACE_INCLUDE_DIRECTORIES = ${_IMPORT_PREFIX}/include +# and `_IMPORT_PREFIX` resolves to `/usr`, so `/usr/include` gets propagated +# to every consumer via `-isystem`. When nvcc then invokes host g++ on a CUDA +# source, `-isystem /usr/include` reorders g++'s system header chain and +# places `/usr/include` before `/usr/include/c++/`. libstdc++'s +# uses `#include_next `, which now finds nothing after itself in the +# search order, and compilation fails with: +# /usr/include/c++/13/cmath: fatal error: math.h: No such file or directory +# Filter `/usr/include` out of the interface so the real pybind11 headers +# (in a subdirectory under _IMPORT_PREFIX) are still exposed but the bogus +# root include is not. No-op for pip-installed pybind11 (its _IMPORT_PREFIX +# points at the venv's site-packages, not /usr). +if(TARGET pybind11::pybind11_headers) + get_target_property(_pybind11_iface_inc pybind11::pybind11_headers + INTERFACE_INCLUDE_DIRECTORIES) + if(_pybind11_iface_inc) + list(REMOVE_ITEM _pybind11_iface_inc "/usr/include") + set_property(TARGET pybind11::pybind11_headers PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${_pybind11_iface_inc}") + endif() +endif() + # ── Header include paths (from source tree) ─────────────────────────────── set(EDGELLM_INCLUDE_DIRS ${EDGELLM_ROOT}/cpp ${EDGELLM_ROOT}/examples/multimodal