Name: quit Version: 3.4 Release: 8%{?dist} Summary: Quantitative Imaging Tools for MRI data processing License: MPL-2.0 URL: https://github.com/spinicist/QUIT Source0: %{url}/archive/v%{version}/QUIT-%{version}.tar.gz # Header-only argument parser library (not packaged in Fedora) Source1: https://github.com/Taywee/args/archive/refs/tags/6.4.8.tar.gz#/args-6.4.8.tar.gz # CMake find module for bundled args library Source2: Findargs.cmake BuildRequires: cmake >= 3.12 BuildRequires: gcc-c++ BuildRequires: make BuildRequires: InsightToolkit5-devel >= 5.3.0 BuildRequires: eigen3-devel BuildRequires: ceres-solver-devel BuildRequires: suitesparse-devel BuildRequires: blas-devel BuildRequires: lapack-devel # fmt v9 is bundled because QUIT relies on implicit ostream formatting removed in fmt v11 Source3: https://github.com/fmtlib/fmt/archive/refs/tags/9.1.0.tar.gz#/fmt-9.1.0.tar.gz BuildRequires: json-devel # Pin the ITK this was BUILT against. ITK freezes its sonames across releases # while ADDING symbols: libITKFFT-5.4.so.1 is identical in name in 5.4.7-2 and # 5.4.7-8, but -8 enabled FFTW and exports # _ZN3itk46FFTWFFTImageFilterInitFactoryRegister__PrivateEv. rpm derives # dependencies from sonames, so it cannot tell them apart -- quit installs cleanly # over the older ITK and then exits 127 on EVERY invocation. Measured on a real # host by the framework session, then confirmed per object here: quit's single shipped ELF # object needs that symbol (1/1). # # A floor, not an equality: the breakage is additive, so a newer ITK is fine. # # The two-part %%{?itk_ver}%%{!?itk_ver:UNSET} expansion is required, not # stylistic. ">= UNSET" is SATISFIED -- rpm ranks a numeric segment above an # alphabetic one -- so an undefined macro makes this floor vacuous, which is what # the %%build guard catches. It must also be this shape rather than a parse-time # %%global check: a parse-time guard once killed every SCM build before a chroot # existed (python-simpleitk 10980296, "can't parse specfile", 0 minutes). Requires: InsightToolkit5%{?_isa} >= %{?itk_ver}%{!?itk_ver:UNSET} Provides: bundled(fmt) = 9.1.0 Provides: bundled(args) = 6.4.8 Provides: bundled(NumericalIntegration) %description QUIT (QUantitative Imaging Tools) is a collection of programs for processing quantitative MRI data, including DESPOT, relaxometry, magnetization transfer, perfusion, susceptibility mapping, and more. All tools are accessed via the single 'qi' command with subcommands, similar to git or bart. QUIT processes NIfTI format neuroimaging files and accepts sequence parameters via JSON input. %prep %autosetup -n QUIT-%{version} # Install bundled args header tar xf %{SOURCE1} install -p -m 0644 args-6.4.8/args.hxx External/include/ # Install cmake find module for args install -p -m 0644 %{SOURCE2} cmake/Findargs.cmake # Bundle fmt 9.1.0 (QUIT relies on implicit ostream formatting removed in fmt v11) tar xf %{SOURCE3} mv fmt-9.1.0 External/fmt # Fix ambiguous format_to with Eigen expression templates in C++20 # Replace the custom formatter with a simple ostream-based one sed -i '/template /,/};/c\ template auto format(const QI::SSFPSequence \&s, FormatContext \&ctx) const {\ std::ostringstream oss;\ oss << "SSFP:\\n\\tTR: " << s.TR << "\\n\\tFA: " << (s.FA * 180. / M_PI).transpose() << "\\n\\tPhaseInc: " << (s.PhaseInc * 180. / M_PI).transpose();\ return fmt::format_to(ctx.out(), "{}", oss.str());\ }\ };' Source/Sequences/SSFPSequence.h sed -i '1i #include ' Source/Sequences/SSFPSequence.h %build test -n "%{?itk_ver}" || { echo "ERROR: itk_ver undefined - add 'BuildRequires: InsightToolkit5-devel'" >&2; exit 1; } # Suppress GCC 15 -Wtemplate-body diagnostics in QUIT's own template code export CFLAGS="%{optflags} -std=gnu17" export CXXFLAGS="%{optflags} -Wno-template-body -include cstdint -fpermissive" # Build bundled fmt 9.1.0 first (plain CXXFLAGS — fmt must NOT force-include Eigen) cmake -S External/fmt -B External/fmt/build \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=%{_builddir}/QUIT-%{version}/External/fmt/install \ -DFMT_DOC=OFF -DFMT_TEST=OFF %make_build -C External/fmt/build %make_build -C External/fmt/build install # QUIT compiles against BOTH the system Eigen (/usr/include/eigen3, 3.4) for its # own code and ITK 5.4's bundled itkeigen (3.3.x) via ITK headers. The two Eigen # copies share include-guard macros (EIGEN_MACROS_H etc.), so when a TU pulls ITK # (itkeigen) before system Eigen, itkeigen 3.3.x's Quaternion.h gets compiled and # its EIGEN_NOEXCEPT_IF(std::is_nothrow_move_*::value) is rejected by # GCC 16 + -std=c++20. Force-include system Eigen first so its guards win and # itkeigen's headers are guard-skipped (Eigen then resolves to the correct 3.4 # headers). Absolute path + added only for QUIT's own build (not fmt). Replaces # the old _itk_eigen_patch shadow, which never overrode ITK's relative includes. export CXXFLAGS="$CXXFLAGS -include /usr/include/eigen3/Eigen/Dense" %cmake \ -DCMAKE_MODULE_PATH:PATH="%{_builddir}/QUIT-%{version}/cmake" \ -DExternal_Include_DIR:PATH="%{_builddir}/QUIT-%{version}/External/include" \ -DBUILD_PARMESAN:BOOL=OFF \ -Dfmt_DIR:PATH="%{_builddir}/QUIT-%{version}/External/fmt/install/lib64/cmake/fmt" %cmake_build %install %cmake_install %files %license LICENSE.txt %doc README.md CHANGELOG.md %{_bindir}/qi %changelog * Fri Sep 25 2026 Morgan Hough - 8 - Replace the UNVERSIONED "Requires: InsightToolkit5%%{?_isa}" with a real floor plus %%build guard. An unversioned require is satisfied by any ITK, so it was equivalent to having none: quit installs over 5.4.7-2 and exits 127, since its one shipped ELF object needs the FFTW registration symbol that only -8 and later export. - Ownership: legion, assigned by Morgan 2026-09-25. Previously unowned. * Sat Sep 19 2026 Morgan Hough - 3.4-7 - Rebuild against InsightToolkit5 5.4.7-8, which unbundles Eigen3, VXL and double-conversion. No source change; the Release bump is what lets the rebuild supersede rather than becoming a second copy at a published NVR. - -7 rather than -6: the two arches had diverged. aarch64 published 3.4-5 while x86_64 published 3.4-6, and the spec still said -5. So -6 would have been fine on aarch64 and a same-NVR collision on x86_64. The bump has to clear the highest release published on ANY arch the wave builds, not just the one that happens to be checked. - Found by pre-flighting the wave on aarch64, which no scratch validation covered -- every test-project build was x86_64. * Wed Jun 18 2026 Morgan Hough - 3.4-5 - Actually fix the GCC16/C++20 FTBFS. Root cause: QUIT mixes system Eigen 3.4 (its own code) and ITK 5.4's bundled itkeigen 3.3.x (via ITK headers) in the same TUs; their shared include-guards let itkeigen 3.3.x Quaternion.h compile with EIGEN_NOEXCEPT_IF(...::value), rejected by GCC 16 + c++20. Force-include system Eigen first (-include /usr/include/eigen3/Eigen/Dense, QUIT build only), so itkeigen is guard-skipped. The old _itk_eigen_patch shadow + typename sed were no-ops (deployed Quaternion.h has no literal typename; the shadow path never overrode ITK's relative includes); removed. * Sun May 24 2026 Morgan Hough - 3.4-4 - Shadow-patch ITK 5.4's bundled itkeigen/Eigen/src/Geometry/Quaternion.h: copy it into _itk_eigen_patch/ in %build, strip the invalid `typename std::is_nothrow_move_*::value` typename keyword, prepend the patched dir to -I so it wins over /usr/include/ITK-5.4/itkeigen/. Add -fpermissive belt-and-suspenders. GCC 16 now enforces what was always invalid C++ (typename used on a non-type ::value member). * Sat May 23 2026 Morgan Hough - 3.4-2 - Add missing Source2 Findargs.cmake (was referenced but never committed, blocking SRPM rebuild). Simple header-only find module providing args::args INTERFACE target. * Mon Mar 30 2026 Morgan Hough - 3.4-1 - Initial package for Fedora