Debugging and IDE integration¶
Coverage tools (gcov/gcovr), debuggers (GDB), and editor tooling (the VSCode
C/C++ extension, clangd) all depend on paths recorded at compile time. Two
scikit-build-core defaults make those paths disappear:
The build directory is temporary. Without
build-dirset, scikit-build-core builds in a temporary directory that is deleted after the build — taking coverage data, debug artifacts, andcompile_commands.jsonwith it.Build isolation hides everything else.
pip install .,python -m build,uv build, anduv synccopy your project and install the build dependencies into throwaway environments, so recorded source paths and include paths no longer exist after the build.
The shared fix is a persistent build directory with isolation disabled (after preinstalling your build dependencies), so everything stays in place:
pip install --no-build-isolation -Cbuild-dir=build -ve .
You can set the build directory in pyproject.toml instead of on the command
line:
[tool.scikit-build]
build-dir = "build"
Coverage and debugging (gcov / gcovr / GDB)¶
gcov records the source path and compilation directory in the .gcno
(compile-time) and .gcda (run-time) files, and DWARF debug info records the
source path in the compiled extension. If those paths no longer exist when you
run the tool, you get errors such as
$ gcovr --xml coverage.xml -r .
(ERROR) Trouble processing '.../CMakeFiles/foo.dir/_foo.c.gcda' with working directory '/home'.
or GDB that cannot show source lines even with debug flags enabled.
With the persistent-tree install above, run gcovr against the persistent tree, pointing the root at your source:
gcovr -r . build
If you would rather not depend on the paths staying put, make the recorded paths
relocatable with compiler flags — -fprofile-abs-path for gcov and
-ffile-prefix-map=<build>=<src> (or -fdebug-prefix-map=... for debug info
only) — for example via cmake.define or CFLAGS.
IDE IntelliSense (compile_commands.json)¶
Editor tooling resolves headers like pybind11/pybind11.h from your include
paths. With build isolation, binding libraries such as pybind11 and nanobind
live in a throwaway overlay — CMake reports a path such as
.../Temp/pip-build-env-xxxx/overlay/Lib/site-packages/pybind11/include, which
no longer exists when your editor looks for it.
Extend the shared fix by having CMake export a compile database:
pip install scikit-build-core pybind11
pip install --no-build-isolation --check-build-dependencies -ve . \
-Cbuild-dir=build \
-Ccmake.define.CMAKE_EXPORT_COMPILE_COMMANDS=1
uv pip install scikit-build-core pybind11
uv pip install --no-build-isolation -ve . \
-Cbuild-dir=build \
-Ccmake.define.CMAKE_EXPORT_COMPILE_COMMANDS=1
In a uv-managed project, disable isolation for your package instead, so
uv sync reuses the environment’s build dependencies rather than a discarded
overlay:
[tool.uv]
no-build-isolation-package = ["mypackage"]
This writes build/compile_commands.json with real, persistent paths. Then
point your editor at the file. For clangd, add --compile-commands-dir=build
(or a .clangd with a CompileFlags.CompilationDatabase: build entry); for the
VSCode C/C++ extension, set
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json".
See editable installs for the related
--no-build-isolation recommendations.
Debug builds on Windows¶
A cmake.build-type=Debug extension links against the debug CPython
(pythonXY_d.dll, _d.pyd suffix), so it only loads under a debug interpreter
(python_d.exe) — importing it under a normal python.exe crashes with
0x80000003. This is CPython behavior, not scikit-build-core’s: run the build
itself under python_d.exe so the _d suffix and import library line up (and
the t ABI flag is added for free-threaded debug builds, e.g.
pythonXYt_d.dll).
To keep debug info while still loading under a normal Python, don’t do a debug
build — instead undefine _DEBUG around the CPython headers so they don’t
auto-link pythonXY_d.lib, and add debug flags yourself:
#ifdef _DEBUG
# define SKB_RESTORE_DEBUG
# undef _DEBUG
#endif
#include <Python.h>
#ifdef SKB_RESTORE_DEBUG
# define _DEBUG
#endif