COMP: Fix DCMTK build-tree export for external ITK consumers#6543
COMP: Fix DCMTK build-tree export for external ITK consumers#6543hjmjohnson wants to merge 3 commits into
Conversation
Match the vendored DCMTK version and guarantee that the system package config exports DCMTK::-namespaced imported targets, which the DCMTK IO modules link against.
Bare dcmdata/dcmimgle/... names survive only inside ITK's own build scope; the exported link interface then carries $<LINK_ONLY:dcmdata>, which no external consumer of the ITK build tree can resolve. The DCMTK:: wrapper targets export as DCMTK::dcmdata, which consumers resolve through find_package(DCMTK). Pin BUILD_SINGLE_SHARED_LIBRARY=OFF for the vendored build: a single- library build renames the per-library targets with a suffix that the DCMTK::<lib> link names cannot match, and the suffix variable is not visible outside DCMTK's directory scope. This also retires the dead DCMTK_LIBRARY_SUFFIX handling in the wrapper loop.
DCMTK's build-tree DCMTKTargets.cmake is written by the legacy export(TARGETS ... NAMESPACE DCMTK::), which prefixes DCMTK:: onto ITK's imported codec targets and records the nonexistent DCMTK::ITK::ITKZLIBModule (and TIFF/JPEG/PNG). An external consumer of an ITK build tree that resolves the DCMTK targets (e.g. an ANTs SuperBuild, where ITK_BINARY_DIR is unset in the find_package(ITK) scope) fails at generate with 'target DCMTK::ITK::ITKZLIBModule not found'. The install-tree export, written by install(EXPORT), resolves the same dependencies to their real ITK::* names and is unaffected. Rewrite the mis-prefixed link-interface entries in place after the existing find_package(DCMTK) import. Patching after import preserves the DCMTK_DIR seeding and every DCMTKConfig side effect (DCMTK_FOUND, DCMTK_INCLUDE_DIRS, ...), and corrects the targets regardless of whether the consumer imported them before or after find_package(ITK).
|
| Filename | Overview |
|---|---|
| Modules/IO/DCMTK/src/CMakeLists.txt | Updates ITKIODCMTK to link namespaced DCMTK targets so exported build-tree consumers resolve DCMTK libraries. |
| Modules/IO/IOTransformDCMTK/src/CMakeLists.txt | Updates IOTransformDCMTK to link the namespaced DCMTK dcmdata target. |
| Modules/ThirdParty/DCMTK/CMakeLists.txt | Adds vendored DCMTK namespaced wrappers and build-tree export repair, with one cache override issue in the single-library mode pin. |
| Modules/ThirdParty/DCMTK/itk-module-init.cmake | Raises system DCMTK discovery to require version 3.7.0 for namespaced target availability. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Consumer as External build-tree consumer
participant ITK as ITKConfig / ITKDCMTK export code
participant DCMTK as DCMTKConfig.cmake
participant Targets as Imported targets
Consumer->>ITK: find_package(ITK COMPONENTS ITKIODCMTK)
ITK->>DCMTK: set DCMTK_DIR to ITK build root
ITK->>DCMTK: find_package(DCMTK REQUIRED NO_MODULE)
DCMTK->>Targets: import DCMTK::dcmdata and related targets
ITK->>Targets: "rewrite DCMTK::ITK::*Module links to ITK::*Module"
Consumer->>Targets: "link ITKIODCMTK / IOTransformDCMTK via DCMTK::* targets"
%%{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"}}}%%
sequenceDiagram
participant Consumer as External build-tree consumer
participant ITK as ITKConfig / ITKDCMTK export code
participant DCMTK as DCMTKConfig.cmake
participant Targets as Imported targets
Consumer->>ITK: find_package(ITK COMPONENTS ITKIODCMTK)
ITK->>DCMTK: set DCMTK_DIR to ITK build root
ITK->>DCMTK: find_package(DCMTK REQUIRED NO_MODULE)
DCMTK->>Targets: import DCMTK::dcmdata and related targets
ITK->>Targets: "rewrite DCMTK::ITK::*Module links to ITK::*Module"
Consumer->>Targets: "link ITKIODCMTK / IOTransformDCMTK via DCMTK::* targets"
Comments Outside Diff (1)
-
General comment
Build-tree ITK import skips DCMTK target rewrite after DCMTK was imported first
- Bug
- The PR claim requires an external build-tree consumer to import DCMTK from the ITK build root before
find_package(ITK), then have ITK's package import rewrite already-importedDCMTK::targets fromDCMTK::ITK::*Modulereferences toITK::*Module. Executed head evidence shows this does not happen: afterfind_package(ITK REQUIRED COMPONENTS ITKIODCMTK),DCMTK::dcmdatastill containsDCMTK::ITK::ITKZLIBModule. This leaves the same broken imported-target namespace that the change was intended to repair.
- The PR claim requires an external build-tree consumer to import DCMTK from the ITK build root before
- Cause
- In
Modules/ThirdParty/DCMTK/CMakeLists.txt, the generated build-tree export code added at lines 172-187 is wrapped inif(NOT ITK_BINARY_DIR). In the build-tree consumer scenario, importing ITK definesITK_BINARY_DIR, so the rewrite loop is skipped and previously importedDCMTK::targets are not repaired.
- In
- Fix
- Run the rewrite loop for external consumers even when
ITK_BINARY_DIRis defined by a build-tree ITK package import. For example, do not guard the repair withif(NOT ITK_BINARY_DIR), or replace it with a condition that only avoids recursive/inside-ITK execution while still executing during external build-tree package import. Then verify thatDCMTK::ITK::entries becomeITK::afterfind_package(ITK).
- Run the rewrite loop for external consumers even when
- Bug
Reviews (1): Last reviewed commit: "COMP: Repair DCMTK build-tree export ref..." | Re-trigger Greptile
| set(BUILD_APPS OFF) | ||
| # Per-library targets only: a single-library build renames targets with a | ||
| # suffix the DCMTK IO modules' DCMTK::<lib> link names cannot match. | ||
| set(BUILD_SINGLE_SHARED_LIBRARY OFF CACHE INTERNAL "") |
There was a problem hiding this comment.
Cache value not forced
set(BUILD_SINGLE_SHARED_LIBRARY OFF CACHE INTERNAL "") does not replace an existing cache entry, so a configure that already has BUILD_SINGLE_SHARED_LIBRARY=ON keeps DCMTK in single-library mode. The code below then looks for unsuffixed per-library targets like dcmdata, finds none, and the IO modules now link DCMTK::dcmdata, which is never created.
| set(BUILD_SINGLE_SHARED_LIBRARY OFF CACHE INTERNAL "") | |
| set(BUILD_SINGLE_SHARED_LIBRARY OFF CACHE INTERNAL "" FORCE) |
There was a problem hiding this comment.
Just do set(BUILD_SINGLE_SHARED_LIBRARY OFF) it should not be a cache variable.
There was a problem hiding this comment.
Agreed. A plain set(BUILD_SINGLE_SHARED_LIBRARY OFF) in the parent scope will shadow any cache entry of that name when CMake evaluates it in the FetchContent subdirectory, which is the correct and idiomatic way to force options on FetchContent dependencies. The CACHE INTERNAL form doesn't force-overwrite an existing cache entry (that requires FORCE), making it unreliable.
| set(BUILD_SINGLE_SHARED_LIBRARY OFF CACHE INTERNAL "") | |
| set(BUILD_SINGLE_SHARED_LIBRARY OFF) |
ITK's FetchContent-built DCMTK writes a build-tree
DCMTKTargets.cmakewhose imported targets reference the nonexistentDCMTK::ITK::ITKZLIBModule(and TIFF/JPEG/PNG variants), so external consumers of an ITK build tree (e.g. an ANTs SuperBuild) fail at configure. Three commits: repair the export references in place, link namespacedDCMTK::targets in the IO modules, and require DCMTK ≥ 3.7.0 forITK_USE_SYSTEM_DCMTK.Root cause
export(TARGETS ... NAMESPACE DCMTK::), which blindly prefixesDCMTK::onto ITK's already-namespaced codec targets passed in viaset(ZLIB_LIBRARIES ITK::ITKZLIBModule)etc. The install-tree export, written byinstall(EXPORT), resolves the same dependencies correctly toITK::*Module.find_package(ITK)scope hasITK_BINARY_DIRunset (any external project, notably SuperBuilds) runsfind_package(DCMTK)fromITKDCMTK.cmakeand imports the broken targets:set_target_properties: target DCMTK::ITK::ITKZLIBModule not found.ITKIODCMTK/IOTransformDCMTKlinked the bare in-scopedcmdata… targets, so the exported link interface carried$<LINK_ONLY:dcmdata>— unresolvable outside ITK's own build; consumers then failed at link withld: library not found for -ldcmdata. (The install export mapped these toDCMTK::dcmdata; only the build export kept them bare.)The fix patches the imported targets'
INTERFACE_LINK_LIBRARIESin place after the existingfind_package(DCMTK), preservingDCMTK_DIRseeding and allDCMTKConfigside effects (DCMTK_FOUND,DCMTK_INCLUDE_DIRS, …), and correcting the targets regardless of whether the consumer imported DCMTK before or after ITK.Downstream testing performed
All against a full ITK build (
Module_ITKIODCMTK=ON,ITK_BUILD_ALL_MODULES=ON, macOS arm64, clang 19, CMake 4.3):find_package(ITK REQUIRED COMPONENTS ITKIODCMTK)withITK_BINARY_DIR='': configures, builds, links, and runs (itk::DCMTKImageIO::New(), rc=0).DCMTK_FOUND=1,DCMTK_DIRandDCMTK_INCLUDE_DIRSpopulated. Before the fix: configure error onDCMTK::ITK::ITKZLIBModule; with only the export repair: link error-ldcmdata not found.find_package(DCMTK)against the ITK build root beforefind_package(ITK)(SuperBuild pattern): probes confirm the brokenDCMTK::ITK::ITKZLIBModulereference is present after the first import and rewritten toITK::ITKZLIBModuleafterfind_package(ITK); build+link+run OK.ctest -R DCMTK: 48/50 pass; the 2 failures are a pre-existing local KWStyle binary rpath issue (aborts before checking code).pre-commit run --all-filesclean on the tip.Commit breakdown
COMP: Require DCMTK 3.7.0 or later for ITK_USE_SYSTEM_DCMTKDCMTK::-namespaced system package targetsCOMP: Link namespaced DCMTK targets in the DCMTK IO modulesDCMTK::dcmdata…; pinsBUILD_SINGLE_SHARED_LIBRARY=OFFand retires dead suffix handlingCOMP: Repair DCMTK build-tree export references for external consumersDCMTK::ITK::*Module→ITK::*Modulerewrite afterfind_package(DCMTK)