Skip to content

ITKModuleMacros: _SYSTEM_INCLUDE_DIRS does not support in-source paths (blocks SYSTEM treatment of vendored Eigen) #6224

@hjmjohnson

Description

@hjmjohnson

CMake/ITKModuleMacros.cmake adds entries from ${itk-module}_SYSTEM_INCLUDE_DIRS into ${itk-module}_SYSTEM_GENEX_INCLUDE_DIRS as raw absolute paths with no $<BUILD_INTERFACE:> / $<INSTALL_INTERFACE:> guards, so the variable cannot be used for in-source paths (export step rejects unguarded absolute paths in INTERFACE_INCLUDE_DIRECTORIES). This blocks the cleanest fix for warning-suppression on vendored Eigen and any future in-source SYSTEM-include need.

Surfaced from PR #6223's Greptile P2 review (warning leak from ${ITKEigen3_SOURCE_DIR}/src/itkeigen exposed as regular -I rather than -isystem).

Reproducer

In Modules/ThirdParty/Eigen3/CMakeLists.txt (vendored branch), replace the regular-include addition with the structurally-correct SYSTEM equivalent:

-  set(ITKEigen3_INCLUDE_DIRS ${ITKEigen3_SOURCE_DIR}/src ${ITKEigen3_SOURCE_DIR}/src/itkeigen)
+  set(ITKEigen3_INCLUDE_DIRS ${ITKEigen3_SOURCE_DIR}/src)
+  set(ITKEigen3_SYSTEM_INCLUDE_DIRS ${ITKEigen3_SOURCE_DIR}/src/itkeigen)

CMake fails the generate step:

CMake Error in Modules/ThirdParty/Eigen3/CMakeLists.txt:
  Target "ITKEigen3Module" INTERFACE_INCLUDE_DIRECTORIES property contains
  path: "/path/to/Modules/ThirdParty/Eigen3/src/itkeigen"
Root cause

CMake/ITKModuleMacros.cmake:264-289 handles _INCLUDE_DIRS and _SYSTEM_INCLUDE_DIRS asymmetrically:

# Lines 264-271: regular includes get $<BUILD_INTERFACE:> + $<INSTALL_INTERFACE:>
foreach(_dir ${${itk-module}_INCLUDE_DIRS})
  list(APPEND ${itk-module}_GENEX_INCLUDE_DIRS "$<BUILD_INTERFACE:${_dir}>")
endforeach()
list(APPEND ${itk-module}_GENEX_INCLUDE_DIRS
  "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${${itk-module}_INSTALL_INCLUDE_DIR}>")

# Lines 273-279: SYSTEM includes added raw — comment explicitly opts out
# of install-interface support.
if(${itk-module}_SYSTEM_INCLUDE_DIRS)
  foreach(_dir ${${itk-module}_SYSTEM_INCLUDE_DIRS})
    list(APPEND ${itk-module}_SYSTEM_GENEX_INCLUDE_DIRS ${_dir})
  endforeach()
endif()

Comment at line 273-274 makes the design intent explicit:

System include directories are assumed to be external dependencies that are not installed, and thus do not have separate install interface paths.

So _SYSTEM_INCLUDE_DIRS is structurally a /usr/include-style escape hatch for paths to libraries that exist independently of the ITK install tree. In-source paths (anything under ${ITKEigen3_SOURCE_DIR}/...) violate this assumption: at export time CMake walks INTERFACE_INCLUDE_DIRECTORIES looking for unguarded absolute paths that aren't valid in the install interface and rejects them.

Why this matters now

PR #6223 unblocks proxTV / InsightSoftwareConsortium/ITKTotalVariation#57 by adding ${ITKEigen3_SOURCE_DIR}/src/itkeigen to ITKEigen3_INCLUDE_DIRS so external consumers using <Eigen/<header>> resolve via ITK's vendored Eigen. That works (verified end-to-end), but it exposes the path as -I (regular include) rather than -isystem. Eigen 5's templates emit -Wshadow / -Wmaybe-uninitialized / -Wctad-maybe-unsupported etc. that downstream consumers compiling with -Wall -Wextra will start seeing.

The neighboring vendored declaration in Modules/ThirdParty/Eigen3/src/itkeigen/CMakeLists.txt already uses the correct mechanism for the same physical directory:

target_include_directories (eigen_internal SYSTEM INTERFACE
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
  "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${ITK3P_INSTALL_INCLUDE_DIR}/itkeigen>;"
)

This is what the macro version should produce when given an in-source SYSTEM path; today it cannot.

By symmetry the long-standing ${ITKEigen3_SOURCE_DIR}/src regular include also probably wants SYSTEM treatment — it leaks the same Eigen warnings to any non-third-party consumer of ITK::ITKEigen3Module (e.g., ITKCommon and every downstream module that ultimately resolves an ITK_EIGEN(<header>) macro to <itkeigen/Eigen/<header>>). It's been regular-include for the entire 4.x/5.x/6.x history and the codebase apparently doesn't trip on the warnings under ITK's own CI flags, but a careful audit would resolve whether it should also be SYSTEM.

Three approaches, in increasing scope

Approach A — Bypass the macro for the specific eigen path

Add the SYSTEM include directly on the eigen_internal target inside Modules/ThirdParty/Eigen3/src/itkeigen/CMakeLists.txt, mirroring the existing ${CMAKE_CURRENT_SOURCE_DIR} line:

target_include_directories (eigen_internal SYSTEM INTERFACE
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>          # <-- add for <Eigen/X> path
  "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${ITK3P_INSTALL_INCLUDE_DIR}/itkeigen>;"
)

Wait — actually that's the same dir. The new path is ${ITKEigen3_SOURCE_DIR}/src/itkeigen which equals ${CMAKE_CURRENT_SOURCE_DIR} of the inner CMakeLists. So this already exists. Need to verify why the existing SYSTEM INTERFACE on eigen_internal doesn't propagate to external <Eigen/<header>> consumers via ITK::ITKEigen3Module → ITK::eigen_internal link transitively. May be path normalization differences during target export (.../itkeigen vs .../itkeigen/..).

Pros: smallest change; bypasses the broken macro path.
Cons: needs investigation — the existing SYSTEM declaration may already be doing the right thing and PR #6223's regular-include addition may be redundant. Or the export representation reorders paths in a way that breaks resolution.

Approach B — Fix the macro for in-source SYSTEM paths

Extend the SYSTEM-include loop in CMake/ITKModuleMacros.cmake:275-279 to mirror the regular-include treatment:

foreach(_dir ${${itk-module}_SYSTEM_INCLUDE_DIRS})
  list(APPEND ${itk-module}_SYSTEM_GENEX_INCLUDE_DIRS "$<BUILD_INTERFACE:${_dir}>")
endforeach()

(With $<INSTALL_INTERFACE:> only added when an install-side mapping makes sense — the existing comment about external dependencies suggests the install side should typically be empty or find_package-driven.)

Pros: Makes _SYSTEM_INCLUDE_DIRS work for in-source paths, which is what most module authors actually want. Closes the gap between the regular and SYSTEM behaviors.
Cons: touches the module macro every ITK module uses. Need to audit existing _SYSTEM_INCLUDE_DIRS consumers — if any module depends on the raw-path behavior (e.g., to inject a system path that should survive into the install interface), that consumer breaks. Quick grep suggests few in-tree consumers but every third-party module is a potential dependency.

Approach C — Move every Eigen include to SYSTEM, with audit

Once Approach B is in place, also move ${ITKEigen3_SOURCE_DIR}/src from _INCLUDE_DIRS to _SYSTEM_INCLUDE_DIRS for symmetry. The existing <itkeigen/Eigen/<header>> consumers (ITKCommon and friends) start receiving -isystem instead of -I, suppressing whatever warnings might leak. Lower priority because nothing's currently breaking.

Pros: full warning-suppression parity; one consistent SYSTEM treatment for the vendored Eigen.
Cons: changes warning surface for the whole tree. Needs -Werror CI on a representative platform set to surface any latent issues. Possibly trivial in practice but should be its own PR with a clean test cycle.

Recommended sequencing
  1. First: investigate Approach A (does the existing eigen_internal SYSTEM INTERFACE already do the right thing once we understand the path-normalization quirk?). Cheap, may resolve the warning concern entirely without touching ITKModuleMacros.cmake.
  2. If Approach A is insufficient: do Approach B as a standalone PR. Touch only the macro, audit existing consumers, ensure no regression in install-interface validity.
  3. Optionally after B: do Approach C as a separate PR with -Werror CI verification.

PR #6223 ships the regular-_INCLUDE_DIRS form because it's the only one that actually generates today; this issue tracks the SYSTEM-treatment cleanup so it doesn't fall through.

Files / lines of interest
File Line Role
CMake/ITKModuleMacros.cmake 264-271 Regular _INCLUDE_DIRS loop with $<BUILD_INTERFACE:>
CMake/ITKModuleMacros.cmake 273-279 SYSTEM _SYSTEM_INCLUDE_DIRS loop without generator-expression guards (the bug)
CMake/ITKModuleMacros.cmake 281-289 THIRD_PARTY consumption of both variables via include_directories(...) and include_directories(SYSTEM ...)
Modules/ThirdParty/Eigen3/CMakeLists.txt 56-69 Where this issue first surfaced; PR #6223 sets _INCLUDE_DIRS for both paths today
Modules/ThirdParty/Eigen3/src/itkeigen/CMakeLists.txt (the target_include_directories(eigen_internal SYSTEM INTERFACE ...) block) The existing in-source SYSTEM declaration; reference for what the macro version should produce

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions