# 3D Slicer Preview — same packaging strategy as 3dslicer, but tracks the # Slicer/Slicer `main` branch HEAD instead of a tagged release. Coexists # with the stable 3dslicer package via parallel-install (separate # install root + separate /usr/bin entry point: `slicer-preview`). # # Bumping policy: refresh %{snapdate}/%{commit} to the most recent main # branch HEAD whenever rebuilding. The release suffix encodes the date # so subsequent rebuilds compare cleanly. %global snapdate 20260914 %global commit 4eafce4f2e3f2d5ca19d52f5fa924c9fa9b4ffe0 %global shortcommit %(c=%{commit}; echo ${c:0:8}) # git rev-list --count for %%{commit}; must be numeric (see the %%build note). %global slicer_revision 30614 # Nominal version tracks Slicer's dev minor (5.13.0, odd = dev). RPM treats # 5.13.0~preview as newer than stable 3dslicer 5.12.0 (even = stable), and a # future 5.13.0 tag would compare cleanly newer than this preview snapshot. %global preview_ver 5.13.0~preview.%{snapdate}git%{shortcommit} %global slicer_xy 5.13 %global slicer_inst %{_prefix}/lib/slicer-preview # Nightly CTest dashboard switch. Default OFF so the shipped RPM never carries # test binaries. Build the dashboard tree with `rpmbuild -bc --with testing ...` # to compile Slicer's CTest suite against this EXACT Fedora (system-libs) config, # then run `ctest` in the build tree. This is how the CDash nightly tests the # Fedora build itself (not an upstream superbuild). See cdash-driver/. %bcond_with testing %define debug_package %{nil} # Disable LTO — Fedora's default -flto=auto -ffat-lto-objects roughly doubles # C++ compile time and inflates link memory. See 3dslicer: with LTO the build # times out; LTO off ~halves compile time. %global _lto_cflags %{nil} # Build single-threaded. At higher -j the ~16 simultaneous Qt/VTK-heavy module # compiles that start at ninja step ~705 blow memory on low-RAM COPR builders # and swap-stall (3dslicer proved -j8/-j4/-j2 all stall there). -j1 + a raised # --timeout 86400 on submission is the working combination. # ONLY for the shipped/COPR build: the --with testing dashboard runs on real # boxes with plenty of RAM, where -j1 wastes the machine — there we let the # CTest driver pass its own -j via --define "_smp_mflags". %if %{without testing} %global _smp_mflags -j1 %endif # Slicer's ctkAppLauncher shebang normalization can leave a /usr/bin/python-real # auto-dependency that no package provides; exclude it as a backstop. %global __requires_exclude ^/usr/bin/python-real$ # Same bundled-OpenSSL/NSS leak that 3dslicer.spec fixes in 5.12.3-10 -- this # tree ships the identical libcrypto.so.3/.4, libssl.so.3/.4 and libssl3.so # under %%{slicer_inst}, so without this filter it too competes distro-wide as a # provider of those sonames and can be dragged into any F45 buildroot that needs # libcrypto.so.3. See the long rationale in 3dslicer.spec. Scoped to these five # sonames, not the whole tree, so Slicer's own libMRML*/libqSlicer* provides stay # visible to the extension packages. %global __provides_exclude_from ^%{slicer_inst}/lib/Slicer-%{slicer_xy}/lib(crypto|ssl|ssl3)\\.so.*$ Name: slicer-preview Version: %{preview_ver} Release: 21%{?dist} Summary: 3D Slicer development snapshot (main branch) for medical image analysis License: BSD-3-Clause # https://fedoraproject.org/wiki/Changes/EncourageI686LeafRemoval ExcludeArch: %{ix86} URL: https://slicer.org/ Source0: https://github.com/Slicer/Slicer/archive/%{commit}/Slicer-preview-%{shortcommit}.tar.gz # Profile.d hook (shared script: re-uses logic from stable, looks for # both slicer-VER and slicer-preview) Source1: slicer-preview.sh # Desktop integration, none of which upstream provides. Distinct component id, # desktop id and icon name from the stable 3dslicer package, because the two # tracks are deliberately co-installable and would otherwise collide on every # one of those files. See docs/desktop-integration-pattern.md. Source20: org.slicer.SlicerPreview.desktop Source21: org.slicer.SlicerPreview.metainfo.xml # Python 3.14 (F44/rawhide) removed the private _PyInterpreterState_GetConfigCopy # / _PyInterpreterState_SetConfig functions that qSlicerCoreApplication used to # refresh sys.argv post-PythonQt. Rewrite that block to set sys.argv via the # stable public API (PySys_SetObject). Same fix carried by 3dslicer.spec. Patch0: slicer-python314-interpreter-config.patch # VTK 9.7: vtkAlgorithm::Update() is now bool, not void. Patch1: slicer-vtk97-update-bool.patch # VTK 9.7 removed vtkLocator::GetTolerance(). Patch2: slicer-vtk97-obbtree-tolerance.patch # Libs/vtkTeem/CMakeLists.txt hardcodes the library name "itkvnl", which exists # only when ITK bundles VXL. Against InsightToolkit5 5.4.7-8 (ITK_USE_SYSTEM_VXL=ON) # the link fails with "cannot find -litkvnl". ITK exports the right set for either # configuration in ${ITKVNL_LIBRARIES}, and vtkTeem already requests ITKVNL as a # find_package component, so the variable is in scope. Correct in both modes, hence # offered upstream rather than kept as a downstream fix. Fourth package with this # same bug, after anima, igsio and shapeworks. Patch3: slicer-vtkteem-itkvnl-system.patch # Build infrastructure — same as 3dslicer; see 3dslicer.spec for rationale. BuildRequires: cmake >= 3.20.6 # Desktop integration. File-based, as Ankur's fsleyes spec does it: it says # what the build actually needs and survives the package being renamed. BuildRequires: /usr/bin/desktop-file-install BuildRequires: /usr/bin/appstream-util BuildRequires: ninja-build BuildRequires: gcc-c++ BuildRequires: gcc-gfortran BuildRequires: git-core BuildRequires: python3-devel BuildRequires: xxd BuildRequires: cmake(Qt6Core) BuildRequires: cmake(Qt6Gui) BuildRequires: cmake(Qt6Widgets) BuildRequires: cmake(Qt6OpenGL) BuildRequires: cmake(Qt6OpenGLWidgets) BuildRequires: cmake(Qt6Network) BuildRequires: cmake(Qt6Sql) BuildRequires: cmake(Qt6Svg) BuildRequires: cmake(Qt6Multimedia) BuildRequires: cmake(Qt6MultimediaWidgets) BuildRequires: cmake(Qt6Xml) BuildRequires: cmake(Qt6Concurrent) BuildRequires: cmake(Qt6PrintSupport) BuildRequires: cmake(Qt6Qml) BuildRequires: cmake(Qt6Quick) BuildRequires: cmake(Qt6WebEngineCore) BuildRequires: cmake(Qt6WebEngineWidgets) BuildRequires: cmake(Qt6WebChannel) BuildRequires: cmake(Qt6Test) BuildRequires: cmake(Qt6LinguistTools) BuildRequires: cmake(Qt6UiTools) BuildRequires: cmake(Qt6Designer) BuildRequires: cmake(Qt6Core5Compat) BuildRequires: qt6-qttools-devel BuildRequires: InsightToolkit5-devel >= 5.4.6 BuildRequires: InsightToolkit5-vtk-devel >= 5.4.6 BuildRequires: vtk-devel >= 9.6 BuildRequires: ctk-devel # CTK is built with CTK_USE_QTTESTING=ON, so CTKConfig.cmake always calls # find_dependency(QtTesting); qMRML*EventPlayer sources also link CTKQtTesting. BuildRequires: qttesting-devel # Slicer_USE_PYTHONQT=ON: find_package(PythonQt REQUIRED) + Python-scripted layer. BuildRequires: python-pythonqt-devel # Pulled in transitively by Slicer's cmake config consumers (VTK/SEM). BuildRequires: cli11-devel BuildRequires: vtkAddon-devel BuildRequires: qRestAPI-devel BuildRequires: commontk-applauncher-devel BuildRequires: python-simpleitk BuildRequires: dcmtk-devel BuildRequires: teem-devel BuildRequires: SlicerExecutionModel-devel BuildRequires: jsoncpp-devel BuildRequires: rapidjson-devel BuildRequires: libarchive-devel BuildRequires: tbb-devel BuildRequires: zlib-devel BuildRequires: openssl-devel # F45+ only: Fedora's thrift is still linked against OpenSSL 3 (libthrift needs # libcrypto.so.3 / libssl.so.3), while F45's openssl-devel is 4.x. Slicer links # thrift, so the real OpenSSL 3 runtime must be present in %%{_libdir} or the # CLI-module link fails with "undefined reference to ...@OPENSSL_3.0.0". # # Requiring the package by NAME is deliberate -- do not relax this to a soname # dependency. 3dslicer used to leak its own bundled libcrypto.so.3 as a Provides # (fixed in 5.12.3-10), so a soname-based dependency let dnf satisfy thrift by # installing the previous 3dslicer into this very buildroot. Its bundled copy # lives under %%{slicer_inst}, which is not on the linker search path, so ld # reported it "not found" and 5.12.3-10 failed at 2603/2845 after 6.5 h # (build 11000918). 5.12.3-9 had only succeeded because it was the first F45 # Slicer build, i.e. before anything in the repo could win that race -- so the # package broke its own rebuild. # # openssl3-libs does not exist on F44 (openssl-libs is 3.x there and provides # these sonames itself), hence the version guard. %if 0%{?fedora} >= 45 BuildRequires: openssl3-libs %endif BuildRequires: libcurl-devel BuildRequires: libxml2-devel BuildRequires: expat-devel BuildRequires: bzip2-devel BuildRequires: freetype-devel BuildRequires: fontconfig-devel BuildRequires: fmt-devel BuildRequires: pugixml-devel BuildRequires: utf8cpp-devel BuildRequires: PEGTL-devel BuildRequires: nlohmann-json-devel BuildRequires: json-devel BuildRequires: lz4-devel BuildRequires: xz-devel BuildRequires: double-conversion-devel BuildRequires: glew-devel BuildRequires: libogg-devel BuildRequires: libtheora-devel BuildRequires: sqlite-devel BuildRequires: libharu-devel BuildRequires: proj-devel BuildRequires: gdal-devel BuildRequires: netcdf-cxx-devel BuildRequires: cgnslib-devel BuildRequires: libpq-devel BuildRequires: mariadb-connector-c-devel BuildRequires: openslide-devel BuildRequires: libtiff-devel BuildRequires: libpng-devel BuildRequires: libjpeg-turbo-devel BuildRequires: mesa-libGL-devel BuildRequires: libX11-devel BuildRequires: libXext-devel BuildRequires: libXt-devel BuildRequires: libXcursor-devel BuildRequires: hdf5-devel BuildRequires: boost-devel BuildRequires: gdcm-devel BuildRequires: python3-pip BuildRequires: python3-numpy Requires: qt6-qtbase Requires: qt6-qtwebengine Requires: qt6-qtmultimedia Requires: hicolor-icon-theme # The desktop entry declares MimeType=application/x-mrml and friends. A # MimeType= naming a type nothing defines is inert -- the entry never # appears in Open With and a default pointing at it is silently ignored -- # so the definitions are a hard dependency, not a nicety. Requires: neuro-mime-types Requires: libGL # DICOM/Python-scripted layer: Slicer's DICOMLib imports these at module load. Requires: python3-dicomweb-client Requires: python3-highdicom Requires: python3-legacy-cgi # Slicer is built with -DSlicer_USE_SCIPY=ON; without this the shipped RPM has # SciPy support enabled but no SciPy installed and scipy-importing modules break. Requires: python3-scipy # vtkAddon runtime: the unversioned libvtkAddon.so has no auto-generated SONAME # dep, and Slicer's bin/Python/vtkAddon.py does "from vtkAddonPython import *" # (ships in python3-vtkAddon). Pull both explicitly. Requires: vtkAddon Requires: python3-vtkAddon # Slicer's own scripted code imports SimpleITK (3 files in this package, sitkUtils among them) and # nothing required python3-simpleitk, so they failed with "No module named 'SimpleITK'" on a clean # install. Found by framework in an offscreen launch of the served f44 set. The only "simpleitk" in # the Requires before this was libitkSimpleITKFilters-5.4.so.1, an ITK library, not the binding. Requires: python3-simpleitk # qSlicerCLIExecutableModuleFactory::findPython() searches PATH for "python-real", then "python". # Fedora has only python3, so a Python CLI module (3dslicer-dmri's ExtractDWIShells) failed with # "Failed to find python interpreter for CLI". python-unversioned-command provides /usr/bin/python. Requires: python-unversioned-command # 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} # CTK, same disease as VTK but a different mechanism. CTK's sonames are frozen # at .so.0.1 across every snapshot, so the automatic soname dependencies rpm # generates (libCTKCore.so.0.1 and nine siblings) are satisfied by a CTK built # months earlier. dnf therefore has no reason to upgrade CTK, and a host ends up # running this Slicer against a CTK that lacks the symbols it needs -- with no # install-time error. Observed: 206 of 246 Slicer libraries unresolved against # the January snapshot. %%ctk_ver comes from ctk-devel's macros.ctk, so this # floor tracks the CTK actually built against. A floor, not an exact match: a # newer CTK is fine, an older one is not. Requires: ctk%{?_isa} >= %{?ctk_ver}%{!?ctk_ver: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 3D Slicer Preview tracks the current Slicer development branch (main) and is intended for users who want access to features and bug-fixes that have landed since the current stable release (5.12.0). This package coexists with the stable 3dslicer package via parallel install: stable installs to /usr/lib/slicer and is invoked as `Slicer`; preview installs to /usr/lib/slicer-preview and is invoked as `slicer-preview`. User settings and downloaded extensions are revision-keyed under ~/.config/slicer.org, so the two stay isolated. Use stable for routine work; use preview to validate upcoming-release behaviour or to use new modules before they reach a tagged release. # Qt Designer / iconengine / style plugins, split out because they are the one part # of Slicer that does NOT live under its own prefix. SlicerCPack installs them into # the SYSTEM Qt6 plugin tree (long-standing Slicer bug, noted as #6 in this spec), # and Qt Designer only scans that standard directory -- so versioning the path would # make the files tidy and inert. # # Both Slicer tracks build the same 20 filenames there: 18 designer plugins plus one # iconengine and one style. That made 3dslicer and slicer-preview mutually # exclusive, which contradicts the two-track design they are packaged for. Measured # on f44-x86_64 as well as f45, so it was never an f45 artefact: co-installing the # two reported exactly 20 file conflicts and nothing else. # # Splitting them means the applications co-install and only the Designer integration # is exclusive -- which it has to be, since one shared directory can only describe # one Slicer's widgets. Nothing at runtime needs this subpackage; it is for authoring # Slicer UIs in Qt Designer. %package designer Summary: Qt Designer plugins for 3D Slicer Preview widgets Requires: %{name}%{?_isa} = %{version}-%{release} # One shared Qt6 plugin directory cannot describe two Slicers. Explicit and # narrow, rather than the accidental collision between the applications. Conflicts: 3dslicer-designer %description designer Qt Designer integration for 3D Slicer Preview: widget plugins, the Slicer icon engine and the Slicer style. Install this only to author Slicer user interfaces in Qt Designer; 3D Slicer Preview itself does not need it. Mutually exclusive with the other Slicer track's designer package, because Qt Designer reads a single system-wide plugin directory. %package devel Summary: Development headers for 3D Slicer Preview Requires: %{name}%{?_isa} = %{version}-%{release} %description devel Headers, cmake config files, and devel symlinks for building Slicer extension modules against the preview channel out of tree. %prep %autosetup -p1 -n Slicer-%{commit} # Sanity patch: relax Slicer_REQUIRED_PYTHON_VERSION strict-equal to allow # Fedora's Python 3.14 (Slicer pins 3.12.10 nominally). sed -i 's/Slicer_REQUIRED_PYTHON_VERSION "3\.[0-9]*\.[0-9]*")/Slicer_REQUIRED_PYTHON_VERSION "3.12.0")/' CMakeLists.txt 2>/dev/null || : # --------------------------------------------------------------------------- # System-libs (non-superbuild) source-bug fixes — ported verbatim from the # battle-tested stable 3dslicer spec. 5.13 (main HEAD) and 5.12 share these # trees, so the same fixes apply. See 3dslicer.spec for the full rationale. # --------------------------------------------------------------------------- # Short-circuit SlicerBlockInstallTBB. It expects to bundle specific TBB SONAME # versions that don't exist on Fedora; as an RPM we rely on the system tbb. echo 'return()' > CMake/SlicerBlockInstallTBB.cmake # VTK linkage: Fedora's modular VTK leaves several modules out of the legacy # VTK_LIBRARIES that Slicer libs link, so they fail with undefined references. # Force the requested VTK:: targets (+ the foundational Common* family) onto # link_libraries() right after find_package(VTK). python3 - CMakeLists.txt <<'PYEOF' import sys, re f = sys.argv[1]; s = open(f).read() inj = ("\n# Fedora modular-VTK linkage fix (packaging): the new VTK no longer puts the\n" "# full module closure into the legacy VTK_LIBRARIES that several Slicer libs\n" "# link, so they fail with undefined references (vtkImageAccumulate,\n" "# vtkExtractSelection, vtkPolyDataToImageStencil, vtkTimerLog, ...). Rather\n" "# than chase modules one at a time, link every component Slicer already\n" "# requested (Slicer_VTK_COMPONENTS) PLUS the foundational Common* family that\n" "# Slicer libs reference transitively but the components list omits (vtkTimerLog\n" "# = CommonSystem); link them all onto every target defined afterwards via\n" "# link_libraries() (--as-needed drops the unused ones). Robust against any\n" "# VTK_LIBRARIES scope/propagation gaps without per-module whack-a-mole.\n" "set(_PKGVTK_EXTRA\n" " CommonCore CommonSystem CommonMath CommonMisc CommonTransforms\n" " CommonExecutionModel CommonDataModel CommonColor CommonComputationalGeometry\n" " FiltersCore FiltersGeneral FiltersGeometry FiltersSources\n" " ImagingCore ImagingGeneral)\n" "foreach(_vmod ${Slicer_VTK_COMPONENTS} ${_PKGVTK_EXTRA})\n" " if(TARGET VTK::${_vmod})\n" " link_libraries(VTK::${_vmod})\n" " else()\n" " message(WARNING \"PKGVTKFIX: VTK::${_vmod} is NOT a target\")\n" " endif()\n" "endforeach()\n" "message(STATUS \"PKGVTKFIX: link_libraries over components + Common* done\")\n") s2 = re.sub(r'(find_package\(VTK [^\n]*COMPONENTS[^\n]*REQUIRED\)\n)', lambda m: m.group(1) + inj, s, count=1) assert s2 != s, "find_package(VTK COMPONENTS) anchor not found" open(f, "w").write(s2) print("VTK link_libraries fix injected") PYEOF # Slicer source bug #1: 4 Libs/*/CMakeLists.txt use POSIX-regex alternation # in cmake glob patterns: `file(GLOB headers "...*.(h|hxx)")`. cmake's # file(GLOB) doesn't support alternation — globs return empty. Rewrite to # two valid glob patterns separated by a space. for f in Libs/vtkTeem/CMakeLists.txt \ Libs/vtkSegmentationCore/CMakeLists.txt \ Libs/vtkITK/CMakeLists.txt \ Libs/ITKFactoryRegistration/CMakeLists.txt; do sed -i 's|file(GLOB headers "${CMAKE_CURRENT_SOURCE_DIR}/\*\.(h\|hxx)")|file(GLOB headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/*.hxx")|' "$f" done # Slicer expects an itkNamespace.h header that its SuperBuild normally patches # into ITK's source tree. Since we don't superbuild, the file is just missing. # Stub it locally in each Libs/ that includes it. cat > Libs/ITKFactoryRegistration/itkNamespace.h <<'EOF' // Stub for Fedora packaging. Slicer SuperBuild normally patches this // file into ITK's source; we don't superbuild. #ifndef itkNamespace_h #define itkNamespace_h namespace itk {} #endif EOF cp -p Libs/ITKFactoryRegistration/itkNamespace.h Libs/vtkITK/itkNamespace.h # Slicer source bug #2: 4 Libs/*/CMakeLists.txt reference # ${CMAKE_CURRENT_BINARY_DIR}/${configure_header_file} in install(FILES ...) # but never set(configure_header_file ...) so the var is empty and install # gets a trailing-slash directory path, aborting. Drop the unresolved ref. for f in Libs/vtkTeem/CMakeLists.txt \ Libs/MRML/CLI/CMakeLists.txt \ Libs/RemoteIO/CMakeLists.txt \ Libs/MRML/Logic/CMakeLists.txt; do sed -i 's| ${CMAKE_CURRENT_BINARY_DIR}/${configure_header_file}||' "$f" done # Slicer source bug #3: Base/QTCore/Testing/Python/CMakeLists.txt installs its # generic Python test template from ${CMAKE_CURRENT_BINARY_DIR}, but nothing # stages it there — it's a verbatim template in the source tree. Install from # source so %install doesn't abort with BUILD_TESTING=OFF + dev install. sed -i 's|${CMAKE_CURRENT_BINARY_DIR}/qSlicerModuleGenericTest.py.in|${CMAKE_CURRENT_SOURCE_DIR}/qSlicerModuleGenericTest.py.in|' \ Base/QTCore/Testing/Python/CMakeLists.txt # Python 3.13 REMOVED the 'pipes' module (F44 ships 3.14), but Slicer's own test # helper Applications/SlicerApp/Testing/Python/SlicerAppTesting.py still does # "import pipes" / pipes.quote(). That kills ~5 py_ tests at import. pipes.quote # moved to shlex.quote -- the documented migration. (Same class as the cgi removal.) sed -i -e 's/^import pipes$/import shlex/' -e 's/pipes\.quote/shlex.quote/g' \ Applications/SlicerApp/Testing/Python/SlicerAppTesting.py # Slicer source bug (only bites BUILD_TESTING=ON, i.e. the CTest dashboard): # SlicerMacroConfigureModuleCxxTestDriver unconditionally #includes # vtkWin32OutputWindow.h in every generated module CxxTest driver, while the code # that USES it is correctly #ifdef _WIN32-guarded. With system VTK 9.6 that # Windows-only header is absent on Linux, so all ~10 module test drivers FTBFS # (fatal error: vtkWin32OutputWindow.h: No such file). Drop the unconditional # include (swap for the harmless vtkVersionMacros.h already pulled in); the # _WIN32-guarded usage is not compiled on Linux. Harmless when testing is OFF. sed -i 's|set(EXTRA_INCLUDE "vtkWin32OutputWindow.h|set(EXTRA_INCLUDE "vtkVersionMacros.h|' \ CMake/SlicerMacroConfigureModuleCxxTestDriver.cmake # N4ITKBiasFieldCorrection.cxx is an ITK-template memory bomb: compiling it at -O2 # peaks ~22 GB, which OOMs memory-constrained builders and the CTest farm nodes # (a single translation unit, so lowering -j does not help). Compile just this one # CLI source at -O1 -- much lower peak, and N4 is not perf-critical. Same class of # reduction Fedora/Debian apply to heavy ITK TUs. cat >> Modules/CLI/N4ITKBiasFieldCorrection/CMakeLists.txt <<'N4EOF' set_source_files_properties( ${CMAKE_CURRENT_SOURCE_DIR}/N4ITKBiasFieldCorrection.cxx PROPERTIES COMPILE_OPTIONS "-O1") N4EOF # Slicer source bug #4: SlicerCPack.cmake copies the Qt-designer launcher from # ${CTKAppLauncher_DIR}/bin/ — correct in the superbuild but wrong on Fedora, # where commontk-applauncher splits the binary (%{_bindir}) from its cmake # config (%{_libdir}/cmake/CTKAppLauncher). Point at the real binary location. sed -i 's|${CTKAppLauncher_DIR}/bin/${_launcher}|%{_bindir}/${_launcher}|' \ CMake/SlicerCPack.cmake %build # dcmtk_abi comes from dcmtk-devel's macros.dcmtk. Absent means the buildroot has a dcmtk-devel # that predates the snapshot, and the Requires above would carry the UNSET placeholder. test -n "%{?dcmtk_abi}" || { echo "ERROR: dcmtk_abi undefined - needs the dcmtk-devel that ships macros.dcmtk" >&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; } # Same for ctk_ver, from ctk-devel's macros.ctk. Absent means the floor above # would ship as ">= UNSET", which rpm happily satisfies with anything. test -n "%{?ctk_ver}" || { echo "ERROR: ctk_ver undefined - add 'BuildRequires: ctk-devel'" >&2; exit 1; } # -I gdcm: Libs/vtkITK includes gdcmDict.h directly but GDCM only comes in # transitively via ITK's use-file, which doesn't propagate with system GDCM. # -I QtTesting / -I PythonQt: installed CTK headers (ctkQtTestingUtility.h, # ctkAbstractPythonManager.h) #include pq*/PythonQt* headers directly, but many # Slicer targets link CTKWidgets (not CTKQtTesting / a PythonQt-bearing CTK lib) # and so never inherit those third-party include dirs. Add them globally. export CXXFLAGS="%{optflags} -std=c++17 -Wno-error -include cstdint -fpermissive -I%{_includedir}/gdcm -I%{_includedir}/QtTesting -I%{_includedir}/PythonQt" export CFLAGS="%{optflags} -Wno-error -std=gnu17" export QT_QPA_PLATFORM=offscreen # Workaround for Fedora teem-devel packaging bug: the shipped TeemConfig.cmake # computes Teem_USE_FILE one parent dir up (/usr/lib64/lib/TeemUse.cmake) but # the config lives in /usr/lib64/cmake/, so the file resolves wrong. Build a # corrected shim in a private dir and point Teem_DIR there. mkdir -p teem-shim cat > teem-shim/TeemConfig.cmake <<'EOF' # Slicer-build shim — corrects path computation that Fedora teem-devel gets # wrong because TeemConfig.cmake is installed in /usr/lib64/cmake/ instead of # /usr/lib64/cmake/Teem/. set(Teem_USE_FILE "%{_libdir}/cmake/TeemUse.cmake") set(Teem_BUILD_SETTINGS_FILE "%{_libdir}/cmake/TeemBuildSettings.cmake") set(Teem_EXECUTABLE_DIRS "%{_bindir}") set(Teem_LIBRARY_DIRS "%{_libdir}") set(Teem_INCLUDE_DIRS "%{_includedir}") set(Teem_LIBRARIES "teem") set(Teem_REQUIRED_C_FLAGS "") set(Teem_VERSION_MAJOR "1") set(Teem_VERSION_MINOR "12") set(Teem_VERSION_PATCH "0") set(Teem_BUILD_SHARED_LIBS "ON") set(Teem_TOOLS "") EOF %cmake -G Ninja \ -DSlicer_SUPERBUILD:BOOL=OFF \ -DSlicer_USE_SYSTEM_ITK:BOOL=ON \ -DSlicer_USE_SYSTEM_VTK:BOOL=ON \ -DSlicer_USE_SYSTEM_CTK:BOOL=ON \ -DSlicer_USE_SYSTEM_DCMTK:BOOL=ON \ `# Use system OpenJPEG so 5.12+ CPack does not fatal bundling it. See 3dslicer.` \ -DSlicer_USE_SYSTEM_OpenJPEG:BOOL=ON \ `# Do NOT let CPack bundle OpenSSL. SlicerBlockInstallOpenSSL.cmake runs` \ `# slicerInstallLibrary() on each OPENSSL_LIBRARIES entry, which globs the` \ `# whole of %{_libdir} for "libcrypto*" / "libssl*" and copies every match` \ `# into %{slicer_inst}. On Fedora that swept in BOTH OpenSSL runtimes present` \ `# in the buildroot (3.5.7 and 4.0.2) plus NSS's unrelated libssl3.so -- the` \ `# glob is on the name, not the package. Those copies then sat ahead of the` \ `# system libraries on the launcher's [LibraryPaths], so Slicer ran against a` \ `# frozen OpenSSL that no Fedora security update could ever reach, outside` \ `# crypto-policies. Slicer_USE_SYSTEM_OpenSSL is upstream's own gate for this` \ `# and is read in exactly one place, SlicerCPack.cmake:149 -- it suppresses` \ `# the bundling ONLY. SSL support itself is gated on` \ `# Slicer_USE_PYTHONQT_WITH_OPENSSL, which stays ON, and the SuperBuild uses` \ `# do not apply here (Slicer_SUPERBUILD=OFF). Enforced in %%install below.` \ -DSlicer_USE_SYSTEM_OpenSSL:BOOL=ON \ -DSlicer_USE_SYSTEM_teem:BOOL=ON \ -DSlicer_USE_SYSTEM_SlicerExecutionModel:BOOL=ON \ -DSlicer_USE_SYSTEM_jsoncpp:BOOL=ON \ -DSlicer_USE_SYSTEM_RapidJSON:BOOL=ON \ -DSlicer_USE_SYSTEM_LibArchive:BOOL=ON \ -DSlicer_USE_SYSTEM_python:BOOL=ON \ -DSlicer_USE_SYSTEM_qRestAPI:BOOL=ON \ -DSlicer_USE_SYSTEM_python_SimpleITK:BOOL=ON \ -DITK_DIR:PATH=%{_prefix}/lib/cmake/ITK-5.4 \ -DVTK_DIR:PATH=%{_libdir}/cmake/vtk \ -DCTK_DIR:PATH=%{_libdir}/cmake/CTK \ `# CTKConfig find_dependency(QtTesting) resolves the imported qttesting target.` \ -DQtTesting_DIR:PATH=%{_libdir}/cmake/qttesting \ -DDCMTK_DIR:PATH=%{_libdir}/cmake/dcmtk \ -DCTKAppLauncher_DIR:PATH=%{_libdir}/cmake/CTKAppLauncher \ `# CTKAppLauncherLib is a distinct package to CMake though its config shares` \ `# the CTKAppLauncher dir; point both _DIR variables.` \ -DCTKAppLauncherLib_DIR:PATH=%{_libdir}/cmake/CTKAppLauncher \ -DqRestAPI_DIR:PATH=%{_libdir}/cmake/qRestAPI \ -DPythonQt_DIR:PATH=%{_libdir}/cmake/PythonQt \ `# Slicer's top-level CMakeLists references ${vtkAddon_CMAKE_DIR} directly` \ `# (no find_package(vtkAddon)); set both so the module path + wrap macros resolve.` \ -DvtkAddon_DIR:PATH=%{_libdir}/cmake/vtkAddon \ -DvtkAddon_CMAKE_DIR:PATH=%{_libdir}/cmake/vtkAddon \ `# SlicerExecutionModel ships its config at %{_libdir}/SlicerExecutionModel/` \ `# (not under cmake/), which Slicer's find_package doesn't auto-discover.` \ -DSlicerExecutionModel_DIR:PATH=%{_libdir}/SlicerExecutionModel \ -DGenerateCLP_DIR:PATH=%{_libdir}/GenerateCLP \ -DModuleDescriptionParser_DIR:PATH=%{_libdir}/ModuleDescriptionParser \ `# Point at our local TeemConfig.cmake shim — see %%build above for why.` \ -DTeem_DIR:PATH=$(pwd)/teem-shim \ `# Qt6 detection: SlicerBlockFindQtAndCheckVersion checks if(DEFINED Qt6_DIR)` \ `# to pick Qt6 over Qt5; without it, find_package(Qt5) aborts.` \ -DQt6_DIR:PATH=%{_libdir}/cmake/Qt6 \ `# Store settings + extensions in the user's home, not the read-only system` \ `# tree (else the Extensions Manager fails on a system install). See 3dslicer.` \ -DSlicer_STORE_SETTINGS_IN_APPLICATION_HOME_DIR:BOOL=OFF \ `# Tarball builds have no .git, so pre-define the revision (else it is empty).` \ `# With STORE_SETTINGS=OFF the user-home settings/extensions paths are` \ `# revision-keyed (~/.config/slicer.org/Slicer-.ini, Extensions-), so` \ `# a distinct revision is what keeps this preview isolated from stable 3dslicer` \ `# in parallel installs — and the Extensions Manager queries the server by it.` \ -DSlicer_REVISION:STRING=%{shortcommit} \ -DSlicer_WC_LAST_CHANGED_DATE:STRING=2026-06-28 \ `# The version header is emitted by a build-time 'cmake -P' script`\ `# (SlicerConfigureVersionHeaderTarget), which only receives the Slicer_FORCED_*`\ `# args. Plain Slicer_REVISION / Slicer_WC_LAST_CHANGED_DATE never reach it, so`\ `# Slicer_BUILDDATE compiled to "" and vtkSlicerVersionConfigureTest1 failed.`\ `# MUST be numeric, not a git hash. Chain: Slicer_FORCED_REVISION ->` \ `# Slicer_REVISION (SlicerVersion.cmake:77) -> Slicer_MAIN_PROJECT_REVISION` \ `# (5.13 forces them equal for the "Slicer" app, SlicerVersion.cmake:114) ->` \ `# MRML_APPLICATION_REVISION (Libs/CMakeLists.txt:71) -> and` \ `# vtkMRMLConfigure.h.in:11 emits it UNQUOTED:` \ `# #define MRML_APPLICATION_REVISION @MRML_APPLICATION_REVISION@` \ `# so a short hash "98d8e3af" compiles as numeric literal 98 with suffix` \ `# d8e3af -> "unable to find numeric literal operator". CommitCount is the` \ `# upstream default and is a NUMBER; use the real count for this commit.` \ -DSlicer_FORCED_REVISION:STRING=%{slicer_revision} \ -DSlicer_FORCED_WC_LAST_CHANGED_DATE:STRING=2026-06-28 \ -DSlicer_WC_URL:STRING=https://github.com/Slicer/Slicer/commit/%{commit} \ -DSlicer_USE_PYTHONQT:BOOL=ON \ -DSlicer_USE_NUMPY:BOOL=ON \ -DSlicer_USE_SCIPY:BOOL=ON \ -DSlicer_USE_SimpleITK:BOOL=ON \ -DSlicer_USE_CTKAPPLAUNCHER:BOOL=ON \ -DSlicer_USE_TBB:BOOL=ON \ -DSlicer_USE_QtTesting:BOOL=ON \ -DSlicer_BUILD_CLI:BOOL=ON \ -DSlicer_BUILD_QTLOADABLEMODULES:BOOL=ON \ -DSlicer_BUILD_QTSCRIPTEDMODULES:BOOL=ON \ -DSlicer_BUILD_DICOM_SUPPORT:BOOL=ON \ -DSlicer_BUILD_DIFFUSION_SUPPORT:BOOL=ON \ -DSlicer_BUILD_I18N_SUPPORT:BOOL=OFF \ -DSlicer_BUILD_WEBENGINE_SUPPORT:BOOL=ON \ -DSlicer_BUILD_MULTIMEDIA_SUPPORT:BOOL=ON \ -DSlicer_BUILD_EXTENSIONMANAGER_SUPPORT:BOOL=ON \ -DSlicer_BUILD_APPLICATIONUPDATE_SUPPORT:BOOL=ON \ -DSlicer_BUILD_DOCUMENTATION:BOOL=OFF \ -DSlicer_BUILD_KRB5_GSSAPI_STUB:BOOL=OFF \ -DSlicer_REQUIRED_QT_VERSION:STRING=6.8 \ -DSlicer_VTK_VERSION_MAJOR:STRING=9 \ -DSlicer_VTK_VERSION_MINOR:STRING=6 \ -DSlicer_VTK_SMP_IMPLEMENTATION_TYPE:STRING=TBB \ -DSlicer_INSTALL_NO_DEVELOPMENT:BOOL=OFF \ -DCMAKE_INSTALL_PREFIX:PATH=%{slicer_inst} \ -DSlicer_INSTALL_ROOT:PATH=%{slicer_inst} \ -DSlicer_INSTALL_DEVELOPMENT:BOOL=ON \ -DBUILD_TESTING:BOOL=%{?with_testing:ON}%{!?with_testing:OFF} \ -DBUILD_SHARED_LIBS:BOOL=ON \ -DCMAKE_SKIP_INSTALL_RPATH:BOOL=ON \ -DCMAKE_BUILD_TYPE:STRING=Release \ -DSlicer_USE_VTK_DEBUG_LEAKS:BOOL=OFF %cmake_build %install %cmake_install # Slicer source bug #5/#8: several installed wizard/utility scripts (e.g. # libexec/Slicer-5.13/ExtensionWizard.py, ModuleWizard.py) carry non-system # python shebangs: # - "#!/usr/bin/env python" -> brp-mangle-shebangs rejects (ambiguous), fails %install # - "#!/usr/bin/python-real" -> unsatisfiable Requires: /usr/bin/python-real # Slicer runs these through its own launcher, so the shebang is cosmetic. # Normalize EVERY non-system python shebang on line 1 to /usr/bin/python3. # (Same block 3dslicer.spec carries; %{slicer_inst}-scoped, version-agnostic.) find %{buildroot}%{slicer_inst} -type f -print0 | while IFS= read -r -d '' f; do IFS= read -r line0 < "$f" 2>/dev/null || continue case "$line0" in '#!'*python*) sed -i -E '1s|^#!.*/(env[[:space:]]+)?python[0-9.]*(-real)?$|#!/usr/bin/python3|' "$f" ;; esac done install -D -m 0644 %{SOURCE1} %{buildroot}%{_sysconfdir}/profile.d/slicer-preview.sh install -d %{buildroot}%{_bindir} ln -sf %{slicer_inst}/Slicer %{buildroot}%{_bindir}/slicer-preview # --------------------------------------------------------------------------- # CLI modules: ported verbatim from 3dslicer.spec, where this was fixed for the # 5.12 tree. slicer-preview never received it, so every CLI module it ships is # non-functional: the executables land in bin/ only, and lib/Slicer-%{slicer_xy}/ # cli-modules -- the directory the launcher actually scans -- does not exist at # all in the built RPM (68 such entries in 3dslicer, 0 here). Both specs use the # same %{slicer_inst}/%{slicer_xy} macros, so the block applies unchanged. # --------------------------------------------------------------------------- # 4. CLI module discovery: Slicer's CLI executables land in bin/ but the # launcher scans lib/Slicer-%{slicer_xy}/cli-modules (which ends up empty), so CLI # modules are unfindable. Symlink the cli-modules dir's expected contents # from the installed CLI binaries + their .xml descriptors. _clidir=%{buildroot}%{slicer_inst}/lib/Slicer-%{slicer_xy}/cli-modules mkdir -p "$_clidir" for x in %{buildroot}%{slicer_inst}/bin/*.xml; do [ -e "$x" ] || continue b=$(basename "$x" .xml) if [ -e "%{buildroot}%{slicer_inst}/bin/$b" ]; then ln -sf ../../../bin/"$b" "$_clidir/$b" ln -sf ../../../bin/"$b.xml" "$_clidir/$b.xml" fi done # 4b. CLI module *loading*: step 4 makes the CLI executables discoverable, but # they still cannot run. Each SEM CLI is a thin main() against a companion # libLib.so, and those land in %{slicer_inst}/%{_lib}/ -- a directory # the launcher's [LibraryPaths] does not list. Combined with # CMAKE_SKIP_INSTALL_RPATH=ON (required for Fedora) the binaries carry no # RPATH either, so every one of them dies at exec with # "libFooLib.so: cannot open shared object file". Symptom seen from Python: # slicer.cli.runSync() returns a node whose output volume is empty. # Append %{_lib} to [LibraryPaths] in each launcher settings file (and bump # the section's size= counter, which Qt's QSettings array format requires -- # entries past size= are ignored). for _ini in %{buildroot}%{slicer_inst}/bin/Slicer*LauncherSettings.ini; do [ -e "$_ini" ] || continue python3 - "$_ini" "%{_lib}" <<'EOF' import re, sys # NOTE: keep this snippet free of bare "printf-style" percent signs -- rpm # expands macros in the heredoc before the shell ever sees it, so a stray # "%d" would be eaten. The libdir arrives as argv[2] instead. path, libdir = sys.argv[1], sys.argv[2] entry = "/../" + libdir with open(path, encoding="utf-8") as fh: lines = fh.read().splitlines() def emit(dest, index): dest.append(str(index) + "\\path=" + entry) dest.append("size=" + str(index)) out, in_sec, n, done = [], False, 0, False for line in lines: if line.strip().startswith("["): if in_sec and not done: # section ended without a size= line emit(out, n + 1) done = True in_sec = line.strip() == "[LibraryPaths]" out.append(line) continue m = re.match(r"^(\d+)\\path=(.*)$", line) if in_sec and m: n = max(n, int(m.group(1))) if m.group(2) == entry: # already listed -- stay idempotent done = True if in_sec and line.startswith("size=") and not done: emit(out, n + 1) done = True continue out.append(line) if in_sec and not done: # [LibraryPaths] ran to end of file emit(out, n + 1) with open(path, "w", encoding="utf-8") as fh: fh.write("\n".join(out) + "\n") EOF done # --------------------------------------------------------------------------- # Unbundling guard: fail the build rather than ship private crypto. # # Slicer_USE_SYSTEM_OpenSSL=ON (in %build) stops SlicerCPack from copying the # system OpenSSL into %{slicer_inst}. That flag is the fix; this is the check # that it actually worked, because the failure mode is silent -- the build # succeeds and the RPM merely gains a private libcrypto that the launcher then # prefers over Fedora's. Bundled crypto sits outside crypto-policies and no # security update can reach it, so this is worth a hard failure. # # Deliberately not `|| :` -- if the glob ever matches again, stop. _bundled_crypto="$(find %{buildroot}%{slicer_inst} \ \( -name 'libcrypto.so*' -o -name 'libssl.so*' -o -name 'libssl3.so*' \) \ -print 2>/dev/null)" if [ -n "$_bundled_crypto" ]; then echo "ERROR: private crypto libraries in the install tree:" >&2 echo "$_bundled_crypto" >&2 echo "Slicer_USE_SYSTEM_OpenSSL=ON should have prevented this; see %%build." >&2 exit 1 fi # Static archives do not belong in a runtime package; same three CLI modules as # 3dslicer build a .a instead of a .so and link it statically into their # executables. (No hierarchy/ move here -- slicer-preview does not ship them.) find %{buildroot}%{slicer_inst} -name '*.a' -type f -delete # Empty GSSAPI-stub directory: the stub is gated on Qt5 + Slicer_USE_SYSTEM_QT=OFF # and is never built here. rmdir, not rm -rf, so a future real library is not # silently discarded. Same as 3dslicer.spec. rmdir %{buildroot}%{slicer_inst}/lib/Slicer-%{slicer_xy}/krb5 2>/dev/null || : # --------------------------------------------------------------------------- # Desktop integration. Upstream ships NONE of this: no .desktop, no AppStream # metainfo, no MIME definitions. It does ship desktop icons at five sizes and # nothing that references them, so the largest application in this repo had no # menu entry, no icon in any theme, no software-centre listing and no file # association -- it could only be started by typing its name in a terminal. # # Pattern taken from Fedora's python-fsleyes (Ankur Sinha); see # docs/desktop-integration-pattern.md. desktop-file-install and appstream-util # both VALIDATE as they install, so a malformed file fails the build here # rather than shipping and being silently ignored by the desktop. desktop-file-install --dir=%{buildroot}%{_datadir}/applications %{SOURCE20} install -Dpm0644 %{SOURCE21} \ %{buildroot}%{_metainfodir}/org.slicer.SlicerPreview.metainfo.xml appstream-util validate-relax --nonet \ %{buildroot}%{_metainfodir}/org.slicer.SlicerPreview.metainfo.xml # Icons: upstream's own PNGs, which are already a clean hicolor ladder. # Applications/SlicerApp/Resources/Icons/{Small,Medium,Large,XLarge,XXLarge} # measure 16, 32, 48, 64 and 128 pixels square -- measured, not assumed, since # the names carry no sizes. Installed under slicer-preview so the stable and preview # tracks keep separate icons and can stay co-installed. _icons_installed=0 for _pair in Small:16 Medium:32 Large:48 XLarge:64 XXLarge:128; do _dir=${_pair%%:*}; _px=${_pair##*:} _src="Applications/SlicerApp/Resources/Icons/$_dir/Slicer-DesktopIcon.png" [ -f "$_src" ] || { echo "missing upstream icon $_src (cwd $PWD)" >&2; exit 1; } install -Dpm0644 "$_src" \ %{buildroot}%{_datadir}/icons/hicolor/${_px}x${_px}/apps/slicer-preview.png _icons_installed=$((_icons_installed+1)) done # Assert rather than trust the loop: a renamed upstream directory would # otherwise leave Icon=slicer-preview resolving to nothing, which looks like a blank # tile and like nothing at all in the build log. test "$_icons_installed" -eq 5 \ || { echo "expected 5 icon sizes, installed $_icons_installed" >&2; exit 1; } %check # Build-time validation of the SHIPPED tree. Modelled on Fedora's # InsightToolkit (Ankur Sinha), which runs `%%ctest || exit 0` and records the # known failures in a comment -- but deliberately stronger in one respect: that # form can never fail, so it documents regressions rather than catching them. # # Slicer's full CTest suite is not run here. It wants a display and a writable # home, it takes longer than the build, and the dashboard already runs it # properly against this exact configuration (`--with testing`, see cdash-driver). # What is NOT covered anywhere else is whether the hand-rolled repairs this spec # performs in %%install still hold -- and those are precisely what rots silently # when upstream moves a directory. Every check below therefore targets one of # our own workarounds, or the class of breakage that actually reached built # packages this cycle (unresolved symbols from an ABI-skewed dependency). # # Risk posture: skip when an input is absent, fail only on positive evidence of # breakage. A false failure here costs a multi-hour rebuild, so nothing is # inferred from a missing file. _sroot=%{buildroot}%{slicer_inst} _slib="$_sroot/lib/Slicer-%{slicer_xy}" _fail=0 # Library path: derive it from the launcher's own [LibraryPaths], which is the # authoritative list of where Slicer looks at runtime -- do NOT hand-list # directories here. 5.12.4-1 failed exactly that way: this check exported only # lib64 and lib/Slicer-%%{slicer_xy}, while the launcher sets SIX paths, and the # two missing ones (bin, qt-loadable-modules) are where # libvtkSlicerMarkupsModuleMRML.so lives. ExecutionModelTour and ExtractSkeleton # link it, so they "failed to load" against a tree that was in fact correct -- # a false failure that killed four builds (11009184, 11009170, 11009171, # 11009172) after ~7 h each. The check was less faithful to runtime than the # launcher it was meant to be checking. # Pick the launcher settings file by CONTENT, not by find order. The tree holds # more than one: SlicerLauncherSettings.ini (6 library paths) and # SlicerDesignerLauncherSettings.ini (1, for the Qt Designer helper). `find | # head -1` returns them in filesystem order, which differs between hosts -- it # picked the main one locally and the Designer one in the COPR buildroot, where # the derived path then had a single entry and all 12 CLIs "failed to load". # The file with the most [LibraryPaths] entries is the application launcher, in # both this package and slicer-preview, without hardcoding either name. _ini_lp="" _best=-1 for _cand in $(find "$_sroot" -maxdepth 2 -name '*LauncherSettings.ini' 2>/dev/null | sort); do _n="$(sed -n '/^\[LibraryPaths\]/,/^\[/p' "$_cand" 2>/dev/null | grep -c '\\path=')" if [ "${_n:-0}" -gt "$_best" ]; then _best="$_n"; _ini_lp="$_cand"; fi done _lp="" if [ -n "$_ini_lp" ]; then _sdir="$(dirname "$_ini_lp")" _lp="$(sed -n '/^\[LibraryPaths\]/,/^\[/p' "$_ini_lp" \ | sed -n 's/^[0-9]*\\path=//p' \ | sed "s||$_sdir|g" \ | while read -r _p; do (cd "$_sdir" 2>/dev/null && cd "$_p" 2>/dev/null && pwd); done \ | paste -sd:)" fi # Fall back to the obvious pair only if the ini could not be parsed. [ -n "$_lp" ] || _lp="$_sroot/%{_lib}:$_slib" export LD_LIBRARY_PATH="$_lp${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" echo "check: library paths from launcher = $(echo "$_lp" | tr ':' '\n' | grep -c .)" # 1. The cli-modules symlink farm (%%install step 4) must resolve. `find -xtype l` # reports links whose target does not exist -- the exact failure mode if # upstream renames bin/ or the .xml pairing changes. if [ -d "$_slib/cli-modules" ]; then _broken="$(find "$_slib/cli-modules" -xtype l -print 2>/dev/null)" if [ -n "$_broken" ]; then echo "ERROR: dangling cli-modules symlinks (see %%install step 4):" >&2 echo "$_broken" | head -20 >&2 _fail=1 fi _nlinks="$(find "$_slib/cli-modules" -type l 2>/dev/null | wc -l)" echo "check: cli-modules symlinks = $_nlinks" if [ "$_nlinks" -eq 0 ]; then echo "ERROR: cli-modules is empty; CLI modules would be undiscoverable." >&2 _fail=1 fi fi # 2. The launcher settings must still list %%{_lib} (%%install step 4b). Without # it every SEM CLI dies at exec on its companion libLib.so, and the # only symptom is an empty output volume from slicer.cli.runSync(). # Reuse the ini chosen above -- checking a different one than the path # derivation used is how part 2 came to pass against the Designer launcher. _ini="$_ini_lp" if [ -n "$_ini" ]; then if grep -q "LibraryPaths" "$_ini" && ! grep -q "%{_lib}" "$_ini"; then echo "ERROR: %{_lib} missing from [LibraryPaths] in $_ini" >&2 _fail=1 fi echo "check: launcher settings = $(basename "$_ini")" fi # 3. Load a sample of CLI executables. REPORTED, NOT ENFORCED -- deliberately, # and this is a demotion rather than an original choice. As an assertion it # produced a false failure that cost four ~7 h builds (see the note on # library paths above), and the underlying path fix, while demonstrably # correct, cannot be fully validated outside a buildroot: doing so needs the # system ITK that only the build chroot has. So it prints what it finds and # lets the build proceed. Promote it back to enforcing once a green build has # shown a zero count here -- with evidence, not on the assumption that the # fix is right this time. # (Kept because the intent is sound: this is the shape of check that would # have caught the stale ctkAppLauncher ABI skew and the missing itkvnl/VXL # link, both of which reached built packages.) _tested=0 _loadfail=0 for _p in $(find "$_sroot/bin" -maxdepth 1 -type f -perm -u+x 2>/dev/null | sort | head -12); do _out="$("$_p" --help 2>&1 &2 echo "$_out" | head -3 >&2 _loadfail=$((_loadfail + 1)) ;; esac _tested=$((_tested + 1)) done echo "check: CLI executables exercised = $_tested, failed to load = $_loadfail (informational)" # 4. Undefined-symbol sweep over the shipped libraries. Reported, not enforced: # Qt/VTK plugins legitimately resolve some symbols only via their host # process, so a nonzero count here is not proof of breakage -- but a JUMP in # it is how the VTK 9.6->9.7 and CTK soname skews first became visible. if command -v ldd >/dev/null 2>&1; then _undef=0 for _l in $(find "$_slib" -maxdepth 1 -name 'libMRML*.so*' -o -maxdepth 1 -name 'libvtkSlicer*.so*' 2>/dev/null | head -25); do if LD_LIBRARY_PATH="$_sroot/%{_lib}:$_slib" ldd -r "$_l" 2>/dev/null | grep -q "undefined symbol"; then _undef=$((_undef + 1)) fi done echo "check: Slicer libs with undefined symbols = $_undef (informational)" fi [ "$_fail" -eq 0 ] || { echo "%%check failed: see ERROR lines above" >&2; exit 1; } echo "check: all assertions passed" %files %license License.txt %doc README.md %{_datadir}/applications/org.slicer.SlicerPreview.desktop %{_metainfodir}/org.slicer.SlicerPreview.metainfo.xml %{_datadir}/icons/hicolor/*/apps/slicer-preview.png %{_bindir}/slicer-preview %{_sysconfdir}/profile.d/slicer-preview.sh %{slicer_inst}/ %exclude %{slicer_inst}/include/ # Slicer's CMake config installs under lib/Slicer-%%{slicer_xy}/CMake/ (NOT lib/cmake/); # route it to -devel. The old lib/cmake/ path matched nothing, so %%files aborted with # "Directory not found" — this is why slicer-preview never packaged. (3dslicer bug #7.) %exclude %{slicer_inst}/lib/Slicer-%{slicer_xy}/CMake/ %files designer %{_libdir}/qt6/plugins/designer/libqMRMLWidgetsPlugins.so %{_libdir}/qt6/plugins/designer/libqSlicer*Plugins.so %{_libdir}/qt6/plugins/iconengines/libqSlicerIconEnginePlugin.so %{_libdir}/qt6/plugins/styles/libqSlicerBaseQTGUIStylePlugins.so %files devel %{slicer_inst}/include/ %{slicer_inst}/lib/Slicer-%{slicer_xy}/CMake/ %changelog * Mon Oct 05 2026 Morgan Hough - 5.13.0~preview.20260914git4eafce4f-21 - Requires: python3-simpleitk and python-unversioned-command. Two runtime failures found by framework in an offscreen launch of the served f44 set, both fixed by installing the package and relaunching: scripted modules that import SimpleITK failed with "No module named 'SimpleITK'" (nothing required it), and Python CLI modules failed with "Failed to find python interpreter for CLI" because Slicer's findPython() wants python-real or python on PATH and Fedora has only python3. - Functional change (Requires only); rebuild after ITK 5.4.7-23 publishes so this build also sees the headers with the legacy API compiled in again. * Mon Oct 05 2026 Morgan Hough - 5.13.0~preview.20260914git4eafce4f-20 - Pin the exact DCMTK snapshot: Requires: dcmtk(abi) = %%{dcmtk_abi}, from dcmtk-devel's macros.dcmtk, plus a %%build guard against the UNSET placeholder. dcmtk is now packaged from git HEAD (policy in dcmtk.spec) and upstream's DCMTK_ABI_VERSION stays 20 across snapshots, so rpm cannot tell snapshots apart by soname; this is the same mechanism as vtk(abi). - This release exists to be rebuilt against the snapshot; the pin means it cannot be installed against a different one. * Tue Sep 29 2026 Morgan Hough - 5.13.0~preview.20260914git4eafce4f-19 - Desktop integration, which upstream ships none of: a .desktop entry, AppStream metainfo, and the five upstream icons installed into hicolor. Before this the largest application in the repo had no menu entry, no icon in any theme, no software-centre listing and no file association; it could only be started by typing its name. Pattern taken from Ankur Sinha's python-fsleyes, recorded in docs/desktop-integration-pattern.md: install and VALIDATE in %install, with desktop-file-install and appstream-util, so a malformed file fails the build rather than being silently ignored by the desktop. - Icon sizes were measured, not assumed: the upstream Small/Medium/Large/XLarge/ XXLarge directories are 16, 32, 48, 64 and 128 pixels, a clean hicolor ladder. The install loop asserts all five landed, because a renamed upstream directory would otherwise leave Icon= resolving to nothing, which looks like a blank tile and like nothing at all in the build log. - Component id org.slicer.SlicerPreview, icon name slicer-preview -- distinct from the stable 3dslicer package's org.slicer.Slicer/slicer, because the two tracks are co-installable and would otherwise collide on the desktop file, the metainfo file and every icon size. The artwork is the same as stable's; only the names differ. - Exec carries %F because Slicer really does load a file given on the command line. That took two tries to establish: the first probe used --python-script and measured zero, because handleCommandLineArguments only consults the file argument when pythonScript() AND extraPythonScript() are both empty (qSlicerCoreApplication.cxx:1243). The probe disabled the thing it was probing. Re-measured with --python-code: a positional .nrrd loads. - MimeType declares application/x-mrml and application/x-mrb among others, so Requires: neuro-mime-types is hard -- a MimeType naming a type nothing defines is inert, and a default application pointing at it is silently ignored. - No StartupWMClass. Slicer under Xvfb never mapped a window in 200 seconds, so it could not be measured, and a plausible-but-wrong value fails silently while looking fixed. It is also the wrong mechanism: StartupWMClass is X11-only and Fedora defaults to Wayland, where only QGuiApplication::setDesktopFileName works -- which Slicer never calls (0 occurrences in 5.12.3). Filed as the one item we cannot fix downstream in docs/upstream/slicer-no-desktop-integration.md. * Sun Sep 27 2026 Morgan Hough - %{preview_ver}-18 - Split the Qt Designer / iconengine / style plugins into a slicer-preview-designer subpackage. These are the one part of Slicer that does not live under its own prefix: SlicerCPack installs them into the SYSTEM Qt6 plugin tree (this spec's bug #6), and both Slicer tracks build the same 20 filenames there -- 18 designer plugins plus one iconengine and one style. That made 3dslicer and slicer-preview mutually exclusive, contradicting the two-track design they are packaged for. - Measured on f44-x86_64 as well as f45 before changing anything, so this was never an f45 artefact: co-installing the two reported exactly 20 file conflicts and nothing else, which is why moving these 20 files fully resolves it. - The subpackages Conflicts with each other rather than the applications doing so. One shared Qt6 plugin directory can only describe one Slicer's widgets, so that exclusion is real -- but it now applies to Designer integration alone, which nothing needs at runtime, instead of to the applications. - Versioning the directory was considered and rejected: Qt Designer scans only its standard plugin path, so a versioned directory would be tidy and inert. * Sun Sep 27 2026 Morgan Hough - %{preview_ver}-17 - Rebuild on f45-x86_64 for viskores 1.2.0. -16 linked the libviskores_*-1.1.so.1.1 sonames; viskores 1.2.0-1 renamed them to libviskores_cont.so.1.2 and COPR's auto_prune removed 1.1.1-1, so the published package became uninstallable on that chroot. vtk 9.7.0-7 (11042114) is verified fixed there -- 12 requires on the 1.2 spelling, zero on 1.1, and vtk-devel fetches -- so this rebuild can link against it. No source change. * Tue Sep 22 2026 Morgan Hough - %{preview_ver}-16 - Same %%check fix as 3dslicer 5.12.4-3: select the launcher settings file by content (most [LibraryPaths] entries) rather than by find order, which is filesystem-dependent and picked the 1-path Qt Designer launcher in the COPR buildroot. This also repairs the enforcing [LibraryPaths] assertion, which had been validating the wrong file. * Mon Sep 21 2026 Morgan Hough - %{preview_ver}-15 - Same %%check fix as 3dslicer 5.12.4-2: derive LD_LIBRARY_PATH from the launcher's [LibraryPaths] instead of hand-listing two directories, and demote the CLI-load test to report-only. The hand-listed path omitted qt-loadable-modules and so false-failed builds 11009171 and 11009172 after ~7 h each against a correct install tree. * Sat Sep 19 2026 Morgan Hough - %{preview_ver}-14 - Drop static archives from the runtime package, as 3dslicer 5.12.3-16 does. No hierarchy/ move: slicer-preview does not ship those files. * Fri Sep 18 2026 Morgan Hough - %{preview_ver}-13 - Mirror 3dslicer 5.12.3-15: enforcing %%check (cli-modules symlinks resolve, %%{_lib} present in [LibraryPaths], CLI executables load, undefined-symbol sweep reported), ExcludeArch: %%{ix86}, and rmdir of the empty krb5 stub directory. Checks skip on absent inputs and fail only on positive evidence. * Fri Sep 18 2026 Morgan Hough - %{preview_ver}-12 - Unbundle OpenSSL: set Slicer_USE_SYSTEM_OpenSSL=ON, same as 3dslicer 5.12.3-14. SlicerBlockInstallOpenSSL.cmake wildcard-copies %%{_libdir}/libcrypto* and libssl* into %%{slicer_inst}, which swept in both OpenSSL runtimes and NSS's libssl3.so, and the launcher then preferred those frozen copies over the system libraries. Drops the bundled() declarations, keeps the Provides filter as defence in depth, and adds the same %%install guard. * Fri Sep 18 2026 Morgan Hough - %{preview_ver}-11 - Add BuildRequires: openssl3-libs on F45+, same reason as 3dslicer 5.12.3-13: Fedora's thrift is still linked against OpenSSL 3 while F45's OpenSSL is 4.x, and a soname-based dependency can be satisfied by a Slicer that only bundles the library under %%{slicer_inst}, where the linker will not find it. * Fri Sep 18 2026 Morgan Hough - %{preview_ver}-10 - Add slicer-vtkteem-itkvnl-system.patch: link ${ITKVNL_LIBRARIES} in Libs/vtkTeem rather than the hardcoded "itkvnl", which exists only when ITK bundles VXL. Against InsightToolkit5 5.4.7-8 the link fails with "cannot find -litkvnl". Found by the t2 session in round 5 of the ITK consumer test rebuild. - The variable is already in scope: vtkTeem requests ITKVNL as a find_package component and ITK sets ITKVNL_LIBRARIES to the bundled or system names as appropriate, so this is correct in both configurations and upstreamable rather than a downstream workaround. Fourth package with this same bug. * Wed Sep 16 2026 Morgan Hough - 5.13.0~preview.20260709git98d8e3af-9 - Filter the bundled OpenSSL/NSS Provides (libcrypto.so.3/.4, libssl.so.3/.4, libssl3.so), the same leak fixed in 3dslicer 5.12.3-10: without it this package poses as a system provider of those sonames and gets pulled into unrelated F45 buildroots, where its two ITKs then collide on file conflicts. - Declare Provides: bundled(openssl) = 3.5.7, = 4.0.2 and bundled(nss). * Tue Sep 15 2026 Morgan Hough - 5.13.0~preview.20260709git98d8e3af-8 - Add a real CTK floor: Requires ctk >= %%{ctk_ver}, from ctk-devel's new macros.ctk, guarded in %%build so it can never ship as ">= UNSET". - Why: CTK's sonames are frozen at .so.0.1 across every snapshot, so the automatic soname deps rpm generates are satisfied by a CTK built months earlier and dnf never upgrades it. A peer session found a host running 3dslicer-5.12.3-8 against the January fd37b874 CTK with 206 of 246 Slicer libraries carrying unresolved symbols -- ctkErrorLog*MessageHandler ctors that exist only in the June snapshot, plus vtkImplicitArray instantiations from a ctk-vtk built against VTK 9.6. Nothing failed at install time. * Sun Sep 13 2026 Morgan Hough - 5.13.0~preview.20260709git98d8e3af-6 - Rebuild to ship the vtk(abi) pin already in the spec. Published RPMs predate it (x86_64 -4, aarch64 -5 with a "vtk >= 9.6.2" floor, i.e. built against VTK 9.6.2). Bumped because the same NVR would not supersede -5. * Sun Aug 23 2026 Morgan Hough - 5.13.0~preview.20260709git98d8e3af-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. - Add slicer-vtk97-obbtree-tolerance.patch: VTK 9.7 removed vtkLocator::GetTolerance(), which vtkProjectMarkupsCurvePointsFilter read off the OBB tree. Nothing ever set it, so the documented default 0.001 is exactly what the call returned. - Add slicer-vtk97-update-bool.patch: VTK 9.7 changed vtkAlgorithm::Update() from void to bool, so vtkITKImageThresholdCalculator's override no longer matched ('conflicting return type'). Guarded on VTK_VERSION_NUMBER so the file still builds against 9.6. * Thu Jul 16 2026 Morgan Hough - 5.13.0~preview.20260709git98d8e3af-4 - Compile N4ITKBiasFieldCorrection.cxx at -O1: at -O2 this single ITK-template-heavy CLI source peaks ~22 GB and OOMs memory-constrained builders / CTest farm nodes (lowering -j does not help -- it is one TU). N4 is not perf-critical. * Sun Jul 12 2026 Morgan Hough - 5.13.0~preview.20260709git98d8e3af-3 - Add Requires: python3-scipy (spec sets Slicer_USE_SCIPY=ON but never required it; real runtime defect -- scipy-importing scripted modules fail). See 3dslicer. - %%prep: migrate SlicerAppTesting.py pipes.quote -> shlex.quote ('pipes' removed in Python 3.13; F44 ships 3.14). * Sun Jul 12 2026 Morgan Hough - 5.13.0~preview.20260709git98d8e3af-2 - Fix %%files: slicer-preview has NEVER packaged because it listed %%{slicer_inst}/lib/cmake/, a path Slicer does not create — %%files aborted with "Directory not found" AFTER a full successful compile (2846/2846). Slicer installs its CMake config under lib/Slicer-5.13/CMake/. Port 3dslicer's proven layout: correct the CMake dir (bug #7) and package the Qt6 designer/iconengine/style plugins that install into the system Qt6 tree (bug #6). Add %%global slicer_xy 5.13. * Fri Jul 10 2026 Morgan Hough - 5.13.0~preview.20260709git98d8e3af-1 - Refresh main-branch snapshot to 98d8e3af (2026-07-09), 24 commits past the prior 9774a684 (2026-06-28) pin, per the bump policy. %prep (Patch0 + VTK link injection + vtkWin32 fix + all seds) verified clean against the new tree. Carries the python3-vtkAddon Requires + testing bcond forward. * Fri Jul 10 2026 Morgan Hough - 5.13.0~preview.20260628git9774a684-8 - Add python3-vtkAddon (+ vtkAddon) runtime Requires: Slicer's bin/Python/vtkAddon.py does "from vtkAddonPython import *", which ships in python3-vtkAddon, not vtkAddon-devel — without it, scripted modules fail at runtime with "No module named 'vtkAddonPython'". Caught by the CTest farm. - Add `%%bcond_with testing` + guard `_smp_mflags -j1` behind `%%{without testing}` so the CDash farm can build this exact Fedora config with BUILD_TESTING=ON at full -j; shipped RPM is unchanged (tests off, -j1). - %%prep: drop the unconditional Windows-only vtkWin32OutputWindow.h include the test-driver macro injects (breaks all module CxxTest drivers on Linux with system VTK 9.6 when BUILD_TESTING=ON). * Wed Jul 08 2026 Morgan Hough - 5.13.0~preview.20260628git9774a684-7 - Port the %install shebang-normalization block from 3dslicer.spec. -6 compiled fully (2845/2845, Patch0 python314 fix confirmed working) but died in %install: brp-mangle-shebangs rejected the ambiguous "#!/usr/bin/env python" in libexec/Slicer-5.13/ExtensionWizard.py. Normalize every non-system python shebang under %{slicer_inst} to /usr/bin/python3 (cosmetic; Slicer uses its own launcher). Also picks up ctk 0.18 (import ctk fix) as the repo's current ctk. * Tue Jul 07 2026 Morgan Hough - 5.13.0~preview.20260628git9774a684-6 - Add Patch0 (slicer-python314-interpreter-config.patch): the -5 parity port ported the seds + cmake hints but missed the two actual .patch files. -5 FTBFS on F44 at ninja 351/2845 compiling qSlicerCoreApplication.cxx: '_PyInterpreterState_GetConfigCopy'/'_PyInterpreterState_SetConfig' not declared (Python 3.14 removed these private APIs). Patch rewrites the argv refresh via the stable PySys_SetObject path. Same fix 3dslicer carries. * Wed Jul 01 2026 Morgan Hough - 5.13.0~preview.20260628git9774a684-5 - Full parity port of the stable 3dslicer system-libs build recipe (this preview spec had never built — -1..-4 all FTBFS on missing build machinery): * BuildRequires qttesting-devel, python-pythonqt-devel, cli11-devel. * cmake hints: QtTesting_DIR, PythonQt_DIR, CTKAppLauncherLib_DIR, vtkAddon_DIR + vtkAddon_CMAKE_DIR, SlicerExecutionModel_DIR, GenerateCLP_DIR, ModuleDescriptionParser_DIR, Teem_DIR (shim), Qt6_DIR. * %prep source-bug fixes: SlicerBlockInstallTBB short-circuit, modular-VTK link_libraries injection, GLOB-alternation rewrites, itkNamespace.h stubs, configure_header_file / qSlicerModuleGenericTest / SlicerCPack install seds. * CXXFLAGS -I gdcm/QtTesting/PythonQt; teem-shim for Fedora TeemConfig bug. * LTO off + -j1 (submit with --timeout 86400) to avoid the step-~705 swap-stall. * Requires python3-dicomweb-client/highdicom/legacy-cgi; python-real exclude. * Mon Jun 29 2026 Morgan Hough - 5.13.0~preview.20260628git9774a684-4 - Slicer_USE_SYSTEM_OpenJPEG=ON to fix the 5.12+ CPack OpenJPEG configure fatal (see 3dslicer 5.12.0-3). * Sun Jun 28 2026 Morgan Hough - 5.13.0~preview.20260628git9774a684-3 - Pre-define Slicer_REVISION (=shortcommit) + WC date/URL. With STORE_SETTINGS=OFF the user-home settings/extensions paths are revision-keyed, so a distinct revision is what keeps preview isolated from stable 3dslicer in parallel installs; an empty revision also breaks the Extensions Manager's by-revision server query. 3dslicer already sets this. * Sun Jun 28 2026 Morgan Hough - 5.13.0~preview.20260628git9774a684-2 - Slicer_STORE_SETTINGS_IN_APPLICATION_HOME_DIR=OFF so user settings + extensions land in the user's home, not the read-only install tree (Extensions Manager fix; see 3dslicer 5.12.0-2). * Sun Jun 28 2026 Morgan Hough - 5.13.0~preview.20260628git9774a684-1 - Refresh the dev/preview track from the 5.11.0~preview snapshot to the current 5.13.0 main HEAD (Slicer's dev version; even minor 5.12 is now stable in 3dslicer). Parallel-installable with stable 3dslicer (separate /usr/lib/slicer-preview root + /usr/bin/slicer-preview launcher). * Wed May 27 2026 Morgan Hough - 5.11.0~preview.20260517git208adb86-1 - Initial preview-channel package tracking Slicer main branch. - Same system-deps strategy as the stable 3dslicer package (5.10.0). - Coexists via parallel-install at /usr/lib/slicer-preview/ ; invoked as `slicer-preview` to keep `slicer` pointing at stable.