# SlicerJupyter — Jupyter kernel that runs Slicer's Python interpreter # inside a notebook. Critical for educational / clinical-research use: # clinicians and educators can build reproducible notebooks that drive # Slicer's processing pipeline. %global commit d89a282d %global longcommit d89a282d54d7249fce07f62320779f82c5eb4604 %global slicer_inst %{_prefix}/lib/slicer %global slicer_subdir Slicer-5.12 %global snapdate 20260420 Name: 3dslicer-jupyter Version: 0 Release: 0.5.%{snapdate}git%{commit}%{?dist} Summary: Jupyter kernel for 3D Slicer (run Slicer Python from notebooks) License: BSD-3-Clause URL: https://github.com/Slicer/SlicerJupyter Source0: %{url}/archive/%{commit}/SlicerJupyter-%{commit}.tar.gz BuildRequires: cmake >= 3.20 BuildRequires: ninja-build BuildRequires: gcc-c++ BuildRequires: 3dslicer-devel >= 5.10.0 BuildRequires: qt6-qtbase-devel BuildRequires: zeromq-devel # Canonical Slicer-extension buildroot, grafted from 3dslicer-dmri.spec. # This spec predates the 5.12 retarget and was never built, so it carried # only a handful of BuildRequires -- find_package(Slicer) then failed inside # SlicerConfig.cmake:672 on find_package(RapidJSON), the first of ~38 missing. # 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: teem-devel BuildRequires: python3-devel BuildRequires: SlicerExecutionModel-devel BuildRequires: rapidjson-devel BuildRequires: libcurl-devel BuildRequires: jsoncpp-devel BuildRequires: xeus-devel BuildRequires: json-devel # JupyterKernel/CMakeLists.txt:7-10 requires all four unconditionally. # xeus-python is packaged in this COPR (upstream's superbuild otherwise clones # it at configure time, which mock/COPR cannot do). BuildRequires: xeus-python-devel BuildRequires: xeus-zmq-devel BuildRequires: cppzmq-devel BuildRequires: pybind11-devel BuildRequires: pybind11-json-devel # Transitive deps that the xeus cmake configs find_dependency() but the -devel # packages do not Requires: -- xeusConfig.cmake:66 LibUUID, # xeus-zmqConfig.cmake:61 OpenSSL. BuildRequires: libuuid-devel BuildRequires: openssl-devel # Slicer's UseSlicer.cmake -> SlicerBlockAdditionalLauncherSettings.cmake:69 # includes ${CTKAppLauncher_DIR}/CMake/ctkAppLauncher.cmake, which only exists # from commontk-applauncher 0.9 onwards. BuildRequires: commontk-applauncher-devel >= 0.1.34-0.9 Requires: 3dslicer >= 5.10.0 Requires: python3-jupyter Requires: python3-zmq Requires: python3-tornado %description SlicerJupyter installs a Jupyter kernel that hosts 3D Slicer's embedded Python interpreter. This lets users build reproducible analysis notebooks that drive Slicer's MRML scene, modules, and visualization from a familiar notebook UI — without needing the Slicer GUI to be open. Critical for: * Educational use (workshops, online courses, NA-MIC training) * Clinical-research workflows that need reproducible scripted pipelines * Remote-collaboration scenarios where notebooks travel between sites * CI/CD of imaging analyses (test notebooks via papermill/nbval) %prep %autosetup -p1 -n SlicerJupyter-%{longcommit} # Distro build: install modules directly, not as an Extension-Manager bundle, so # skip SlicerExtensionCPack (SlicerExtensionCPack.cmake:52 fails on # EXTENSION_README_FILE, and it wants the s4ext description template the install # tree doesn't ship). Same treatment as the other extensions in this COPR. sed -i '/include(${Slicer_EXTENSION_CPACK})/d' CMakeLists.txt ! grep -q 'include(${Slicer_EXTENSION_CPACK})' CMakeLists.txt # ctk-devel's ctkAbstractPythonManager.h:31 does an unprefixed # #include # but the header lives in %{_includedir}/PythonQt/ and CTKConfig.cmake does not # export that directory in CTK_INCLUDE_DIRS. Only this extension trips it -- # it is the one that embeds the Python manager. The proper fix is in ctk-devel; # inject the include path here so SlicerJupyter can build meanwhile. sed -i '/^find_package(Slicer REQUIRED)/a include_directories(SYSTEM "%{_includedir}/PythonQt")' CMakeLists.txt grep -q 'include_directories(SYSTEM "%{_includedir}/PythonQt")' CMakeLists.txt # The JupyterKernel module uses the CPython C API through pybind11, and # xSlicerInterpreter.cxx calls PythonQt::self()/pythonStdErr(), but neither # libpython nor libPythonQt is on the link line -- so every Py* and # PythonQt::* symbol comes out undefined (PyProperty_Type, _PyType_Lookup, # PythonQt::staticMetaObject, ...). Upstream's superbuild picks both up from # Slicer's own build tree; SlicerConfig.cmake exports no PythonQt link target # for an installed Slicer. Link them explicitly -- SlicerApp already embeds the # same interpreter, so this does not pull in a second one. sed -i '/^find_package(Slicer REQUIRED)/a find_package(Python3 COMPONENTS Development REQUIRED)\nlink_libraries(Python3::Python PythonQt)' CMakeLists.txt grep -q 'link_libraries(Python3::Python PythonQt)' CMakeLists.txt # CMakeLists.txt:42 does # install(DIRECTORY "${python_packages_DIR}/" DESTINATION ...) # but python_packages_DIR is only ever set by the superbuild # (SuperBuild/External_python-packages.cmake), which we turn off. Unset, the # argument collapses to "/" and cmake walks the entire root filesystem into the # buildroot -- the install died on /var/lib/selinux/targeted/semanage.read.LOCK # after copying much of /var. Those pip-bootstrapped packages are supplied as # RPM Requires (python3-jupyter, python3-zmq, python3-tornado) instead, so drop # the block entirely. sed -i '/install(DIRECTORY "${python_packages_DIR}\/"/,/COMPONENT RuntimeLibraries)/d' CMakeLists.txt ! grep -q 'python_packages_DIR' CMakeLists.txt %build %cmake -G Ninja \ `# SlicerJupyter_SUPERBUILD defaults to ON (CMakeLists.txt:29) and then` \ `# ExternalProject_Add()s xeus and nlohmann_json from git at configure time --` \ `# no network in mock/COPR, so it died in` \ `# ExternalProject/shared_internal_commands.cmake:744. Both are packaged:` \ `# xeus-devel 5.2.6 and Fedora's json-devel (nlohmann_json 3.12.0), so build` \ `# against the system copies instead.` \ -DSlicerJupyter_SUPERBUILD:BOOL=OFF \ -Dxeus_DIR:PATH=%{_libdir}/cmake/xeus \ -Dnlohmann_json_DIR:PATH=%{_datadir}/cmake/nlohmann_json \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_TESTING:BOOL=OFF \ `# Was %{_prefix}/lib/slicer-5.10.0/lib/cmake/Slicer -- a Slicer 5.10 layout` \ `# that has not existed since the 5.12 retarget. This spec was written then` \ `# and never built, so nothing ever caught it.` \ -DSlicer_DIR:PATH=%{slicer_inst}/lib/%{slicer_subdir} \ -DCMAKE_SKIP_INSTALL_RPATH:BOOL=ON \ `# Was %{_prefix}/lib/slicer-5.10.0 -- same dead 5.10 layout as Slicer_DIR` \ `# above. Every green extension installs into %{slicer_inst}.` \ -DCMAKE_INSTALL_PREFIX:PATH=%{slicer_inst} %cmake_build %install %cmake_install # Catch-all file list (files + symlinks only, so the shared module dirs that # 3dslicer owns are never claimed) -- same approach as the other extensions. ( cd %{buildroot} && find . \( -type f -o -type l \) | sed 's|^\.||' ) \ > %{_builddir}/jupyter.files %files -f %{_builddir}/jupyter.files %license LICENSE %doc README.md %changelog * Mon Aug 03 2026 Morgan Hough - 0-0.4.20260420gitd89a282d - Add the five BuildRequires the -0.3 graft missed (python3-devel, SlicerExecutionModel-devel, rapidjson-devel, libcurl-devel, jsoncpp-devel). The extraction regex I used captured only the contiguous Qt6..teem-devel block, so the entries outside it were silently dropped -- find_package(Slicer) then still failed at SlicerConfig.cmake:672 on RapidJSON. Now verified by diffing the full BuildRequires set against 3dslicer-dmri.spec: 0 differences. * Sun Aug 02 2026 Morgan Hough - 0-0.3.20260420gitd89a282d - Graft the canonical Slicer-extension buildroot (~34 BuildRequires) from 3dslicer-dmri.spec. This spec predates the 5.12 retarget and was never built, so it carried only a handful; find_package(Slicer) then failed inside SlicerConfig.cmake:672 on find_package(RapidJSON) -- the first of the missing set, which also covers the full Qt6 component list, ITK/VTK/CTK/DCMTK devel, SlicerExecutionModel, PythonQt, qRestAPI, teem and vtkAddon. * Sun Aug 02 2026 Morgan Hough - 0-0.2.20260420gitd89a282d - Point Slicer_DIR at the 5.12 layout: it was %%{_prefix}/lib/slicer-5.10.0/lib/ cmake/Slicer, a path that has not existed since the 5.12 retarget. The spec was written then and never built, so nothing caught it. * Wed May 27 2026 Morgan Hough - 0-0.1.20260420gitd89a282d - Initial COPR package tracking SlicerJupyter master. - Jupyter kernel runs Slicer's embedded Python; pairs with Fedora's python3-jupyter / Jupyter Lab on the user side.