# PETDICOMExtension: PET in 3D Slicer's DICOM module. The SUVFactorCalculator # CLI (C++, ITK + DCMTK) computes Standardized Uptake Value factors from PET # DICOM headers; two DICOM plugins load PET series as SUV images (DICOMPETSUVPlugin) # and read Real World Value Mapping objects (DICOMRWVMPlugin). A dependency of # QuantitativeReporting. Built non-superbuild against the system 3dslicer. %global commit d36e67e3068250bb8c33c3abaf65b369634f12d7 %global shortcommit %(c=%{commit}; echo ${c:0:8}) %global snapdate 20260313 %global slicer_inst %{_prefix}/lib/slicer %global slicer_subdir Slicer-5.12 Name: 3dslicer-petdicom Version: 0 Release: 0.1.%{snapdate}git%{shortcommit}%{?dist} Summary: PET DICOM support (SUV) for 3D Slicer (PETDICOMExtension) # The 3D Slicer Contribution and Software License Agreement v1.0, accepted # provisionally by the project pending a Fedora Legal determination # (ci/check-fedora-licence.py, PROVISIONAL). License: 3D-Slicer-1.0 URL: https://github.com/QIICR/Slicer-PETDICOMExtension Source0: %{url}/archive/%{commit}/Slicer-PETDICOMExtension-%{shortcommit}.tar.gz BuildRequires: cmake >= 3.20.6 BuildRequires: ninja-build BuildRequires: gcc-c++ BuildRequires: python3-devel # SUVFactorCalculator's CMake runs SlicerMacroExtractRepositoryInfo. BuildRequires: git-core BuildRequires: 3dslicer-devel >= 5.12.4 # Full Qt6 set find_package(Slicer)'s Qt check demands. BuildRequires: cmake(Qt6Core) BuildRequires: cmake(Qt6Gui) BuildRequires: cmake(Qt6Widgets) BuildRequires: cmake(Qt6Network) BuildRequires: cmake(Qt6Sql) BuildRequires: cmake(Qt6Svg) BuildRequires: cmake(Qt6Xml) BuildRequires: cmake(Qt6OpenGL) BuildRequires: cmake(Qt6OpenGLWidgets) BuildRequires: cmake(Qt6PrintSupport) BuildRequires: cmake(Qt6Multimedia) BuildRequires: cmake(Qt6MultimediaWidgets) BuildRequires: cmake(Qt6Core5Compat) BuildRequires: cmake(Qt6StateMachine) BuildRequires: cmake(Qt6WebEngineCore) BuildRequires: cmake(Qt6WebEngineWidgets) BuildRequires: cmake(Qt6WebChannel) BuildRequires: cmake(Qt6Qml) BuildRequires: cmake(Qt6Quick) BuildRequires: cmake(Qt6QuickWidgets) BuildRequires: cmake(Qt6Test) BuildRequires: cmake(Qt6UiTools) BuildRequires: qt6-qttools-devel BuildRequires: InsightToolkit5-devel >= 5.4.6-7 BuildRequires: InsightToolkit5-vtk-devel BuildRequires: vtk-devel >= 9.6 BuildRequires: ctk-devel BuildRequires: qttesting-devel BuildRequires: vtkAddon-devel BuildRequires: qRestAPI-devel BuildRequires: python-pythonqt-devel BuildRequires: commontk-applauncher-devel BuildRequires: dcmtk-devel BuildRequires: SlicerExecutionModel-devel BuildRequires: rapidjson-devel BuildRequires: libcurl-devel BuildRequires: jsoncpp-devel # Loading the foundation SlicerConfig.cmake triggers find_package(Teem) (config # mode); Fedora ships TeemConfig.cmake under %%{_libdir}/cmake. BuildRequires: teem-devel Requires: 3dslicer Requires: python3-six # 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} # itk(abi): ITK_LEGACY_REMOVE changes class layouts at unchanged sonames; equality on the # legacy-state token, see InsightToolkit5.spec. Requires: itk(abi) = %{?itk_abi}%{!?itk_abi:UNSET} # Pin the exact DCMTK snapshot this package was built against. dcmtk is packaged from git # HEAD and upstream's DCMTK_ABI_VERSION stays 20 across snapshots, so every snapshot installs # libdcm*.so.20 and rpm cannot tell them apart: a consumer built against one installs cleanly # against another and fails at load with undefined symbols (dcmqi on f45, with OverlapUtil). # %%dcmtk_abi comes from dcmtk-devel's macros.dcmtk, so it re-pins on every rebuild. The # missing-macro check is in %%build, not at parse time, for the reason spelled out at the vtk # pin in 3dslicer.spec: an SRPM parse has no buildroot. Requires: dcmtk(abi) = %{?dcmtk_abi}%{!?dcmtk_abi:UNSET} %description PETDICOMExtension adds PET support to 3D Slicer's DICOM module. PET series load as Standardized Uptake Value (SUV) images, with the body-weight SUV factor computed from the DICOM header by the SUVFactorCalculator CLI module, and DICOM Real World Value Mapping objects are read. QuantitativeReporting depends on it. %prep %autosetup -n Slicer-PETDICOMExtension-%{commit} -p1 # Distro build: install modules directly, not as an Extension-Manager bundle. sed -i '/include(${Slicer_EXTENSION_CPACK})/d' CMakeLists.txt ! grep -q 'Slicer_EXTENSION_CPACK' CMakeLists.txt || { echo "ERROR: CPack include left" >&2; exit 1; } %build # The top-level CMakeLists.txt asks for cmake_minimum_required(VERSION 2.8.9), # which CMake 4 refuses (CMAKE_POLICY_VERSION_MINIMUM above). test -n "%{?vtk_abi}" || { echo "ERROR: vtk_abi undefined - add 'BuildRequires: vtk-devel'" >&2; exit 1; } test -n "%{?itk_abi}" || { echo "ERROR: itk_abi undefined - needs InsightToolkit5-devel >= 5.4.7-24 (macros.itk)" >&2; exit 1; } test -n "%{?dcmtk_abi}" || { echo "ERROR: dcmtk_abi undefined - needs the dcmtk-devel that ships macros.dcmtk" >&2; exit 1; } %cmake -G Ninja \ -DSlicer_DIR:PATH=%{slicer_inst}/lib/%{slicer_subdir} \ -DTeem_DIR:PATH=%{_libdir}/cmake \ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ -DBUILD_TESTING:BOOL=OFF \ -DSlicer_SKIP_SlicerBlockAdditionalLauncherSettings:BOOL=TRUE \ -DCMAKE_INSTALL_PREFIX:PATH=%{slicer_inst} %cmake_build %install %cmake_install # Put the SEM CLI where Slicer looks for it (lib//cli-modules). Non- # superbuild against the system SlicerExecutionModel it installs to bin; see # 3dslicer-spharm-pdm.spec for the full story. clidir=%{buildroot}%{slicer_inst}/lib/%{slicer_subdir}/cli-modules install -d "$clidir" for f in SUVFactorCalculator SUVFactorCalculator.xml; do if test -e "%{buildroot}%{slicer_inst}/bin/$f"; then mv "%{buildroot}%{slicer_inst}/bin/$f" "$clidir/$f" fi test -e "$clidir/$f" || { echo "ERROR: $f is in neither bin nor cli-modules" >&2; exit 1; } done # The CLI's static helper library (libSUVFactorCalculatorLib.a) is an intermediate # build product the install rules copy out; nothing links against it. rm -f %{buildroot}%{slicer_inst}/lib64/libSUVFactorCalculatorLib.a ! find %{buildroot} -name '*.a' | grep . || { echo "ERROR: static archives left" >&2; exit 1; } # Both DICOM plugins and the self-test module must be there. _qsm=%{buildroot}%{slicer_inst}/lib/%{slicer_subdir}/qt-scripted-modules for m in DICOMPETSUVPlugin DICOMRWVMPlugin PETDicomExtensionSelfTest; do test -f "$_qsm/$m.py" || { echo "ERROR: $m.py not installed" >&2; exit 1; } done ( cd %{buildroot} && find . \( -type f -o -type l \) | sed 's|^\.||' ) \ | grep -v '^%{slicer_inst}/include/' > %{_builddir}/petdicom.files %check # The CLI loads and describes itself (the XML Slicer reads to register it). # Slicer starts CLIs with its launcher's [LibraryPaths] (libITKFactoryRegistration # and Slicer's own libraries are private to %%{slicer_inst}), so use the same list. _ini=$(grep -ls 'qt-loadable-modules' %{slicer_inst}/bin/*LauncherSettings.ini | head -1) test -n "$_ini" || { echo "ERROR: no launcher settings with [LibraryPaths]" >&2; exit 1; } _lp=$(sed -n '/^\[LibraryPaths\]/,/^\[/s/^[0-9]*\\path=//p' "$_ini" \ | sed "s||%{slicer_inst}/bin|; s|^\.\./|%{slicer_inst}/bin/../|" | paste -sd:) LD_LIBRARY_PATH="$_lp" %{buildroot}%{slicer_inst}/lib/%{slicer_subdir}/cli-modules/SUVFactorCalculator --xml > suv.xml grep -q '' suv.xml %files -f %{_builddir}/petdicom.files %license License.txt %doc README.md %changelog * Mon Oct 12 2026 Morgan Hough <morgan.hough@gmail.com> - 0-0.1.20260313gitd36e67e3 - Initial package, a dependency of QuantitativeReporting