Skip to content

Fix build-tree export references for superproject builds#150

Open
hjmjohnson wants to merge 1 commit into
DCMTK:masterfrom
hjmjohnson:fix-buildtree-export-superproject-namespace
Open

Fix build-tree export references for superproject builds#150
hjmjohnson wants to merge 1 commit into
DCMTK:masterfrom
hjmjohnson:fix-buildtree-export-superproject-namespace

Conversation

@hjmjohnson

@hjmjohnson hjmjohnson commented Jul 3, 2026

Copy link
Copy Markdown

When DCMTK is vendored inside a superproject (add_subdirectory()/FetchContent()) and the superproject supplies its own imported codec targets (e.g. ZLIB_LIBRARIES=ITK::ITKZLIBModule), the build-tree DCMTKTargets.cmake written by export(TARGETS ... NAMESPACE DCMTK::) records nonexistent double-namespaced targets like DCMTK::ITK::ITKZLIBModule. This patch has DCMTKConfig.cmake strip the mis-applied prefix from any DCMTK::-prefixed link-interface reference that does not name an existing target after DCMTKTargets.cmake has been imported (all genuine DCMTK:: targets exist at that point) — a no-op for the install tree and for standalone builds.

Failure this fixes

ITK vendors DCMTK via FetchContent and points external consumers of ITK's build tree at the generated DCMTKConfig.cmake. Such a consumer (e.g. an ANTs SuperBuild) fails at CMake generate:

CMake Error at .../DCMTKTargets.cmake:83 (set_target_properties):
  The link interface of target "DCMTK::dcmdata" contains:

    DCMTK::ITK::ITKZLIBModule

  but the target was not found.

The root cause is export(TARGETS) semantics: it namespace-prefixes every in-build target referenced by the exported link interfaces, including targets owned by the superproject rather than by DCMTK. The install-tree export, written by install(EXPORT), resolves the same references to their real names and is unaffected. Two shapes occur: ITK's FetchContent setup records the namespaced name (DCMTK::ITK::ITKZLIBModule), while a plain ALIAS supplied by a superproject is recorded de-aliased (DCMTK::super_zlibwrap). Checking target existence after import handles both without ever touching genuine DCMTK targets. A self-contained minimal failing test case (no ITK needed) is in the first PR comment.

Downstream context: InsightSoftwareConsortium/ITK#6548 carries the same repair on the ITK side; with this patch in DCMTK, that consumer-side workaround becomes unnecessary. Related in spirit to #145, which handled external imported targets in DCMTKConfig.cmake for standalone builds.

Verification

Reproduced and validated against ITK's FetchContent vendoring of DCMTK (Linux, CMake 3.28, Ninja):

  1. Build ITK with Module_ITKIODCMTK=ON (vendored DCMTK); grep 'DCMTK::ITK::' <build>/DCMTKTargets.cmake shows the mis-prefixed entries.
  2. Configure a minimal external project against the ITK build tree (find_package(ITK COMPONENTS ITKIODCMTK) + link + run): fails at generate with the error above.
  3. With only this DCMTKConfig.cmake.in change applied to the vendored DCMTK source and ITK reconfigured, the same consumer configures, generates, links, and runs.
  4. Also validated against the self-contained dependency-provider test case in the PR comment (pristine master fails at consumer generate; with this patch it passes, and the superproject's install(EXPORT) output is unchanged).

The build-tree DCMTKTargets.cmake is written by
export(TARGETS ... NAMESPACE DCMTK::), which prefixes DCMTK:: onto
every in-build target referenced by the exported link interfaces,
including targets owned by a superproject that vendors DCMTK via
add_subdirectory() or FetchContent() (e.g. a codec target supplied
through ZLIB_LIBRARIES). The export then records nonexistent targets
such as DCMTK::ITK::ITKZLIBModule or DCMTK::super_zlibwrap, and any
external consumer of the superproject's build tree that imports
DCMTKConfig fails at CMake generate with 'target ... not found'. The
install-tree export is written by install(EXPORT) and resolves the
same references correctly.

All genuine DCMTK:: targets are defined once DCMTKConfig.cmake has
included DCMTKTargets.cmake, so strip the mis-applied prefix from any
DCMTK::-prefixed link-interface reference that is not an existing
target. For the install tree and for standalone builds the loop is a
no-op.
@hjmjohnson hjmjohnson force-pushed the fix-buildtree-export-superproject-namespace branch from a867c7c to 6d69495 Compare July 3, 2026 20:59
@hjmjohnson

Copy link
Copy Markdown
Author

Here is a self-contained minimal failing test case (no ITK required) — four small files, CMake ≥ 3.24, configure-only. It uses a dependency provider, the standard mechanism for a superproject to satisfy a vendored project's find_package() calls with its own targets.

super/CMakeLists.txt — a superproject that vendors DCMTK and supplies its own zlib wrapper target (mirroring what ITK does with ITK::ITKZLIBModule):

cmake_minimum_required(VERSION 3.24)
project(super C CXX)
add_library(super_zlibwrap INTERFACE)
add_library(Super::zlibwrap ALIAS super_zlibwrap)
install(TARGETS super_zlibwrap EXPORT SuperTargets)
install(EXPORT SuperTargets NAMESPACE Super:: DESTINATION lib/cmake/super)
set(BUILD_APPS OFF)
add_subdirectory(${DCMTK_SOURCE} dcmtk)

super/provider.cmake — routes DCMTK's find_package(ZLIB) to the superproject's target:

macro(super_provide_dependency method dep_name)
  if("${dep_name}" STREQUAL "ZLIB")
    find_package(ZLIB BYPASS_PROVIDER)
    set(ZLIB_LIBRARIES Super::zlibwrap)
  endif()
endmacro()
cmake_language(SET_DEPENDENCY_PROVIDER super_provide_dependency SUPPORTED_METHODS FIND_PACKAGE)

consumer/CMakeLists.txt — an external project importing the superproject's build tree:

cmake_minimum_required(VERSION 3.24)
project(consumer C CXX)
find_package(DCMTK REQUIRED CONFIG)
add_executable(t main.cxx)
target_link_libraries(t DCMTK::dcmdata)

(consumer/main.cxx is just int main() { return 0; }.)

Run:

cmake -S super -B super-b -G Ninja \
  -DDCMTK_SOURCE=/path/to/dcmtk \
  -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=$PWD/super/provider.cmake \
  -DDCMTK_WITH_ZLIB=ON
# superproject configures and generates with no errors, but:
grep 'DCMTK::super_zlibwrap' super-b/DCMTKTargets.cmake   # the mis-prefixed reference

cmake -S consumer -B consumer-b -G Ninja -DDCMTK_DIR=$PWD/super-b

Result on current master — the consumer fails at generate:

CMake Error at .../super-b/DCMTKTargets.cmake:83 (set_target_properties):
  The link interface of target "DCMTK::dcmdata" contains:

    DCMTK::super_zlibwrap

  but the target was not found.

With this PR applied, the same consumer configures and generates cleanly, and the superproject's own install(EXPORT) behavior is unchanged (the install tree resolves super_zlibwrap to Super::super_zlibwrap correctly on its own).

Note the test case surfaced a second shape of the same defect: when the superproject's value is a plain ALIAS, CMake records the de-aliased real name (DCMTK::super_zlibwrap); in ITK's FetchContent setup the namespaced name itself is recorded (DCMTK::ITK::ITKZLIBModule). The PR now handles both by stripping the DCMTK:: prefix from any link-interface reference that is not an actual target once DCMTKTargets.cmake has been imported — genuine DCMTK targets all exist at that point, so real references are never touched. Verified against both this minimal case and the full ITK vendoring (external build-tree consumer configures, links, and runs).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant