Skip to content

NIfTI: track the maintained nifti_clib fork, modernise the CMake plumbing - #230

Open
gdevenyi wants to merge 6 commits into
BIC-MNI:develop-1.9.18from
gdevenyi:nifti-modernize
Open

NIfTI: track the maintained nifti_clib fork, modernise the CMake plumbing#230
gdevenyi wants to merge 6 commits into
BIC-MNI:develop-1.9.18from
gdevenyi:nifti-modernize

Conversation

@gdevenyi

@gdevenyi gdevenyi commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Why

nifti_clib upstream (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 existing nifti_mangle.h left those unprefixed, which is the exact condition it exists to prevent: an unmangled symbol can collide at link time with the niftiio ITK bundles (ITK 4.x here has no ITK_USE_SYSTEM_NIFTI switch).

What

cmake-modules/BuildNIFTI.cmake

  • Pin InsightSoftwareConsortium/nifti_clib@f24a607, in step with the identical pin in the libminc submodule.
  • Define the NIFTI::niftiio / NIFTI::znz imported targets that nifti_clib's own NIFTIConfig.cmake provides, so the ADD_SUBDIRECTORY()'d consumers (libminc, minctools, Display) link the same way whether NIFTI came from here or from find_package(NIFTI CONFIG). The real config package cannot be used here — ExternalProject builds at build time, long after those consumers configure — so this mirrors it. Build ordering still rides on the existing add_dependencies(... NIFTI) calls, not on the imported targets.

USE_SYSTEM_NIFTI is now a real option(). It had been commented out for years, leaving FIND_PACKAGE(NIFTI REQUIRED) unreachable; it is restored as a proper option so it shows up in ccmake alongside the other USE_SYSTEM_* switches. It selects find_package(NIFTI CONFIG REQUIRED) over build_nifti(), and needs no consumer changes — both paths supply the same two imported targets. LIBMINC_USE_SYSTEM_NIFTI is forwarded so libminc can skip its mangling invariant, and NIFTI drops out of MINC2_deps in system mode since there is no ExternalProject to wait for.

The mangling caveat is documented at the call site rather than treated as a reason to drop the option: the bundled build is renamed to minc_* so it cannot collide with ITK's bundled ITKniftiio, whereas a system nifti is unmangled. That is fine against the shared libniftiio.so distributions package — which is what the release .deb/.rpm link — but not against a static system nifti with MT_BUILD_ITK_TOOLS on.

cmake-modules/FindNIFTI.cmake — deleted. nifti_clib ships a real config package, and the hand-rolled module hardcoded /usr/local/bic and reported everything it found as "NetCDF headers"/"NetCDF library".

Verification

Against a genuine installed system nifti_clib (shared, unmangled, exporting NIFTI::niftiio):

  • USE_SYSTEM_NIFTI=ON — configures and generates; mnc2nii/nii2mnc link /usr/lib/libniftiio.so.2.1.0 and /usr/lib/libznz.so.3.0.0; no NIFTI ExternalProject is generated; the exported LIBMINCConfig records the system library.
  • USE_SYSTEM_NIFTI=OFF — unchanged; consumers link the staged libniftiio.a/libznz.a and pick up the nifti include directory as -isystem.
  • Display, with GIFTI off, references nifti in neither mode.
  • Standalone libminc builds and tests in both modes: 38 tests bundled, 37 system, the difference being the correctly-skipped mangling invariant.
  • The mangled bundled build exports 112 symbols, all minc_-prefixed, with zero overlap against an unmangled build of the same source.

Ordering

Companion PRs, all against develop-1.9.18:

Submodule pin bumps are deliberately not included here.

Not in scope

🤖 Generated with Claude Code

https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa

Copilot AI review requested due to automatic review settings August 1, 2026 02:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the superbuild to always build NIfTI from a pinned, maintained nifti_clib fork (ITK/InsightSoftwareConsortium) and removes the dead “system NIFTI” path and legacy FindNIFTI.cmake, ensuring symbol-mangling avoids collisions with ITK’s bundled niftiio.

Changes:

  • Remove the unreachable USE_SYSTEM_NIFTI branch and always build NIfTI from BuildNIFTI.cmake.
  • Pin InsightSoftwareConsortium/nifti_clib to a specific commit SHA and add NIFTI::niftiio / NIFTI::znz imported targets mirroring NIFTIConfig.cmake.
  • Delete the legacy cmake-modules/FindNIFTI.cmake.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
CMakeLists.txt Removes the dead system-NIFTI branch and makes NIFTI a consistent superbuild dependency.
cmake-modules/BuildNIFTI.cmake Pins the maintained fork and adds imported targets for consistent consumer linking.
cmake-modules/FindNIFTI.cmake Removes obsolete find-module for the dropped system-NIFTI path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread cmake-modules/BuildNIFTI.cmake Outdated
# the ADD_SUBDIRECTORY()'d consumers are configured.
# Build ordering is not carried by these targets -- keep the explicit
# add_dependencies(<target> NIFTI) calls at the call sites.
file(MAKE_DIRECTORY ${NIFTI_INCLUDE_DIR}) # imported INTERFACE include dirs must exist at configure time

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the review commit.

The quoting is a real issue — unquoted, a prefix containing spaces splits into several arguments and creates the wrong directories. Now:

FILE(MAKE_DIRECTORY "${NIFTI_INCLUDE_DIR}" "${ZNZ_INCLUDE_DIR}")

On the second half: ZNZ_INCLUDE_DIR is set to the same string as NIFTI_INCLUDE_DIR a few lines up (${staging_prefix}/${install_prefix}/include/nifti), so it was already being created — but relying on that is fragile, so both are now named explicitly.

Comment thread cmake-modules/BuildNIFTI.cmake Outdated
Comment on lines +145 to +148
set_target_properties(NIFTI::znz PROPERTIES
IMPORTED_LOCATION "${ZNZ_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${ZNZ_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${ZLIB_LIBRARY}")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, fixed in the review commit — and the pointer to BuildZLIB.cmake was the useful part.

NIFTI::znz now links ZLIB::ZLIB. Checked it exists on both paths before build_nifti() runs: USE_SYSTEM_ZLIB=ON goes through FIND_PACKAGE(ZLIB REQUIRED), and OFF goes through build_zlib(), which creates it as a GLOBAL imported target at BuildZLIB.cmake:113. build_zlib() is called well before build_nifti(), so ordering is fine.

Following that file the rest of the way, I also added ADD_DEPENDENCIES(NIFTI::niftiio NIFTI) / ADD_DEPENDENCIES(NIFTI::znz NIFTI), mirroring its ADD_DEPENDENCIES(ZLIB::ZLIB ZLIB). Build ordering now rides on the imported target itself rather than only on the add_dependencies calls at each call site.

@gdevenyi gdevenyi changed the title NIfTI: track the maintained nifti_clib fork, drop the dead system-nifti path NIfTI: track the maintained nifti_clib fork, modernise the CMake plumbing Aug 1, 2026
gdevenyi added a commit to gdevenyi/minc-toolkit-v2 that referenced this pull request Aug 1, 2026
The note said USE_SYSTEM_NIFTI has no option() entry and must be passed on
the command line. BIC-MNI#230 makes it a real option(), so
that stops being true, and the table row above it stops needing the
explanation.

What is worth documenting is the trade-off. The bundled NIfTI has every
exported symbol renamed to minc_* so it cannot collide with the unmangled
niftiio ITK bundles; a system NIfTI is whatever the distribution shipped.
That is fine against the shared libniftiio.so distributions package, which
is what the release .deb and .rpm link, but pairing USE_SYSTEM_NIFTI with
a static system NIfTI and MT_BUILD_ITK_TOOLS collides at link time.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
gdevenyi added a commit to gdevenyi/minc-toolkit-v2 that referenced this pull request Aug 2, 2026
The note said USE_SYSTEM_NIFTI has no option() entry and must be passed on
the command line. BIC-MNI#230 makes it a real option(), so
that stops being true, and the table row above it stops needing the
explanation.

What is worth documenting is the trade-off. The bundled NIfTI has every
exported symbol renamed to minc_* so it cannot collide with the unmangled
niftiio ITK bundles; a system NIfTI is whatever the distribution shipped.
That is fine against the shared libniftiio.so distributions package, which
is what the release .deb and .rpm link, but pairing USE_SYSTEM_NIFTI with
a static system NIfTI and MT_BUILD_ITK_TOOLS collides at link time.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
@gdevenyi

gdevenyi commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto develop-1.9.18 at 342c896 (picks up #222 arguments→cxxopts, #223, #228) and addressed both review comments in a third commit.

  • NIFTI::znz links ZLIB::ZLIB instead of the raw ${ZLIB_LIBRARY} path.
  • NIFTI::niftiio no longer hardcodes m in its interface; it is added under if(UNIX), matching nifti_clib's own NIFTI_SYSTEM_MATH_LIB (empty on WIN32).
  • FILE(MAKE_DIRECTORY) is quoted and names both include dirs explicitly.
  • Following BuildZLIB.cmake the rest of the way, the imported targets now ADD_DEPENDENCIES(... NIFTI), so build ordering rides on the target rather than only on the call sites.

Re-verified on the new base, both modes, against an installed system nifti_clib:

configure nii2mnc links
USE_SYSTEM_NIFTI=OFF clean staged libniftiio.a + libznz.a
USE_SYSTEM_NIFTI=ON clean /usr/lib/libniftiio.so.2.1.0 + /usr/lib/libznz.so.3.0.0

One thing worth knowing rather than fixing: USE_SYSTEM_NIFTI=ON with USE_SYSTEM_ZLIB=OFF puts both /usr/lib/libz.so and the staged libz.a on the link line, because the system NIFTITargets.cmake bakes in the absolute zlib path it was built against. That is inherent to mixing a system nifti with a bundled zlib and predates this PR; pair the two USE_SYSTEM_* switches to avoid it.

gdevenyi added a commit to gdevenyi/minc-toolkit-v2 that referenced this pull request Aug 2, 2026
The note I added claimed a system NIfTI was fine against the shared
libniftiio.so distributions ship, and that the hazard was a link-time clash
on nifti_image_read with a static one. Both halves were wrong.

ITK renames its bundled niftiio but not its znzlib, so the clash is on
znzopen and seven siblings, not nifti_image_read. And shared is precisely
the case that breaks: ITK's copies interpose over libznz.so at runtime, so
nothing fails at link time and reads simply start returning nothing.

BIC-MNI#230 now refuses the combination outright, so
describe that rather than a caveat the reader has to police.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
@gdevenyi

gdevenyi commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Added a fourth commit (e7cbe31) in response to @vfonov on BIC-MNI/libminc#152, who pointed out that a system NIfTI collides with ITK's. It does, and I had the mechanism wrong in this PR's earlier description.

What I got wrong. I claimed the clash was on nifti_image_read with a static system nifti, and that the shared libniftiio.so distributions ship was safe. Both halves are false:

  • ITK 4.x mangles its bundled niftiio (itk_nifti_mangle.h, 103 names), so nifti_image_read is not where they meet.
  • ITK ships no mangle for znzlib, so ITKznz exports Xznzclose, znzopen, znzputs, znzread, znzrewind, znzseek, znztell, znzwrite unprefixed — 8 of the system library's 110 exported symbols.
  • Shared is exactly the case that breaks. ITK's copies are pulled into the executable, land in its dynamic symbol table, and interpose over libznz.so; the system libniftiio.so then reaches ITK's znz layer for every read.

Nothing fails at link time. Reproduced against the installed system nifti_clib with a stand-in for ITKznz: nifti_image_read returns NULL on a valid file, while the same test with the nifti side mangled returns the image. Full transcript in the libminc thread.

The change. USE_SYSTEM_NIFTI=ON with MT_BUILD_ITK_TOOLS=ON is now a hard configure error explaining why and offering both ways out. A prebuilt library cannot be renamed and ITK 4.x has no ITK_USE_SYSTEM_NIFTI, so there is no third option worth building.

USE_SYSTEM_NIFTI MT_BUILD_ITK_TOOLS configure
OFF ON ok (default; what CI and release use)
OFF OFF ok
ON OFF ok
ON ON rejected

MT_BUILD_LITE=ON forces the ITK tools off, so it pairs with USE_SYSTEM_NIFTI too — verified.

This keeps the option where a distribution packager would actually want it while making the broken combination unreachable. CI and the release workflow both build MT_BUILD_ITK_TOOLS=ON, so neither can trip over it.

#227 carries the matching README correction — the caveat I wrote there described the wrong symbols and the wrong linkage mode.

gdevenyi added a commit to gdevenyi/minc-toolkit-v2 that referenced this pull request Aug 2, 2026
The note I added claimed a system NIfTI was fine against the shared
libniftiio.so distributions ship, and that the hazard was a link-time clash
on nifti_image_read with a static one. Both halves were wrong.

ITK renames its bundled niftiio but not its znzlib, so the clash is on
znzopen and seven siblings, not nifti_image_read. And shared is precisely
the case that breaks: ITK's copies interpose over libznz.so at runtime, so
nothing fails at link time and reads simply start returning nothing.

BIC-MNI#230 now refuses the combination outright, so
describe that rather than a caveat the reader has to police.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
@gdevenyi

gdevenyi commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Correcting my own evidence above (commit 2572521).

The claim that the system libniftiio.so "returns NULL on valid files" came from a stand-in for ITKznz whose znzopen returned NULL — my stub, not ITK's code. Rebuilt against ITK release-4.14's actual znzlib.c/znzlib.h:

ITKznz from unmangled exports znz in exe .dynsym system nifti_image_read
real ITK release-4.14 15 8 an image
+ itk_znzlib_mangle.h 0 0 an image

The interposition is real and measurable; the breakage is not, on LP64 with both sides built HAVE_ZLIB. What remains, and what the guard is now justified on:

  • one definition of each function silently replacing another (UB, invisible);
  • a genuine ABI mismatch on ILP32 + large-file support — ITK's znzseek/znztell use long (4 bytes) against nifti_clib 3.x's znz_off_t (8), measured under -m32 -D_FILE_OFFSET_BITS=64;
  • sizeof(struct znzptr) differing between builds that disagree on HAVE_ZLIB.

The guard and the behaviour are unchanged — only the comment, the error message and the README note in #227, which now say this instead of the overstated version.

@gdevenyi

gdevenyi commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

The upstream fix landed, so the restriction is gone. Added in 20dee19.

InsightSoftwareConsortium/ITK#6756 merged to release-4.14 on 2026-08-05 (CI green on Linux, Windows and macOS), giving ITK's bundled znzlib the itk_* prefixing its niftiio has had since 2017. That was the single thing making a system NIfTI unusable alongside the ITK tools.

Pin moves cae3eb956073b968. Those are the only two commits in between — the fix and its merge — so the bump carries nothing else.

The FATAL_ERROR is removed. With both halves of ITK's copy prefixed, USE_SYSTEM_NIFTI and MT_BUILD_ITK_TOOLS no longer collide.

Verified against the newly pinned tarball:

old pin cae3eb95 new pin 6073b968
ITKznz unmangled exports 15 0
ITKznz itk_* exports 0 15
znz names in .dynsym of a binary also using system nifti_clib 8 0

and all four permutations configure clean, including the one that was rejected a commit ago:

USE_SYSTEM_NIFTI MT_BUILD_ITK_TOOLS configure
ON ON ok (was rejected)
OFF ON ok
ON OFF ok
OFF OFF ok

Our own NIfTI stays renamed to minc_* regardless — ITK still has no ITK_USE_SYSTEM_NIFTI, so those remain two separate copies and the mangling is what keeps them apart.

This closes out the packaging goal that started the thread: a release build can now use a distribution NIfTI and ship the ITK tools. #231, the local PATCH_COMMAND fallback, is closed as superseded. #227 carries the matching README correction.

@vfonov

vfonov commented Aug 6, 2026

Copy link
Copy Markdown
Member

why do we still need to mangle , if ITK accepts system NIFTI?

@gdevenyi

gdevenyi commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

why do we still need to mangle , if ITK accepts system NIFTI?

For the same reason ITK mangles, we're vendoring our own copy of NIFTI as part of the superbuild, so we should mangle symbols so we don't mess with the system copies if they exist.

gdevenyi and others added 6 commits August 6, 2026 13:35
…ti path

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. Keep it in step with the identical pin
in libminc/cmake-modules/BuildNIFTI.cmake, and regenerate nifti_mangle.h
whenever either moves -- the fork exports symbols v3.0.0 did not, and an
unmangled symbol is one that can collide with ITK's bundled niftiio.

build_nifti() now also defines the NIFTI::niftiio / NIFTI::znz imported
targets that nifti_clib's own NIFTIConfig.cmake provides, so the
ADD_SUBDIRECTORY()'d consumers link the same way whether NIFTI came from
here or from find_package(NIFTI CONFIG). The config package itself cannot
be used here: ExternalProject builds at build time, long after those
consumers are configured. Build ordering still rides on the existing
add_dependencies(... NIFTI) calls.

The USE_SYSTEM_NIFTI branch is removed rather than modernised. Its
option() has been commented out for years, so FIND_PACKAGE(NIFTI REQUIRED)
was unreachable, and it cannot come back: our copy is symbol-mangled to
minc_* precisely so it cannot collide with the unmangled niftiio ITK
bundles, and a system nifti would reintroduce that collision. Its
FindNIFTI.cmake is deleted with it -- it hardcoded /usr/local/bic and
reported everything it found as "NetCDF".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
Removing it went too far. The option was unreachable because its option()
line was commented out, but the capability is wanted: the release packages
are built against system libraries so that the .deb and .rpm depend on
distribution packages, and NIfTI should be no different.

It is now a real option() rather than a command-line-only variable, so it
shows up in ccmake alongside the other USE_SYSTEM_* switches, and it
selects find_package(NIFTI CONFIG REQUIRED) over build_nifti(). No
consumer changes were needed: both paths define NIFTI::niftiio and
NIFTI::znz, which is all libminc, minctools and Display ask for.

The mangling caveat is now documented at the call site rather than used as
a reason to drop the option. The bundled build renames its symbols to
minc_* so it cannot collide with ITK's bundled ITKniftiio; a system nifti
is unmangled, which is fine against the shared libniftiio.so distributions
package, but not against a static one when ITK tools are enabled.

LIBMINC_USE_SYSTEM_NIFTI is forwarded so libminc can skip its mangling
invariant test, and NIFTI is dropped from MINC2_deps in system mode since
there is no ExternalProject to wait for.

Verified against an installed system nifti_clib (shared, unmangled):
USE_SYSTEM_NIFTI=ON configures and links mnc2nii/nii2mnc against
/usr/lib/libniftiio.so, builds no NIFTI ExternalProject, and records the
system library in the exported LIBMINCConfig. USE_SYSTEM_NIFTI=OFF is
unchanged. Standalone libminc passes 38 tests bundled and 37 system, the
difference being the correctly-skipped mangling test.

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,
   which BuildZLIB.cmake already creates for exactly this purpose, 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);
   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 what BuildZLIB.cmake does for ZLIB::ZLIB, so build ordering rides
on the target itself rather than only on the add_dependencies calls at the
call sites.

Verified both ways against an installed system nifti_clib: USE_SYSTEM_NIFTI
ON and OFF both configure and generate, and nii2mnc links the expected
libraries in each.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
vfonov flagged that a system NIfTI collides with ITK's. It does, and the
failure is quieter than a link error.

ITK mangles its bundled niftiio -- itk_nifti_mangle.h, included from its
nifti1.h, covering 103 names -- but ships no equivalent for znzlib, so
ITKznz exports Xznzclose, znzopen, znzputs, znzread, znzrewind, znzseek,
znztell and znzwrite unprefixed. A distribution nifti is unmangled too, so
those eight clash in every binary that links both ITK and libminc: c3d,
elastix, ANTs, the EZminc tools.

Nothing fails at link time. ITK's copies are pulled into the executable and
land in its dynamic symbol table, where they interpose over libznz.so, so
the system libniftiio.so reaches ITK's znz layer for every read. Reproduced
against the installed system nifti_clib plus a stand-in for ITKznz:
nifti_image_read returns NULL for a file it reads correctly when the same
ITKznz is present but the nifti side is mangled.

So the earlier claim that a shared system nifti was safe was wrong; shared
is exactly the case that breaks, and it breaks silently. Since a prebuilt
library cannot be renamed and ITK 4.x has no ITK_USE_SYSTEM_NIFTI to point
it at one copy, refuse the combination instead of documenting it.

USE_SYSTEM_NIFTI remains available for builds without the ITK tools, which
is where a distribution package would want it: MT_BUILD_ITK_TOOLS=OFF or
MT_BUILD_LITE=ON. Verified all four combinations -- only the unsafe one is
rejected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
My earlier justification overstated the consequence. It said the system
libniftiio.so ends up returning NULL for valid files; that came from a
stand-in for ITKznz which returned NULL, not from ITK's own code.

Rebuilt against ITK release-4.14's actual znzlib sources: the interposition
is real and measurable -- 8 znz names land in the executable's dynamic
symbol table -- but ITK's implementation and nifti_clib's agree closely
enough on LP64 that reads still succeed. Both are built with HAVE_ZLIB, so
sizeof(struct znzptr) matches too.

What remains is still worth refusing: it is one definition of each function
silently replacing another, it breaks on ILP32 with large-file support
(ITK's znzseek/znztell use long, nifti_clib 3.x uses znz_off_t -- 4 bytes
against 8), and it breaks wherever HAVE_ZLIB differs between the two builds.
So the guard stays, with an accurate rationale rather than an alarming one.

Also note where the real fix belongs: ITK's znzlib needs the mangle header
its niftiio already has, after which this guard can be dropped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
InsightSoftwareConsortium/ITK#6756 is merged, so ITK's bundled znzlib now
carries the itk_* prefixing its niftiio has had since 2017. That was the one
thing making a system NIfTI unusable alongside the ITK tools: ITKznz exported
plain znzopen/znzread/znzseek/..., which interpose over any other znzlib in
the same binary, and every tool linking both ITK and libminc has one.

Pin moves cae3eb95 -> 6073b968. Those are the only two commits in between --
the fix and its merge -- so this bump carries nothing else.

With both halves of ITK's copy prefixed, USE_SYSTEM_NIFTI and
MT_BUILD_ITK_TOOLS no longer collide, and the FATAL_ERROR refusing the
combination goes away.

Verified against the new tarball: ITK's znzlib.c builds to 15 itk_* exports
and no unmangled ones, and a program linking it next to a shared system
nifti_clib exports no znz names at all in its dynamic symbol table, where the
old pin put 8. All four USE_SYSTEM_NIFTI x MT_BUILD_ITK_TOOLS combinations
now configure, including the one that was previously rejected.

Our own NIfTI stays renamed to minc_* regardless; ITK still has no
ITK_USE_SYSTEM_NIFTI, so the two builds remain separate copies.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019katBUnczdvUS7WQ4RpVoa
@gdevenyi

gdevenyi commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto develop-1.9.18 at a87c3c1, picking up #226 (shared libs on by default), #227 (README rewrite) and #229 (packaging-tree removal). Six commits replayed with no conflicts.

The README commits this branch depended on landed via #227, so nothing README-related remains here.

Re-verified on the new base, which matters because MT_BUILD_SHARED_LIBS now defaults to ON:

USE_SYSTEM_NIFTI MT_BUILD_ITK_TOOLS configure
ON ON ok
OFF ON ok
ON OFF ok
OFF OFF ok

On the macOS CI failures: they are pre-existing and not from this branch. develop-1.9.18 itself fails both macOS jobs, at the old base 342c896 and the current a87c3c1, on the same test with a byte-identical value:

relative RMS difference: 0.000850981867212377 larger then 0.0001
The following tests FAILED:
	146 - nu_reference_1 (Failed)
99% tests passed, 1 tests failed out of 150

That is the N3 nu_estimate numerical reference comparison, unrelated to NIfTI. The NIfTI tests on those same runs pass — mnc2nii 7 tests, nii2mnc 13. Linux is green on this branch.

(Unrelated, but noticed while checking: the ubuntu (full) failure on the base run is a GitHub infrastructure blip — Service Unavailable / Failed to resolve action download info — not a build failure.)

@gdevenyi

gdevenyi commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@vfonov ITK does not accept a system NIfTI — there is no ITK_USE_SYSTEM_NIFTI, on release-4.14 or on main. Compare the two third-party modules:

Modules/ThirdParty/ZLIB/CMakeLists.txt — system-capable:

option(ITK_USE_SYSTEM_ZLIB "Use system-installed zlib" ${ITK_USE_SYSTEM_LIBRARIES})
if(ITK_USE_SYSTEM_ZLIB)
  find_package(ZLIB REQUIRED)
  set(ITKZLIB_LIBRARIES "ZLIB::ZLIB")

Modules/ThirdParty/NIFTI/CMakeLists.txt — no option, no find_package, always its own copy:

set(ITKNIFTI_LIBRARIES ITKznz ITKniftiio)
itk_module_impl()

So there are always two NIfTI copies in any binary linking both ITK and libminc.

That said, your instinct is right that something changed. Since InsightSoftwareConsortium/ITK#6756 the pinned ITK prefixes both halves of its copy — niftiio to itk_* since 2017, and now znzlib too. Against that ITK, our minc_* mangling is redundant: itk_nifti_image_read and nifti_image_read cannot collide whatever we do.

Two reasons I would keep it anyway, though I am happy to be overruled:

  1. It only holds for the 4.14 line. main and v5.4.x have no nifti mangling at all — itk_nifti_mangle.h was deleted in 0cdb1dd5 (2018) when the vendored nifti was refreshed and NIFTI_PACKAGE_PREFIX was adopted, which renames CMake targets rather than C symbols. Any move off 4.14 reinstates the collision, and it reappears silently.
  2. ITK is not the only way a second copy arrives. Anything else in the process bringing its own libniftiio — including a system one pulled in by another library — meets our copy directly.

Dropping it while pinned to 4.14 is a defensible call; it would just want a comment saying it depends on the ITK pin carrying #6756, so an ITK upgrade re-examines it. Say the word and I will strip the mangling, the patch step and the two tests out of BIC-MNI/libminc#152.

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.

3 participants