InsightSoftwareConsortium / InsightSoftwareConsortium/ITK
Factory registration include directory collides between targets in one project
- Dominant language
- C++
- Stars
- 1.7k
- Forks
- 748
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 64
Description
`itk_generate_factory_registration()` stores the generated header's include directory on the shared `ITK::ITKImageIO` target, so in a project with more than one target requesting different IO components the last call wins and the other targets compile against the wrong registration header.
### Reproducer
```cmake
# CMakeLists.txt
cmake_minimum_required(VERSION 3.22.1)
project(FactoryRegistrationCollision CXX)
find_package(ITK 6 REQUIRED) # aggregate project finds ITK once
add_subdirectory(png_example)
add_subdirectory(meta_example)
```
```cmake
# png_example/CMakeLists.txt (meta_example is identical with ITKIOMeta)
project(png_example CXX)
find_package(ITK REQUIRED COMPONENTS ITKCommon ITKIOImageBase ITKIOPNG)
itk_generate_factory_registration()
add_executable(png_example prog.cxx)
target_link_libraries(png_example PRIVATE ITK::ITKCommonModule ITK::ITKImageIO)
```
`prog.cxx` writes a 4x4 image to `argv[1]` with `itk::WriteImage`.
### Observed
| target | components requested | factories in binary | runtime |
|---|---|---|---|
| `png_example` | `ITKIOPNG` | `Meta` | fails to write `.png` |
| `meta_example` | `ITKIOMeta` | `Meta` | writes `.mha` |
`png_example` fails with `Could not create IO object for writing file`, because it was compiled against `meta_example`'s registration header.
### Expected
Each target registers the factories for the components it requested.
### Cause
`_itk_configure_FactoryRegisterManager` in `CMake/ITKFactoryRegistration.cmake` writes the header to `${CMAKE_CURRENT_BINARY_DIR}/ITKFactoryRegistration` and then points the meta-module target at it:
```cmake
set_property(
TARGET ${_meta_module}
PROPERTY INTERFACE_INCLUDE_DIRECTORIES
"$"
)
```
The property is overwritten on each call. Two sibling subdirectories that each call `find_package(ITK)` themselves are unaffected, because each gets its own imported target; the collision appears once a parent scope calls `find_package(ITK)` and the subdirectories inherit that target.
### Impact
Per-target narrowing of IO components is not usable in a single-project build. In ITKSphinxExamples, narrowing each example to the formats it reads reduces the built examples from 5.26 GB to 0.72 GB, but only works when each example is configured as its own project; in the aggregate build 62 of 558 tests fail this way.
Verified against ITK `main` (6.0.0) on macOS with AppleClang.
Contributor guide
Research direction
Start in CMake/ITKFactoryRegistration.cmake, especially _itk_configure_FactoryRegisterManager and itk_generate_factory_registration(), then reproduce the collision with the parent find_package(ITK) and sibling example setup shown in the issue. Done means separate targets retain the registration headers for their requested IO components and the aggregate build no longer exhibits the reported wrong-header failures.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cmake, cpp
- Domain
- build-system
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100