COMP: Wrap build-tree system include dirs in BUILD_INTERFACE - #6465
hjmjohnson wants to merge 1 commit into
Conversation
|
| Filename | Overview |
|---|---|
| CMake/ITKModuleMacros.cmake | Wraps build-tree entries in SYSTEM_INCLUDE_DIRS inside $<BUILD_INTERFACE:>; the regex-based prefix check is fragile when CMAKE_BINARY_DIR contains ERE metacharacters, and the new 2-line comment violates the repo's 1-line prose-budget cap. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["foreach _dir in SYSTEM_INCLUDE_DIRS"] --> B{"_dir MATCHES\n'^CMAKE_BINARY_DIR'\nOR\n'^PROJECT_BINARY_DIR'?"}
B -- Yes --> C["list APPEND SYSTEM_GENEX_INCLUDE_DIRS\n$<BUILD_INTERFACE:_dir>"]
B -- No --> D["list APPEND SYSTEM_GENEX_INCLUDE_DIRS\n_dir (verbatim)"]
C --> E["target_include_directories SYSTEM PUBLIC\n→ active during build only\n→ dropped from install(EXPORT)"]
D --> F["target_include_directories SYSTEM PUBLIC\n→ active at both build and install time"]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["foreach _dir in SYSTEM_INCLUDE_DIRS"] --> B{"_dir MATCHES\n'^CMAKE_BINARY_DIR'\nOR\n'^PROJECT_BINARY_DIR'?"}
B -- Yes --> C["list APPEND SYSTEM_GENEX_INCLUDE_DIRS\n$<BUILD_INTERFACE:_dir>"]
B -- No --> D["list APPEND SYSTEM_GENEX_INCLUDE_DIRS\n_dir (verbatim)"]
C --> E["target_include_directories SYSTEM PUBLIC\n→ active during build only\n→ dropped from install(EXPORT)"]
D --> F["target_include_directories SYSTEM PUBLIC\n→ active at both build and install time"]
Reviews (1): Last reviewed commit: "COMP: Wrap build-tree system include dir..." | Re-trigger Greptile
|
@blowekamp ANTs builds against the latest ITKv6 would not build. This allows it to build (with CMake 4.2) but I'm hoping you know of a less complicated solution. |
A module's <module>_SYSTEM_INCLUDE_DIRS entries are added to the module target's interface include directories verbatim, on the assumption that they are stable external/system paths present at both build and install time. A module that fetches a header-only dependency into its own build tree (for example a remote module using FetchContent) exposes a build-directory path here. install(EXPORT ITKTargets) then records an unrelocatable build-directory path in the interface and, under CMake 4.x, fails generation with "INTERFACE_INCLUDE_DIRECTORIES property contains path which is prefixed in the build directory". Wrap only build-tree-prefixed system include paths in $<BUILD_INTERFACE:> so they apply to the build and are dropped from the install interface. Stable system paths are passed through unchanged, so behavior for the common case (e.g. an installed Eigen) is unaffected.
432f5e1 to
896f9f4
Compare
|
I believe you can set |
|
Closing in favor of a module-side fix — thanks @blowekamp, your suggestion was exactly right. 🎉 You called it: setting Investigating your approach also surfaced a second, latent defect in the module that the core patch alone would not have fixed: Both are now in ITKVkFFTBackend PR #83, commit |
A module's
<module>_SYSTEM_INCLUDE_DIRSentries are placed in the module target's interface include directories verbatim. When a module fetches a header-only dependency into its build tree, that build-directory path becomes part ofinstall(EXPORT ITKTargets)and CMake 4.x fails generation with "INTERFACE_INCLUDE_DIRECTORIES property contains path which is prefixed in the build directory." This wraps only build-tree-prefixed system include paths in$<BUILD_INTERFACE:>; stable system paths are unchanged.Why this is needed — full analysis
CMake/ITKModuleMacros.cmakebuilds<module>_SYSTEM_GENEX_INCLUDE_DIRSfrom<module>_SYSTEM_INCLUDE_DIRSand applies it to the module targets astarget_include_directories(<tgt> SYSTEM PUBLIC ...)(both the library and the<module>Moduleinterface target). Unlike the regular include dirs a few lines above — which are wrapped per entry in$<BUILD_INTERFACE:>plus an explicit$<INSTALL_INTERFACE:>— the system include dirs were appended raw:The in-code comment states the assumption: "System include directories are assumed to be external dependencies that are not installed, and thus do not have separate install interface paths." That holds for stable system paths (
/usr/include/eigen3, a system OpenCL SDK, …) which are valid at both build and install time, so a raw entry is fine.It breaks when a module's system include dir is a build-tree path. A remote module that pulls a header-only library via
FetchContent/ExternalDatatypically exposes something like${CMAKE_BINARY_DIR}/.../_deps/include. That path:is not relocatable (it is meaningless after install / on another machine), and
is rejected outright by
install(EXPORT). Because the raw build-tree path lands inINTERFACE_INCLUDE_DIRECTORIESof an exported target, CMake (strict since 3.x, hard-erroring under the 4.x policy set) fails at generate time:The whole ITK configure then fails generation, even if the consumer never installs, because
install(EXPORT ITKTargets)is always set up.Fix: wrap only the build-tree-prefixed system paths in
$<BUILD_INTERFACE:>(apply during build, dropped from the install interface, mirroring how regular include dirs are already handled). Non-build paths pass through unchanged, so the common case is byte-for-byte unaffected.How it was found + validation
Surfaced building ITK
mainwithModule_VkFFTBackend=ON(the VkFFT remote module fetches the header-only VkFFT library into<build>/.../_deps/includeand adds it toVkFFTBackend_SYSTEM_INCLUDE_DIRS) under CMake 4.2.1. Without this change, generate fails as above; with it, generate and the full build/install succeed.pre-commit run --all-files→ clean (gersemi-formatted).mainconfigure + generate + build + install withModule_VkFFTBackend=ON(where the build-tree path is wrapped) → success.mainconfigure + generate with no remote modules (where every system path is a stable/usr-style path and the new branch is a no-op) → success, confirming no regression for the common case.CMake-build-system change only; no new tests.