%define debug_package %{nil} Name: TTK Version: 4.0.1 Release: 9%{?dist} Summary: Tensor ToolKit for diffusion MRI (INRIA) License: BSD-2-Clause URL: https://github.com/medInria/TTK Source0: https://github.com/medInria/TTK/archive/refs/tags/v%{version}/TTK-%{version}.tar.gz BuildRequires: cmake >= 3.24 BuildRequires: ninja-build BuildRequires: gcc-c++ BuildRequires: InsightToolkit5-devel >= 5.4.6 # TTK uses VTK's stable PolyData/Points/CellArray/SmartPointer/IdType # surface; that API hasn't changed 9.2 -> 9.5+. The historical # `vtk-devel < 9.3` pin was for medInria-chain Qt5 coexistence on F43, # which F44 no longer requires. BuildRequires: vtk-devel # java-devel was needed because old VTK 9.2.6 cmake targets referenced # JVM include paths transitively; with newer VTK it's not required, but # keeping it cheap-as-insurance. BuildRequires: java-devel # Pin the exact VTK ABI this package was built against. VTK freezes its soname at # libvtk*.so.1 across all of 9.x, so rpm cannot tell 9.5 from 9.6 from 9.7: a consumer # built against one installs cleanly against another and fails at LOAD with undefined # symbols. A >= floor does not help -- 9.7.0 satisfies >= 9.6.2 -- so this is an exact # match. %%vtk_abi comes from vtk-devel's macros.vtk, so it re-pins on every rebuild. # # The missing-macro check lives in %%build, deliberately, NOT at parse time. A # parse-time %%error cannot distinguish "COPR is generating the SRPM" from "this is a # real buildroot": rpkg srpm parses the spec with no buildroot and therefore no # vtk-devel, so a parse-time guard killed every SCM build of every guarded package # before any chroot existed (python-simpleitk 10980296 failed in 0 minutes with # "can't parse specfile"). Expanding to UNSET keeps the SRPM parse alive, and the # %%build test still fails loudly in a buildroot lacking vtk-devel, so no RPM carrying # the placeholder can ever be produced. Requires: vtk(abi) = %{?vtk_abi}%{!?vtk_abi:UNSET} # itk(abi): ITK_LEGACY_REMOVE changes class layouts at unchanged sonames, so the itk_ver floor alone let a # consumer built against the removed state install over the present one (python3-simpleitk -11 on ITK -23 # returned all-zero images). Equality on the legacy-state token; see InsightToolkit5.spec. Requires: itk(abi) = %{?itk_abi}%{!?itk_abi:UNSET} %description TTK (Tensor ToolKit) is a library for processing diffusion tensor images (DTI) built on ITK and VTK. It provides tensor estimation, scalar map computation, fiber tractography, and related algorithms. Developed at INRIA for use with medInria. Note: This is INRIA's Tensor ToolKit, not Kitware's Topology ToolKit. %package devel Summary: Development files for %{name} Requires: %{name}%{?_isa} = %{version}-%{release} Requires: InsightToolkit5-devel >= 5.4.5 Requires: vtk-devel %description devel Headers and cmake config files for TTK (Tensor ToolKit). %prep %autosetup -n TTK-%{version} # ITK_LEGACY_REMOVE made itk::Vector's scalar constructor EXPLICIT # (itkVector.h:110-119: implicit under legacy, `explicit` without it), so a # Vector can no longer be compared against a bare scalar: # # Commands/itkLogEuclideanDTIEstimatorCommand.cxx:178: # error: no match for 'operator!=' (operand types are 'GradientType' # {aka 'itk::Vector'} and 'double') # if( g!=0.0 ) # # GradientType(0.0) reproduces exactly what the implicit conversion produced -- # the explicit ctor fills every component with the scalar -- so the comparison # stays component-wise against the zero vector and the meaning is unchanged. # GetNorm() != 0.0 would also work but introduces a sqrt where there was none. # # ONE site, and the scoping matters. TTK has seven other `!= 0.0` / `== 0.0` # comparisons -- updatenorm, Q, sum, dist2, voxelCount -- and every one is a # genuine scalar that must not be touched. A pattern-based rewrite of # "x != 0.0" would corrupt all seven. The assertion below pins their count so # a future broadening of this block fails loudly. # # Verified with ci/prep-tdd.sh before building: RED on the unpatched tarball, # GREEN after, with the seven scalar comparisons asserted untouched in both. grep -qE 'if\([[:space:]]*g!=0\.0[[:space:]]*\)' Commands/itkLogEuclideanDTIEstimatorCommand.cxx \ || { echo "ERROR: the Vector!=scalar comparison is gone -- drop this block" >&2; exit 1; } sed -i 's|if( g!=0\.0 )|if( g != GradientType(0.0) )|' Commands/itkLogEuclideanDTIEstimatorCommand.cxx # SECOND site class, and the reason the first release of this block was not # enough: ITK_LEGACY_REMOVE makes the scalar constructor of itk::POINT explicit # too, not just itk::Vector. itkPoint.h:104-123 carries the same split and # says so outright -- "ITK_LEGACY_REMOVE=ON will disallow implicit conversion # from a single value". So copy-initialisation from a scalar stops compiling: # # Commands/itkWarpFibersCommand.cxx:174: # error: conversion from 'double' to non-scalar type # 'itk::ImageBase<3>::PointType' {aka 'itk::Point'} requested # VectorImageType::PointType pt_n = 0.0; # # Direct-initialisation `pt_n(0.0)` uses the explicit constructor and fills # every component, which is what the implicit form did. sed -i 's|VectorImageType::PointType pt_n = 0\.0;|VectorImageType::PointType pt_n(0.0);|' \ Commands/itkWarpFibersCommand.cxx sed -i 's|FloatImageType::PointType correctorigin = 0\.0;|FloatImageType::PointType correctorigin(0.0);|' \ Commands/itkImageConverterCommand.cxx # CLASS-level residual check, not one per site. The -7 release asserted only # the Vector comparison, went green in ci/prep-tdd.sh, and then failed on the # builder at the Point site -- red-green only proves what you assert, and that # assertion was too narrow. This one fails on ANY Point/Vector-typed variable # copy-initialised from a scalar, so a new site in a future tarball is caught # here rather than four minutes into a build. _ttk_scalar_init=$(grep -rnE '\b[A-Za-z_][A-Za-z0-9_:]*(PointType|VectorType|GradientType|OffsetType)[[:space:]]+[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*=[[:space:]]*-?[0-9]' \ --include=*.cxx --include=*.h --include=*.txx . | wc -l) test "$_ttk_scalar_init" -eq 0 \ || { echo "ERROR: Point/Vector still copy-initialised from a scalar:" >&2 grep -rnE '\b[A-Za-z_][A-Za-z0-9_:]*(PointType|VectorType|GradientType|OffsetType)[[:space:]]+[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*=[[:space:]]*-?[0-9]' \ --include=*.cxx --include=*.h --include=*.txx . >&2; exit 1; } grep -qE 'g[[:space:]]*!=[[:space:]]*GradientType\(0\.0\)' Commands/itkLogEuclideanDTIEstimatorCommand.cxx \ || { echo "ERROR: the GradientType(0.0) rewrite did not apply" >&2; exit 1; } _ttk_scalars=$(grep -rE '\b[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*(!=|==)[[:space:]]*0\.0[[:space:]]*\)' \ --include=*.cxx --include=*.h --include=*.txx . \ | grep -vc 'itkLogEuclideanDTIEstimatorCommand.cxx') test "$_ttk_scalars" -eq 7 \ || { echo "ERROR: expected 7 untouched scalar comparisons, found $_ttk_scalars" >&2; exit 1; } # Inject SOVERSION for shared libraries (upstream lacks versioning) # Upstream uses ADD_LIBRARY(${TARGET_NAME} ...) so append to each CMakeLists.txt for f in Common/CMakeLists.txt Algorithms/CMakeLists.txt IPF/CMakeLists.txt \ Commands/ttkCommands/CMakeLists.txt Commands/ttkConvertCommands/CMakeLists.txt \ Commands/ttkUtilCommands/CMakeLists.txt; do printf '\nset_target_properties(${TARGET_NAME} PROPERTIES VERSION %{version} SOVERSION 1)\n' >> "$f" done # Fix hardcoded lib/ install destinations to respect CMAKE_INSTALL_LIBDIR sed -i 's|LIBRARY DESTINATION lib$|LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}|' CMake/export_and_install_libs.cmake sed -i 's|ARCHIVE DESTINATION lib$|ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}|' CMake/export_and_install_libs.cmake # Fix cmake config install paths (only INSTALL_DESTINATION and DESTINATION lines, not build-tree paths) sed -i 's|INSTALL_DESTINATION lib/cmake/|INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/|' CMake/export_and_install_libs.cmake sed -i '/^SET(ConfigPackageLocation/s|lib/cmake/|${CMAKE_INSTALL_LIBDIR}/cmake/|' CMake/export_and_install_libs.cmake sed -i '/^ DESTINATION lib\/cmake/s|lib/cmake/|${CMAKE_INSTALL_LIBDIR}/cmake/|' CMake/export_and_install_libs.cmake # Fix top-level CMakeLists.txt install paths (only DESTINATION, not build-tree paths) sed -i 's|INSTALL_DESTINATION lib/cmake/|INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/|' CMakeLists.txt sed -i 's| DESTINATION lib/cmake/| DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/|g' CMakeLists.txt sed -i 's| DESTINATION lib/cmake/| DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/|g' CMakeLists.txt # --------------------------------------------------------- # FIX: VTK 9.2.6 GetNextCell takes const vtkIdType*& (not vtkIdType*&) # Fix all non-const vtkIdType pointer vars used with GetNextCell # --------------------------------------------------------- sed -i 's/vtkIdType\* ptids/const vtkIdType* ptids/' Algorithms/itkFiberBundleStatisticsCalculator.cxx sed -i 's/vtkIdType \*pts = 0/const vtkIdType *pts = 0/' Algorithms/itkVTKFibersToITKFibers.txx sed -i 's/vtkIdType npt, \*pto/vtkIdType npt; const vtkIdType *pto/' Commands/itkConsolidateFiberBundleCommand.cxx # --------------------------------------------------------- # FIX: ITK 5.4 NumericTraits::GetLength passes rvalue — need const ref # --------------------------------------------------------- sed -i 's/GetLength(Tensor<\([^>]*\)> &t)/GetLength(const Tensor<\1> \&t)/g' \ Common/itkNumericTraitsTensorPixel2.h Common/itkNumericTraitsTensorPixel2.cxx # --------------------------------------------------------- # FIX: ApplyMatrix ambiguity — vnl_matrix_fixed converts to both # itk::Matrix and vnl_matrix. Disambiguate with explicit cast. # Affects itkTensor.txx, itkMatrixOffsetTensorTransformBase.txx, # and itkResampleTensorImageFilter.txx. # --------------------------------------------------------- sed -i 's/tensor\.ApplyMatrix ( m_Rigid\.GetTranspose() )/tensor.ApplyMatrix ( vnl_matrix(m_Rigid.GetTranspose()) )/' \ Common/itkMatrixOffsetTensorTransformBase.txx sed -i 's/tensor\.ApplyMatrix ( m_Rigid\.GetVnlMatrix() )/tensor.ApplyMatrix ( vnl_matrix(m_Rigid.GetVnlMatrix()) )/' \ Common/itkMatrixOffsetTensorTransformBase.txx # Internal delegation in ApplyMatrix(const MatrixType&) — R.GetVnlMatrix() returns vnl_matrix_fixed sed -i 's/this->ApplyMatrix (R\.GetVnlMatrix() )/this->ApplyMatrix (vnl_matrix(R.GetVnlMatrix()) )/' \ Common/itkTensor.txx # GetDirection().GetTranspose() returns vnl_matrix_fixed sed -i 's/value\.ApplyMatrix (this->GetOutput()->GetDirection()\.GetTranspose())/value.ApplyMatrix (vnl_matrix(this->GetOutput()->GetDirection().GetTranspose()))/' \ Algorithms/itkResampleTensorImageFilter.txx # --------------------------------------------------------- # FIX: itkTypeMacro wrong class names (GCC 15 -Wtemplate-body) # --------------------------------------------------------- sed -i 's/itkTypeMacro (AddGaussianNoiseTensorImageFilter,/itkTypeMacro (AddGaussianNoiseImageFilter,/' \ Algorithms/itkAddGaussianNoiseImageFilter.h sed -i 's/itkTypeMacro (AddRicianNoiseTensorImageFilter,/itkTypeMacro (AddRicianNoiseImageFilter,/' \ Algorithms/itkAddRicianNoiseImageFilter.h # --------------------------------------------------------- # FIX: ITK 5.4 BooleanStdVectorType is vector not vector # --------------------------------------------------------- sed -i 's/std::vector ValidTimeStepList/BooleanStdVectorType ValidTimeStepList/' \ Common/itkDenseFiniteDifferenceImageFilter2.h %build # Pin the ITK layout state this build sees (see the Requires above). test -n "%{?itk_abi}" || { echo "ERROR: itk_abi undefined - needs InsightToolkit5-devel >= 5.4.7-24 (macros.itk)" >&2; exit 1; } # vtk_abi comes from vtk-devel's macros.vtk. Absent means BuildRequires: vtk-devel is # missing, and the Requires above would ship as the vacuous "vtk(abi) = UNSET". test -n "%{?vtk_abi}" || { echo "ERROR: vtk_abi undefined - add 'BuildRequires: vtk-devel'" >&2; exit 1; } export CXXFLAGS="%{optflags} -std=c++17 -include cstdint -fpermissive" %cmake \ -GNinja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_SKIP_INSTALL_RPATH=ON \ -DCMAKE_INSTALL_LIBDIR=%{_lib} \ -DCMAKE_CXX_STANDARD=17 \ -DITK_DIR=%{_prefix}/lib/cmake/ITK-5.4 \ -DBUILD_SHARED_LIBS=ON \ -DBUILD_EXECUTABLES=OFF \ -DTTK_USE_GMM=OFF \ -DTTK_USE_MKL=OFF \ -DTTK_USE_ACML=OFF %cmake_build %install %cmake_install %ldconfig_scriptlets %files %license LICENSE.txt %{_libdir}/libITKTensor.so.* %{_libdir}/libttkAlgorithms.so.* %{_libdir}/libITKProgramFactory.so.* %{_libdir}/libttkCommands.so.* %{_libdir}/libttkConvertCommands.so.* %{_libdir}/libttkUtilCommands.so.* %files devel %{_includedir}/Common/ %{_includedir}/IPF/ %{_includedir}/Algorithms/ %{_includedir}/Commands/ %{_includedir}/Registration/ %{_includedir}/ttkConfigure.h %{_libdir}/libITKTensor.so %{_libdir}/libttkAlgorithms.so %{_libdir}/libITKProgramFactory.so %{_libdir}/libttkCommands.so %{_libdir}/libttkConvertCommands.so %{_libdir}/libttkUtilCommands.so %{_libdir}/cmake/TTK/ %{_libdir}/cmake/ITKTensor/ %{_libdir}/cmake/ttkAlgorithms/ %{_libdir}/cmake/ITKProgramFactory/ %{_libdir}/cmake/Registration/ %{_libdir}/cmake/ttkCommands/ %{_libdir}/cmake/ttkConvertCommands/ %{_libdir}/cmake/ttkUtilCommands/ %changelog * Tue Oct 06 2026 Morgan Hough - 4.0.1-9 - Pin the ITK layout state: Requires: itk(abi) = %%{itk_abi}, from InsightToolkit5-devel's macros.itk, plus a %%build guard. ITK -23 flipped ITK_LEGACY_REMOVE back off, which changes class layouts at unchanged sonames; this package was built against -10..-21 (legacy removed) and the itk_ver floor let it install over -23, giving silent wrong results or crashes. Rebuild against ITK >= 5.4.7-24. * Mon Oct 05 2026 Morgan Hough - 4.0.1-8 - itk::Point's scalar constructor is explicit under ITK_LEGACY_REMOVE too, not just itk::Vector's (itkPoint.h:104-123, whose own comment says "ITK_LEGACY_REMOVE=ON will disallow implicit conversion from a single value"). Two copy-initialisation sites fixed by direct-initialisation. - Why -7 was not enough, which is the useful part: -7 asserted only the Vector comparison, passed ci/prep-tdd.sh RED->GREEN, and then failed on the builder at the Point site. Red-green only proves what you ASSERT. A green run against a narrow assertion is not evidence about constructs the assertion never mentions. - The check is now CLASS-level: any Point/Vector-typed variable copy-initialised from a scalar fails %%prep, anywhere in the tree. A new site in a future tarball is caught in %%prep rather than four minutes into a build. The seven genuine scalar comparisons are still pinned separately. * Mon Oct 05 2026 Morgan Hough - 4.0.1-7 - ITK_LEGACY_REMOVE made itk::Vector's scalar constructor explicit, so `if( g != 0.0 )` on an itk::Vector no longer compiles (Commands/itkLogEuclideanDTIEstimatorCommand.cxx:178). Compare against GradientType(0.0), which is exactly what the implicit conversion used to produce. Same root cause as RPI 4.0-6 earlier today, a different symptom. - One site. TTK has seven other `!= 0.0` comparisons and all seven are genuine scalars, so %%prep pins their count: a future broadening of this rewrite fails loudly instead of corrupting them. - Surfaced on fedora-rawhide-x86_64, where TTK was blocking medInria: TTK was still the stale 4.0.1-4.fc45 build linking libitkvnl-5.4.so.1, which ITK 5.4.7-21 no longer ships. - Verified with ci/prep-tdd.sh rather than by building: RED on the unpatched tarball, GREEN after, with the seven scalar comparisons asserted untouched in both directions. * Sat Sep 19 2026 Morgan Hough - 4.0.1-6 - Rebuild against InsightToolkit5 5.4.7-8, which unbundles Eigen3, VXL and double-conversion. No source or spec change; the Release bump is what makes the rebuild reach anyone. COPR adds a sibling at an already-published NVR rather than replacing it, and dnf then serves the older copy -- so a rebuild at the same NVR would leave this package linked against the bundled ITK while appearing to succeed. - Validated against -8 in mhough/neurofedora-itk5-test before this bump. * Sun Aug 23 2026 Morgan Hough - 4.0.1-5 - Rebuild against VTK 9.7.0. VTK here installs unversioned sonames (libvtk*.so.1), so 9.7 replaces 9.6 in place and every consumer must be rebuilt or it fails at runtime with undefined symbols. * Tue May 19 2026 Morgan Hough - 4.0.1-4 - Drop vtk-devel < 9.3 BR for F44 — TTK's VTK API surface (PolyData/Points/CellArray/SmartPointer/IdType) is stable across VTK 9.2 -> 9.5+; the existing `const vtkIdType*` GetNextCell sed patches still apply against VTK 9.5.2. Qt-free at the source level (zero QObject/QString hits). - Bump InsightToolkit5-devel requirement to 5.4.6 * Tue Mar 03 2026 Morgan Hough - 4.0.1-3 - Fix VTK 9.2.6 GetNextCell const vtkIdType* API change - Fix ITK 5.4 NumericTraits::GetLength rvalue binding (const ref) - Fix ApplyMatrix ambiguity with vnl_matrix_fixed to vnl_matrix cast - Fix itkTypeMacro wrong class names (GCC 15 -Wtemplate-body) - Fix ITK 5.4 BooleanStdVectorType (vector of itk::Boolean, not bool) - Add java-devel BuildRequires: VTK 9.2.6 cmake targets reference JVM paths * Tue Mar 03 2026 Morgan Hough - 4.0.1-1 - Initial package for TTK 4.0.1 (INRIA Tensor ToolKit) - Shared libraries with injected SOVERSION - Disable executables and optional GMM/MKL/ACML features