Skip to content

Export LIBMINC::nifti, and install the bundled NIfTI with libminc - #156

Draft
gdevenyi wants to merge 5 commits into
BIC-MNI:develop-1.9.18from
gdevenyi:libminc-nifti-target
Draft

gdevenyi wants to merge 5 commits into
BIC-MNI:develop-1.9.18from
gdevenyi:libminc-nifti-target

Conversation

@gdevenyi

@gdevenyi gdevenyi commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #152

This branch contains the four commits of #152. Review only the last commit, 2c4a8b43. After #152 merges, I will rebase this PR, and then the diff shows only that commit.

It also needs BIC-MNI/minc-toolkit-v2#249 before the toolkit takes it. This PR adds find_dependency(NIFTI) to LIBMINCConfig.cmake, and it is the first thing that loads the NIFTI package that the toolkit installs. On lib64 systems (Fedora), that package refers to archives that the toolkit does not install, so find_package(LIBMINC) failed. BIC-MNI/minc-toolkit-v2#249 fixes the toolkit. A throwaway CI branch with #152, #154, #155, this PR and that fix passes in all toolkit jobs (macOS fails only nu_reference_1, as on develop-1.9.18): https://github.com/gdevenyi/minc-toolkit-v2/actions/runs/35648847130

Why

BIC-MNI/minc-toolkit-v2#247, which causes BIC-MNI/minc-toolkit-v2#245 (falcon does not link). With NIfTI support, libminc2 contains nifti_clib, and each symbol has the minc_ prefix. Only the renamed headers in include/nifti apply the same rename, and no libminc target or variable gives that directory. A consumer that finds another nifti1_io.h compiles, and then the link fails with undefined nifti_* references.

What

  • New target LIBMINC::nifti. It gives include/nifti and links LIBMINC::minc2, not libniftiio.a. So a program that links shared libminc gets no second copy of the NIfTI code. LIBMINC::minc2 does not get include/nifti, so tools that link libminc and ITK still find the ITK headers. With a system nifti, the target links NIFTI::niftiio.

  • Standalone installs now include the bundled NIfTI. build_nifti() stages it under build/external, and until now only the superbuild installed its staging tree. So LIBMINC::nifti had no headers to point at, and every installed static minc2 failed in consumers:

    The link interface of target "LIBMINC::minc2" contains:
      NIFTI::niftiio
    but the target was not found.
    

    (develop-1.9.18 links the archive by its build-tree path, which works only while that build tree exists.) The staged tree (headers, libniftiio.a, libznz.a, the NIFTI package) is now installed next to libminc.

  • LIBMINCConfig.cmake finds NIFTI::niftiio from that package, or from the system package that libminc was built with. It skips this when the targets exist already, as in the superbuild.

  • nifti_readback links LIBMINC::nifti, so the tests use the target the same way a consumer does.

Verification

Build ctest Installed package, used by a separate CMake project
This branch, standalone, static 59/59 LIBMINC::minc2 and LIBMINC::nifti link (the static case failed before)
This branch, standalone, shared 59/59 Both link. The NIfTI program defines no minc_ symbol, so the code comes from libminc2.so
Same commit plus #155 (rename test), system nifti_clib (unrenamed, shared) 58/58 (rename tests skipped) Both link. nifti_image_read comes from libniftiio.so
Same commit plus #155 (rename test), in the minc-toolkit-v2 develop-1.9.18 superbuild NIfTI tests 3/3 Both link. libminc2.so exports no unrenamed NIfTI symbol
  • A program that compiles against its own unrenamed nifti1_io.h still gets undefined reference to nifti_image_read. This is the falcon failure: the header is the problem, not the link.
  • cmake --install --prefix, DESTDIR and cpack -G TGZ all include the NIfTI tree. The superbuild installs each NIfTI file once (11 files, the same as before).

After this PR

  • falcon can delete its vendored nifti1*.h / znzlib.* and link LIBMINC::nifti.
  • minc-toolkit-v2 needs a libminc submodule bump.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RCrjXCD1KiTWLtah7wmySX

gdevenyi and others added 5 commits August 8, 2026 22:48
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
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
With NIfTI support, libminc2 carries nifti_clib with every symbol renamed
to minc_*. Only the renamed headers in include/nifti apply the same rename,
and nothing told a consumer where they are: LIBMINC::minc2 and
LIBMINC_INCLUDE_DIRS give only <prefix>/include. A consumer that picks up
another nifti1_io.h -- its own copy, a system one -- compiles, then fails
to link with undefined nifti_* references. That is falcon (#245).

LIBMINC::nifti is the target to link for calling NIfTI through libminc. It
gives include/nifti and links LIBMINC::minc2, not libniftiio.a, so a
program linking shared libminc gets no second copy of the code. The
headers stay off minc2, so a tool linking libminc and ITK still finds ITK's
own nifti headers. With a system nifti, which is neither renamed nor inside
libminc, the target links NIFTI::niftiio instead.

Standalone builds did not install the bundled NIfTI at all: build_nifti()
stages it under build/external, and only the minc-toolkit superbuild
copied its staging tree into the prefix. So there was nothing for
LIBMINC::nifti to point at, and an installed static minc2 was unusable:

  The link interface of target "LIBMINC::minc2" contains:
    NIFTI::niftiio
  but the target was not found.

develop-1.9.18 linked the archive by its absolute build-tree path, which
worked only while that build tree existed. The staged tree (headers,
libniftiio.a, libznz.a, the NIFTI package) is now installed next to
libminc, and LIBMINCConfig.cmake finds NIFTI::niftiio from that package, or
from the system package libminc was built with. It skips this when the
targets already exist, as in the superbuild, which defines them itself.

nifti_readback now links LIBMINC::nifti, so the tests use the target the
way a consumer does.

Verified on this branch, standalone, static and shared, NIfTI on: 59/59
tests pass. Each install is consumed by a separate project: LIBMINC::minc2
and LIBMINC::nifti configure, build and link. A program that calls
nifti_image_read through LIBMINC::nifti references minc_nifti_image_read
and no bare nifti_*/znz* name; against shared libminc it defines no minc_
symbol itself, so the code comes from libminc2.so. The static install,
which failed as above before this change, now links.

Fixes BIC-MNI/minc-toolkit-v2#247.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RCrjXCD1KiTWLtah7wmySX
gdevenyi added a commit to gdevenyi/falcon that referenced this pull request Sep 21, 2026
The libminc in minc-toolkit-v2 1.9.18 bundles nifti_clib 3.0. Its
nifti1_io.h renames each nifti_clib symbol to minc_* with macros, so
libminc2.so exports minc_nifti_image_read, not nifti_image_read.

falcon.h includes "nifti1_io.h" with quotes. The compiler looks for a
quoted include in the directory of the including file first, so it
always found the vendored src/nifti1_io.h. When LIBMINC_NIFTI_SUPPORT
was ON, CMake did not compile the vendored nifti1_io.c, and each falcon
program failed to link with undefined references to nifti_* symbols.

Changes:

- Move the vendored nifti1.h, nifti1_io.[ch] and znzlib.[ch] to
  src/3rd_party/nifti, so that no NIfTI header stays in src.
- If libminc exports LIBMINC::nifti (BIC-MNI/libminc#156), link it. The
  target gives the libminc NIfTI headers, and the NIfTI code comes from
  libminc2. Otherwise, compile the vendored copy. Its plain nifti_*
  names do not collide with the minc_* names in libminc.
- Remove nifti_io_get_problem_code_kunio() and its callers. Commit
  4bd7d56 replaced the NIfTI source that set this code, and since then
  nothing sets it. The stubs always returned 0, so the checks never
  found an error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CsPYwazurrav5LnkKyRq4H
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