NIfTI: track the maintained nifti_clib fork, drop the dead vendored copy - #152
NIfTI: track the maintained nifti_clib fork, drop the dead vendored copy#152gdevenyi wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
🟡 Not ready to approve
The new CMake target plumbing has portability/configure-time issues (notably reading deprecated LOCATION and unconditionally linking m) that can break configuration or non-UNIX builds.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
This PR updates libminc’s NIfTI integration to track the maintained ITK fork of nifti_clib, removes the unused vendored NIfTI source tree, and standardizes CMake usage around NIFTI::niftiio / NIFTI::znz targets to avoid symbol collisions and improve consumer linking behavior.
Changes:
- Pin ExternalProject NIfTI to
InsightSoftwareConsortium/nifti_clib@f24a607…and regenerate the symbol-mangling header to cover newly exported symbols. - Replace legacy variable-based linking/include plumbing with
NIFTI::niftiiotarget-based linking (including in tests) and switch system NIfTI discovery tofind_package(NIFTI CONFIG). - Delete the dead vendored
nifti/sources and remove the obsoletecmake-modules/FindNIFTI.cmake.
File summaries
| File | Description |
|---|---|
| CMakeLists.txt | Switch system NIfTI discovery to config mode and link libminc via NIFTI::niftiio; derive legacy exported variables from targets. |
| cmake-modules/BuildNIFTI.cmake | Pin to ITK fork commit and add imported NIFTI::niftiio / NIFTI::znz targets for ExternalProject builds. |
| cmake-modules/FindNIFTI.cmake | Deleted legacy module-mode finder. |
| cmake-modules/nifti_mangle.h | Regenerated mangling list to cover the fork’s exported symbols and fix header regeneration notes. |
| testdir/CMakeLists.txt | Update NIfTI tests to link with NIFTI::niftiio target. |
| nifti/nifti1.h | Deleted vendored header (part of removing unused vendored NIfTI copy). |
| nifti/nifti1_io.h | Deleted vendored header (part of removing unused vendored NIfTI copy). |
| nifti/znzlib.c | Deleted vendored source (part of removing unused vendored NIfTI copy). |
| nifti/znzlib.h | Deleted vendored header (part of removing unused vendored NIfTI copy). |
Review details
- Files reviewed: 9/10 changed files
- Comments generated: 3
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
| if(NOT NIFTI_LIBRARY) | ||
| get_target_property(NIFTI_LIBRARY NIFTI::niftiio LOCATION) | ||
| get_target_property(ZNZ_LIBRARY NIFTI::znz LOCATION) | ||
| endif() |
There was a problem hiding this comment.
Keeping LOCATION here — CMP0026 doesn't apply, and the suggested replacement would break this.
CMP0026 forbids reading LOCATION from build targets, not imported ones; imported targets are explicitly exempt. (The superbuild even sets CMP0026 NEW explicitly and this configures fine.)
More concretely, IMPORTED_LOCATION is the wrong property to read here. nifti_clib's installed NIFTITargets-release.cmake records only the per-configuration location:
set_target_properties(NIFTI::niftiio PROPERTIES
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libniftiio.so.2.1.0"so plain IMPORTED_LOCATION is never set. Measured against an installed system nifti_clib:
CMAKE_BUILD_TYPE |
LOCATION |
IMPORTED_LOCATION |
|---|---|---|
| (unset) | /usr/lib/libniftiio.so.2.1.0 |
NOTFOUND |
Release |
/usr/lib/libniftiio.so.2.1.0 |
NOTFOUND |
Debug |
/usr/lib/libniftiio.so.2.1.0 |
NOTFOUND |
RelWithDebInfo |
/usr/lib/libniftiio.so.2.1.0 |
NOTFOUND |
LOCATION resolves through IMPORTED_CONFIGURATIONS regardless of build type, which is exactly the fallback the comment asks for — CMake already does it. Switching to IMPORTED_LOCATION would silently empty NIFTI_LIBRARY, and with it NIFTI_LIBRARY_NAME in the exported LIBMINCConfig.cmake.
Added a comment in place recording this so it doesn't get re-raised.
The other two comments on this PR were both right and are fixed in ad8b403.
| if(NOT TARGET NIFTI::niftiio) | ||
| add_library(NIFTI::niftiio STATIC IMPORTED GLOBAL) | ||
| set_target_properties(NIFTI::niftiio PROPERTIES | ||
| IMPORTED_LOCATION "${NIFTI_LIBRARY}" | ||
| INTERFACE_INCLUDE_DIRECTORIES "${NIFTI_INCLUDE_DIR}" | ||
| INTERFACE_LINK_LIBRARIES "NIFTI::znz;m") | ||
| endif() |
There was a problem hiding this comment.
Right, fixed in ad8b403.
nifti_clib itself only links a math library where one exists — its NIFTI_SYSTEM_MATH_LIB is m on Emscripten/WASI, find_library(m) on other non-Windows platforms, and empty on WIN32 — and the rest of libminc only links m under if(UNIX). The interface now matches:
set(NIFTI_INTERFACE_LIBS NIFTI::znz)
if(UNIX)
list(APPEND NIFTI_INTERFACE_LIBS m)
endif()| if(NOT TARGET NIFTI::znz) | ||
| add_library(NIFTI::znz STATIC IMPORTED GLOBAL) | ||
| set_target_properties(NIFTI::znz PROPERTIES | ||
| IMPORTED_LOCATION "${ZNZ_LIBRARY}" | ||
| INTERFACE_INCLUDE_DIRECTORIES "${ZNZ_INCLUDE_DIR}" | ||
| INTERFACE_LINK_LIBRARIES "${ZLIB_LIBRARY}") | ||
| endif() |
There was a problem hiding this comment.
Agreed, fixed in ad8b403 — NIFTI::znz now links ZLIB::ZLIB rather than the raw ${ZLIB_LIBRARY} path.
Confirmed the target exists on both paths before build_nifti() runs: standalone libminc calls find_package(ZLIB REQUIRED) first (and already links ZLIB::ZLIB elsewhere), and in the superbuild BuildZLIB.cmake creates ZLIB::ZLIB as a GLOBAL imported target — so this holds with bundled zlib too, not just system zlib.
|
Added a second commit: That test asserts every exported symbol is Verified against an installed system BIC-MNI/minc-toolkit-v2#230 forwards |
|
using system NIFTI will collide with ITK's nifti |
|
Addressed the review in ad8b403. Two of the three were right and are fixed; the third I pushed back on with measurements, in the thread.
Not changed: the Re-verified against an installed system |
|
@vfonov you're right, and the failure is quieter than I'd assumed. Investigated properly — details below, and BIC-MNI/minc-toolkit-v2#230 now refuses the combination outright. Where the collision actually is. ITK 4.x does mangle its bundled niftiio: Diffing the installed system It does not fail at link time, which is what makes it worse than I expected. Reproduced with the installed system ITK's copies land in the executable's dynamic symbol table and interpose over Control, same fake Which is precisely the collision Worth adding: the two Fix. A prebuilt system library can't be renamed, and ITK 4.x has no All four combinations verified: only that one is rejected. Nothing in this PR changes. libminc has no ITK in the picture; the guard belongs in the superbuild, where ITK is built. The only libminc-side interaction is |
|
Correction to my previous comment, @vfonov — I overstated the consequence, and the difference matters. I demonstrated
So your core point stands — the collision is real and measurable, 8 znz names interpose over What's genuinely left is narrower than I implied, and still worth refusing:
BIC-MNI/minc-toolkit-v2#230 keeps the guard, with the rationale rewritten to say that rather than the alarming version. Still nothing to change in this PR. |
|
@vfonov ITK has merged my patch to fix their symbol mangling on the 4.14 branch, so we can now handle SYSTEM_NIFTI properly: minc-toolkit-v2 PRs updated |
nifti_clib upstream (NIFTI-Imaging) has not moved since v3.0.0 in 2020. The ITK fork is the maintained line, so pin that instead; it carries no tags, so the pin is a commit SHA. The bump exports three symbols v3.0.0 did not -- nifti_image_write_status, nifti_image_write_bricks_status and nifti_set_fix_floats -- which the old nifti_mangle.h left unprefixed and therefore free to collide with ITK's bundled niftiio again. Regenerated the mangle list against the new pin: all 112 exported symbols are minc_*, with no overlap against an unmangled build of the same source. Also: - build_nifti() now defines the NIFTI::niftiio / NIFTI::znz imported targets that nifti_clib's own NIFTIConfig.cmake provides, so consumers link the same way whether NIFTI came from the ExternalProject or from find_package(NIFTI CONFIG). The config package itself cannot be used there, because ExternalProject builds long after consumers configure. - LIBMINC_USE_SYSTEM_NIFTI uses find_package(NIFTI CONFIG); the hand-rolled FindNIFTI.cmake is gone. It hardcoded /usr/local/bic and reported everything it found as "NetCDF". - Deleted nifti/, a tracked copy of nifti1_io.c / znzlib.c that no build system referenced. Verified: libminc builds against the new pin and all 38 ctests pass, including nifti_readback and nifti_mangle_symbols. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
nifti_mangle_symbols asserts that every exported symbol is minc_*. That holds only for the bundled build, which is patched on the way in. Under LIBMINC_USE_SYSTEM_NIFTI the library is whatever the distribution shipped -- unmangled -- so the test would fail on a correct build. nifti_readback still runs in both modes; it is the functional check, and it is agnostic about the naming. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
From review of the imported-target block:
- NIFTI::znz linked the raw ${ZLIB_LIBRARY} path. Link ZLIB::ZLIB instead,
so consumers inherit its usage requirements rather than a bare path.
- NIFTI::niftiio hardcoded m in its interface. nifti_clib itself only
links a math library where one exists (NIFTI_SYSTEM_MATH_LIB is empty on
WIN32) and the rest of libminc only links m under if(UNIX); match that.
- file(MAKE_DIRECTORY) was unquoted, so a prefix containing spaces would
split into several arguments. Quote it, and name ZNZ_INCLUDE_DIR
explicitly rather than relying on it being the same string.
Also add_dependencies() the imported targets on the NIFTI ExternalProject,
matching ZLIB::ZLIB in the superbuild's BuildZLIB.cmake, so build ordering
rides on the target itself instead of only on the call sites.
The LOCATION read is left alone: CMP0026 forbids LOCATION on build targets,
not imported ones, and nifti_clib's installed NIFTITargets records only
IMPORTED_LOCATION_RELEASE -- so IMPORTED_LOCATION reads NOTFOUND under every
CMAKE_BUILD_TYPE while LOCATION resolves via IMPORTED_CONFIGURATIONS.
Documented in place.
Verified both ways against an installed system nifti_clib: 38 tests pass
bundled, 37 system.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
ad8b403 to
f8b6eed
Compare
LIBMINC_USE_SYSTEM_NIFTI asks for the system copy. Without REQUIRED, find_package quietly reports it as not found, LIBMINC_NIFTI_SUPPORT stays off, and the build proceeds without NIfTI at all -- the user gets neither the library they asked for nor any indication of it. That silent fallback is what issue BIC-MNI#102 reported. Verified both ways: with nifti_clib installed, configure, build and 37 tests are unchanged; with the package hidden, configure now stops at Could not find a package configuration file provided by "NIFTI" Closes BIC-MNI#102. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
|
Added
Verified both directions against an installed
The other half of #102 — that #117 is covered by the earlier commits in this PR: |
Why
nifti_clibupstream (NIFTI-Imaging) has not moved since v3.0.0 in July 2020. The ITK fork is the maintained line, so this pins that instead. It carries no tags, so the pin is a commit SHA (f24a607, 2026-07-22).The bump matters beyond freshness: the fork exports three symbols v3.0.0 did not —
nifti_image_write_status,nifti_image_write_bricks_status,nifti_set_fix_floats. The existingnifti_mangle.hleft those unprefixed, which is exactly the condition it exists to prevent: an unmangled symbol can collide at link time with theniftiioITK bundles. The mangle list is regenerated against the new pin.What
cmake-modules/BuildNIFTI.cmake— pinInsightSoftwareConsortium/nifti_clib@f24a607.cmake-modules/nifti_mangle.h— regenerated. All 112 exported symbols are nowminc_*;nifti_extension_sizeis dropped since it isstaticupstream now. The regeneration recipe in the header comment is also fixed: it previously contained ased 's/.*/…/'whose*/closed the C comment early.NIFTI::niftiio/NIFTI::znzimported targets —build_nifti()now defines the same targetsnifti_clib's ownNIFTIConfig.cmakeprovides, so consumers link identically whether NIFTI came from the ExternalProject or fromfind_package(NIFTI CONFIG). The real config package cannot be used there: ExternalProject builds at build time, long after consumers configure. Build ordering still rides on the existingadd_dependencies(minc2 NIFTI).LIBMINC_USE_SYSTEM_NIFTInow usesfind_package(NIFTI CONFIG), and the hand-rolledcmake-modules/FindNIFTI.cmakeis deleted. It hardcoded/usr/local/bicand reported everything it found as "NetCDF".nifti/— a tracked copy ofnifti1_io.c/znzlib.c/nifti1.h(~380 KB) that no build system referenced.Verification
libmincbuilds end-to-end against the new pin; all 38 ctests pass, includingnifti_readbackand thenifti_mangle_symbolsguard.nm -g --defined-only libniftiio.a libznz.aon the mangled build: 112 symbols, allminc_-prefixed, zero overlap with the unmangled build of the same source.Ordering
BIC-MNI/minc-toolkit-v2carries an identical pin and readsnifti_mangle.hout of this submodule, so its companion PR and this one should land together — a new pin with the old mangle list leaves those three symbols exposed.🤖 Generated with Claude Code
https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa