Skip to content

COMP: Pin DCMTK with single-export namespace fix - #6548

Merged
hjmjohnson merged 1 commit into
InsightSoftwareConsortium:mainfrom
hjmjohnson:fix-dcmtk-buildtree-export-repair
Jul 14, 2026
Merged

COMP: Pin DCMTK with single-export namespace fix#6548
hjmjohnson merged 1 commit into
InsightSoftwareConsortium:mainfrom
hjmjohnson:fix-dcmtk-buildtree-export-repair

Conversation

@hjmjohnson

@hjmjohnson hjmjohnson commented Jul 3, 2026

Copy link
Copy Markdown
Member

Pin DCMTK to the head of InsightSoftwareConsortium/DCMTK#3 (@blowekamp's fix: one export() call instead of three export(APPEND) calls), so the build-tree DCMTKTargets.cmake records ITK's codec targets as ITK::* instead of the nonexistent DCMTK::ITK::*. Replaces the earlier ITK-side repair loop; external build-tree consumers (e.g. an ANTs SuperBuild) now survive CMake generate with no workaround in ITK.

Blocked on: InsightSoftwareConsortium/DCMTK#3 landing on a for/itk-… branch; the pin currently points at blowekamp/DCMTK because the fix SHA is not clone-reachable from the ISC repo. Flip DCMTK_GIT_REPOSITORY back once merged.

Root cause (from blowekamp's analysis on this PR)

CMake's export(TARGETS ... APPEND) skips the cross-export-set namespace lookup (HandleMissingTarget) and blindly prefixes the current namespace onto foreign dependency targets. DCMTK's GenerateCMakeExports.cmake built DCMTKTargets.cmake with three APPEND calls, so ITK's codec targets were recorded as DCMTK::ITK::ITK{ZLIB,TIFF,JPEG,PNG}Module. A single non-append export() resolves them to their real ITK:: names. Intentional (undocumented) CMake behavior, not a CMake bug. The install-tree export (install(EXPORT)) was never affected.

Testing performed (2026-07-13, pin commit f9b0320)
  • Build-tree DCMTKTargets.cmake: zero DCMTK::ITK::* entries; deps export as ITK::ITK{ZLIB,TIFF,JPEG,PNG}Module.
  • Minimal external consumer against the build tree (find_package(ITK COMPONENTS ITKIODCMTK) + itk::DCMTKImageIO::New(), same compiler as the ITK build): configure + generate + link + run all succeed. On unpatched main (post-COMP: Link namespaced DCMTK targets in the DCMTK IO modules #6543) the same consumer fails at generate with target DCMTK::ITK::ITKZLIBModule not found.
  • ctest -R DCMTK: 38/39 pass; sole Not Run is ITKIODCMTKKWStyleTest (KWStyle not installed locally — unrelated).

@github-actions github-actions Bot added type:Compiler Compiler support or related warnings type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots area:ThirdParty Issues affecting the ThirdParty module labels Jul 3, 2026
@hjmjohnson
hjmjohnson requested a review from blowekamp July 3, 2026 19:45
@hjmjohnson
hjmjohnson force-pushed the fix-dcmtk-buildtree-export-repair branch from 6eab740 to ec1ff34 Compare July 3, 2026 21:11
@hjmjohnson
hjmjohnson marked this pull request as ready for review July 4, 2026 14:12
@greptile-apps

greptile-apps Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR repairs DCMTK build-tree exports for external ITK consumers. The main changes are:

  • Re-imports DCMTK from the ITK build root for build-tree consumers.
  • Rewrites missing DCMTK:: link-interface references after find_package(DCMTK).
  • Leaves install-tree DCMTK lookup unchanged.

Confidence Score: 4/5

Safe after the suffixed-target case is handled.

The change is narrow, but the repair loop skips the actual exported target names when DCMTK_LIBRARY_SUFFIX is set.

Modules/ThirdParty/DCMTK/CMakeLists.txt

T-Rex T-Rex Logs

What T-Rex did

  • The suffixed-target repair scenario was reproduced using a deterministic harness that loads the repository CMakeLists.txt and models the suffixed DCMTK target.
  • The run demonstrated that the build-tree repair loop iterates unsuffixed names, checks DCMTK::dcmdata, skips the repair, and leaves the suffixed target's final INTERFACE_LINK_LIBRARIES containing the ITKZLIBModule entry.
  • Because CMake and Ninja were not available in PATH, the reproduction used a deterministic fallback script rather than a native CMake harness.
  • Environment checks showed cmake, ninja, and pixi were not found while gcc/g++ were available, and the end-to-end runtime path could not be executed, so no runtime finding was reported.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
Modules/ThirdParty/DCMTK/CMakeLists.txt Adds a build-tree DCMTK target link-interface repair loop, but it misses configurations using DCMTK_LIBRARY_SUFFIX.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
  participant Consumer as External build-tree consumer
  participant ITK as ITKConfig / module export code
  participant DCMTK as DCMTKConfig + DCMTKTargets
  Consumer->>ITK: find_package(ITK COMPONENTS ITKIODCMTK)
  ITK->>DCMTK: find_package(DCMTK) with build-tree DCMTK_DIR
  DCMTK-->>ITK: imports DCMTK:: targets
  ITK->>ITK: rewrite missing DCMTK:: link-interface entries
  ITK-->>Consumer: exposes corrected ITKIODCMTK dependencies
Loading
%%{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 / module export code
  participant DCMTK as DCMTKConfig + DCMTKTargets
  Consumer->>ITK: find_package(ITK COMPONENTS ITKIODCMTK)
  ITK->>DCMTK: find_package(DCMTK) with build-tree DCMTK_DIR
  DCMTK-->>ITK: imports DCMTK:: targets
  ITK->>ITK: rewrite missing DCMTK:: link-interface entries
  ITK-->>Consumer: exposes corrected ITKIODCMTK dependencies
Loading

Reviews (1): Last reviewed commit: "COMP: Repair DCMTK build-tree export ref..." | Re-trigger Greptile

Comment thread Modules/ThirdParty/DCMTK/CMakeLists.txt Outdated
Comment on lines +172 to +179
list(JOIN _ITKDCMTK_LIB_NAMES " " _itkdcmtk_lib_names_spaced)
set(
ITKDCMTK_EXPORT_CODE_BUILD
"
if(NOT ITK_BINARY_DIR)
set(DCMTK_DIR \"${CMAKE_BINARY_DIR}\")
find_package(DCMTK REQUIRED NO_MODULE)
foreach(_itkdcmtk_lib ${_itkdcmtk_lib_names_spaced})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Include suffixed targets
The repair loop uses the unsuffixed _ITKDCMTK_LIB_NAMES, but this file creates and links DCMTK::${_dcmtk_lib}${DCMTK_LIBRARY_SUFFIX} targets at lines 151-161. When DCMTK_LIBRARY_SUFFIX is non-empty, no DCMTK::dcmdata target exists, so the loop skips every exported DCMTK target and leaves the broken DCMTK::ITK::* references in the build-tree export.

Artifacts

Repro: deterministic suffixed DCMTK repair harness

  • Contains supporting evidence from the run (text/x-python; charset=utf-8).

Repro: harness execution log showing skipped repair and remaining broken entry

  • Keeps the command output available without making the summary code-heavy.

Repro: command-v output showing CMake and Ninja unavailable

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

@blowekamp

Copy link
Copy Markdown
Member

This solution does not seem quite right to me.

If DCMTK saw ITK's provided libraries as "imported" targets it would not alter the namespace. I tries one way to modify the ITK's third party libraries to be imported target to DCMTK but it did not work. I may look into modifying how DCMTK does the exports.

I have not found this type of issue in searches. I may create a minimal reproducible example and seek some advice on it.

@hjmjohnson

hjmjohnson commented Jul 8, 2026

Copy link
Copy Markdown
Member Author

I may create a minimal reproducible example and seek some advice on it.

@blowekamp See DCMTK/dcmtk#150 for a minimal reproducible example. I am trying to build a forest of tools (ANTs, BRAINSTools, Slicer, SlicerExtensions, ITK-SNAP, .... and may others, and keep running into DCMTK build failures downstream.

@blowekamp

Copy link
Copy Markdown
Member

I may create a minimal reproducible example and seek some advice on it.

@blowekamp See DCMTK/dcmtk#150 for a minimal reproducible example. I am trying to build a forest of tools (ANTs, BRAINSTools, Slicer, SlicerExtensions, ITK-SNAP, .... and may others, and keep running into DCMTK build failures downstream.

That is a good start to the MRE I was thinking. But I was going also going to create a minimal sub-project to replace DCMTK's library and export.

On thing that is atypical in ITK is that the export name space is set as a property:

set_target_properties(
${_name}
PROPERTIES
EXPORT_NAME
${_export_namespace}${_name}
)

I am currently looking at example cmake code which export multiple namespace to see how things can be done.

@hjmjohnson

Copy link
Copy Markdown
Member Author

@blowekamp Thank you for looking into this. If you find a workable solution, please just push over top of this PR, or close this and open a new PR.

@blowekamp

Copy link
Copy Markdown
Member

Root cause of DCMTK::ITK::ITKZLIBModule namespace corruption

The generated DCMTKTargets.cmake contains entries like:

INTERFACE_LINK_LIBRARIES "DCMTK::ofstd;DCMTK::oflog;DCMTK::ITK::ITKZLIBModule"

The DCMTK:: prefix is incorrectly prepended to ITK's already-namespaced targets.

Root cause: CMake's export() command has two paths for handling dependency targets that are not in the current export set:

  • Non-append mode: calls FindExportInfo(), which searches all registered build export sets in the global generator. If found in exactly one other set with one namespace, that namespace is used — e.g. ITK::ITKZLIBModule.
  • Append mode (APPEND): skips the lookup entirely and blindly prepends the current export's namespace — e.g. DCMTK::ITK::ITKZLIBModule.

DCMTK's GenerateCMakeExports.cmake uses three separate APPEND calls to build DCMTKTargets.cmake incrementally (executables, libraries, umbrella target). This triggers the append-mode shortcut in CMake's cmExportBuildFileGenerator::HandleMissingTarget, which is why the ITK codec targets get the wrong DCMTK:: prefix regardless of whether they appear in ITK's own export set.

This is intentional CMake design (comment in source: "Assume the target will be exported by another command"), not a CMake bug. It is undocumented behaviour and has no associated policy.

Reproduction: minimal CMake demo
# ITK-like target (regular INTERFACE library + alias)
add_library(ITKZLIBModule INTERFACE)
add_library(ITK::ITKZLIBModule ALIAS ITKZLIBModule)
export(TARGETS ITKZLIBModule NAMESPACE ITK::
    FILE "${CMAKE_BINARY_DIR}/ITKTargets.cmake")

add_library(dcmdata INTERFACE)
target_link_libraries(dcmdata INTERFACE ITKZLIBModule)

# Without APPEND: HandleMissingTarget finds ITK's export set → correct
export(TARGETS dcmdata NAMESPACE DCMTK::
    FILE "${CMAKE_BINARY_DIR}/DCMTKTargets_noappend.cmake")
# Result: INTERFACE_LINK_LIBRARIES "ITK::ITKZLIBModule"  ✓

# With APPEND: HandleMissingTarget bypassed → wrong
file(WRITE "${CMAKE_BINARY_DIR}/DCMTKTargets_append.cmake" "")
export(TARGETS dcmdata APPEND NAMESPACE DCMTK::
    FILE "${CMAKE_BINARY_DIR}/DCMTKTargets_append.cmake")
# Result: INTERFACE_LINK_LIBRARIES "DCMTK::ITKZLIBModule"  ✗
Fix: merge DCMTK's three APPEND calls into one export() call

In DCMTK/CMake/GenerateCMakeExports.cmake, replace the file(WRITE) + 3×export(APPEND) pattern with a single export():

# Before (triggers append-mode namespace bypass):
file(WRITE "${CMAKE_BINARY_DIR}/DCMTKTargets.cmake" "")
get_property(DCMTK_EXECUTABLE_TARGETS GLOBAL PROPERTY DCMTK_EXECUTABLE_TARGETS)
export(TARGETS ${DCMTK_EXECUTABLE_TARGETS} APPEND FILE ... NAMESPACE DCMTK::)
get_property(DCMTK_LIBRARY_TARGETS GLOBAL PROPERTY DCMTK_LIBRARY_TARGETS)
export(TARGETS config ${DCMTK_LIBRARY_TARGETS} APPEND FILE ... NAMESPACE DCMTK::)
export(TARGETS DCMTK APPEND FILE ... NAMESPACE DCMTK::)

# After (single call; HandleMissingTarget finds ITK's export set):
get_property(DCMTK_EXECUTABLE_TARGETS GLOBAL PROPERTY DCMTK_EXECUTABLE_TARGETS)
get_property(DCMTK_LIBRARY_TARGETS GLOBAL PROPERTY DCMTK_LIBRARY_TARGETS)
export(TARGETS config ${DCMTK_LIBRARY_TARGETS} DCMTK ${DCMTK_EXECUTABLE_TARGETS}
    FILE "${CMAKE_BINARY_DIR}/DCMTKTargets.cmake"
    NAMESPACE DCMTK::)

Verified: after this change DCMTKTargets.cmake correctly contains ITK::ITKZLIBModule instead of DCMTK::ITK::ITKZLIBModule.

This fix needs to be committed to ITK's patched DCMTK fork (InsightSoftwareConsortium/DCMTK) and the git tag in Modules/ThirdParty/DCMTK/DCMTKGitTag.cmake updated accordingly.

@blowekamp

Copy link
Copy Markdown
Member

The fix described above has been submitted upstream: InsightSoftwareConsortium/DCMTK#3

@hjmjohnson
hjmjohnson force-pushed the fix-dcmtk-buildtree-export-repair branch from ec1ff34 to f9b0320 Compare July 13, 2026 21:21
@github-actions github-actions Bot removed the type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots label Jul 13, 2026
@hjmjohnson

Copy link
Copy Markdown
Member Author

Reworked per @blowekamp's root-cause fix: the ITK-side repair loop is gone, replaced by a DCMTK pin bump to the head of InsightSoftwareConsortium/DCMTK#3 (single export() call instead of three export(APPEND) calls). Verified locally end-to-end against the build-tree consumer repro.

Local verification (2026-07-13)
  • Branch rebased onto current main (post-COMP: Link namespaced DCMTK targets in the DCMTK IO modules #6543), single commit: pin DCMTK_GIT_TAG to 26222d3573b.
  • The commit only exists on blowekamp/DCMTK (fix-export-append-namespace), so DCMTK_GIT_REPOSITORY temporarily points at that fork; a plain clone of the ISC repo cannot check out a PR-only SHA.
  • Build-tree DCMTKTargets.cmake: zero DCMTK::ITK::* entries; codec deps export as ITK::ITK{ZLIB,TIFF,JPEG,PNG}Module.
  • Minimal external consumer (find_package(ITK COMPONENTS ITKIODCMTK) against the build tree, same compiler): configure + generate + link + run all succeed. On unpatched main the same consumer fails at generate with target DCMTK::ITK::ITKZLIBModule not found.
  • ctest -R DCMTK: 38/39 pass; the sole Not Run is ITKIODCMTKKWStyleTest (KWStyle executable not installed locally — unrelated).

Blocked on: InsightSoftwareConsortium/DCMTK#3 landing on a for/itk-… branch of the ISC fork; then the pin flips back to the ISC URL at that head and this PR is mergeable.

@hjmjohnson hjmjohnson changed the title COMP: Repair DCMTK build-tree export references for external consumers COMP: Pin DCMTK with single-export namespace fix Jul 13, 2026
@blowekamp

Copy link
Copy Markdown
Member

@bradking We now building DCMTK with FetchContent, and using namespace ITK third-party interfaces/libraries and exporting into a separate Targets file. As documented in my "Root cause of DCMTK::ITK::ITKZLIBModule namespace corruption" post above it appears that the CMake export(TARGETS ... APPEND ...) does not properly support targets exported to separate files. Do you have any recommendations for best practices here or references for this behavior? TYIA

@bradking

Copy link
Copy Markdown
Member

export(TARGETS)'s APPEND mode does not have global information about the complete export set and therefore cannot reliably add a namespace prefix consistently. I suggest avoiding it altogether and instead using export(EXPORT) in combination with install(TARGETS ... EXPORT ...).

@blowekamp

Copy link
Copy Markdown
Member

export(TARGETS)'s APPEND mode does not have global information about the complete export set and therefore cannot reliably add a namespace prefix consistently. I suggest avoiding it altogether and instead using export(EXPORT) in combination with install(TARGETS ... EXPORT ...).

This is what the change is for DCMTK. Thank you.

DCMTK's GenerateCMakeExports.cmake wrote DCMTKTargets.cmake with three
export(APPEND) calls; CMake's append mode skips the cross-export-set
namespace lookup and blindly prefixes DCMTK:: onto ITK's codec targets,
recording the nonexistent DCMTK::ITK::ITKZLIBModule (and TIFF/JPEG/PNG).
An external consumer of an ITK build tree then fails at generate. The
pinned InsightSoftwareConsortium/DCMTK#3 merges the exports into a
single export() call so foreign targets resolve to their ITK:: names.

Root cause analysis and DCMTK fix by Bradley Lowekamp.
@blowekamp
blowekamp force-pushed the fix-dcmtk-buildtree-export-repair branch from f9b0320 to 94fc539 Compare July 14, 2026 14:53
@blowekamp

Copy link
Copy Markdown
Member

@hjmjohnson Merged DCMTK pr, and update this PR.

@hjmjohnson

Copy link
Copy Markdown
Member Author

I confirmed that downstream (ANTs testbed) builds are fixed with this change! Yippie! This was a blocker for testing downstream tool building.

@hjmjohnson
hjmjohnson merged commit a40bf87 into InsightSoftwareConsortium:main Jul 14, 2026
19 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ThirdParty Issues affecting the ThirdParty module type:Compiler Compiler support or related warnings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants