# MNE-CPP has no formal releases since v0.1.9 (2021). # Use a recent git snapshot from main branch. %global commit 1daca518d5375aa40be156ef5f9b1f4396a8bee5 %global shortcommit %(c=%{commit}; echo ${c:0:7}) %global snapdate 20260304 %define debug_package %{nil} Name: mne-cpp Version: 0.1.9^%{snapdate}git%{shortcommit} Release: 9%{?dist} Summary: Cross-platform C++ framework for MEG/EEG data analysis License: BSD-3-Clause URL: https://mne-cpp.github.io/ Source0: https://github.com/mne-tools/mne-cpp/archive/%{commit}/mne-cpp-%{shortcommit}.tar.gz Source1: mne-analyze.desktop Source2: mne-analyze.metainfo.xml # mne_anonymize dereferences FiffTag::toInt() without checking it for null, # and SIGSEGVs on a real MEG recording. See docs/upstream/. Patch0: mne-cpp-anonymize-null-toint-deref.patch # Upstream lacks cmake install() targets and SOVERSION on shared libraries. # We inject both via sed in prep. BuildRequires: gcc-c++ BuildRequires: cmake >= 3.15 BuildRequires: ninja-build BuildRequires: eigen3-devel BuildRequires: fftw-devel # Qt6 modules used across libraries and applications BuildRequires: qt6-qtbase-devel BuildRequires: qt6-qtcharts-devel BuildRequires: qt6-qt3d-devel BuildRequires: qt6-qtsvg-devel BuildRequires: qt6-qtdeclarative-devel BuildRequires: qt6-qtshadertools-devel # Qt6 private headers needed for disp3D_rhi (Qt RHI-based 3D rendering) BuildRequires: qt6-qtbase-private-devel BuildRequires: /usr/bin/desktop-file-install BuildRequires: /usr/bin/appstream-util %description MNE-CPP is a cross-platform, open-source C++ framework for MEG/EEG data acquisition and analysis. It includes libraries for FIFF I/O, forward and inverse modeling, connectivity analysis, real-time processing, and 3D visualization. Applications include MNE Scan (real-time acquisition), MNE Analyze (offline analysis), and various command-line utilities. %package devel Summary: Development files for %{name} Requires: %{name}%{?_isa} = %{version}-%{release} Requires: qt6-qtbase-devel Requires: eigen3-devel Requires: hicolor-icon-theme %description devel Header files and cmake config for developing against the MNE-CPP libraries. %prep # GitHub archives extract to -/, not -/ %autosetup -n mne-cpp-%{commit} -p1 # %%autosetup reports a hunk that does not match as "ignored" and does NOT # fail the build, so verify Patch0 by its effect. simnibs 4.6.0-11 shipped # green with its only patch silently skipped; this is the same shape. _fa=src/applications/mne_anonymize/fiffanonymizer.cpp grep -q 'tagIntOr' "$_fa" || \ { echo "ERROR: Patch0 did not apply to $_fa" >&2; exit 1; } if grep -n '\*m_pTag->toInt()' "$_fa"; then echo "ERROR: Patch0 left an unguarded toInt() dereference in $_fa" >&2; exit 1 fi # Remove bundled Eigen install (uses system eigen3-devel at build time, # but Eigen headers would be installed to /usr/include/Eigen/ conflicting with system) # Note: source bundles Eigen 5.0.0 which we keep for building (system eigen3 3.4 works # for includes but the external/ copy is referenced by cmake). We just prevent install. # The upstream cmake outputs everything to out/Release/ and has no install() # targets. We override the output directories to use the cmake binary dir, # and add install() calls for libraries, headers, and applications. # 1. Override BINARY_OUTPUT_DIRECTORY to use CMAKE_BINARY_DIR sed -i 's|set(BINARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../out/${CMAKE_BUILD_TYPE})|set(BINARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out)|' \ src/CMakeLists.txt # 1b. Patch FFTW to use system library instead of bundled copy. # ~50 cmake files across libraries, applications, and plugins all use # ${FFTW_DIR_LIBS}/lib/libfftw3.so (bundled layout). Replace with system # library name 'fftw3' (cmake will find -lfftw3 via system linker). # Also fix include paths: bundled uses /api/ subdir, system uses /usr/include/. find src/ -name CMakeLists.txt -exec \ sed -i 's|set(FFTW_LIBS ${FFTW_DIR_LIBS}/lib/libfftw3.so)|set(FFTW_LIBS fftw3)|' {} + find src/ -name CMakeLists.txt -exec \ sed -i 's|target_include_directories(${PROJECT_NAME} PRIVATE ${FFTW_DIR_INCLUDE}/api)|# system fftw3 headers in standard include path|' {} + # 2. Fix the git hash commands that fail on non-git source dirs # Delete both execute_process blocks (lines 155-164 pattern) sed -i '/^execute_process($/,/OUTPUT_STRIP_TRAILING_WHITESPACE)$/d' \ src/CMakeLists.txt # 3. Remove the symlink-to-resources block entirely (lines 196-end) # This block uses cmake_path and execute_process to create symlinks # that reference source-tree paths not available at runtime. sed -i '/^##.*Add symbolic links to project resources/,$d' \ src/CMakeLists.txt # 4. Inject SOVERSION into each library CMakeLists.txt for libcml in src/libraries/*/CMakeLists.txt; do libname=$(basename $(dirname "$libcml")) # Add VERSION/SOVERSION after the add_library call if grep -q 'add_library' "$libcml"; then sed -i "/^[[:space:]]*target_compile_definitions/i\\ set_target_properties(\${TARGET_NAME} PROPERTIES VERSION %{version} SOVERSION 2)" \ "$libcml" fi done # 5. The upstream library cmake files already have install() targets for # libraries and headers. We just need GNUInstallDirs (step 7) and # CMAKE_INSTALL_LIBDIR set so they install to the right locations. # 7. Add GNUInstallDirs to top-level cmake sed -i '/project(mne_cpp/a include(GNUInstallDirs)' src/CMakeLists.txt # 8. Inject install() for applications cat >> src/CMakeLists.txt << 'APP_EOF' # Fedora: install applications foreach(app IN ITEMS mne_scan mne_analyze mne_anonymize mne_browse mne_compute_mne mne_compute_raw_inverse mne_dipole_fit mne_edf2fiff mne_flash_bem mne_forward_solution mne_inspect mne_inverse_operator mne_make_source_space mne_process_raw mne_rt_server mne_setup_forward_model mne_setup_mri mne_show_fiff mne_surf2bem mne_watershed_bem) if(TARGET ${app}) install(TARGETS ${app} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) endif() endforeach() APP_EOF # 9. Fix C++14 to C++17 for GCC 15 compatibility sed -i 's/set(CMAKE_CXX_STANDARD 14)/set(CMAKE_CXX_STANDARD 17)/' \ src/CMakeLists.txt # 10. Point the GUI apps at an absolute, FHS plugin directory. # Upstream loads plugins from qApp->applicationDirPath()+"/mne_*_plugins", # which resolves to %{_bindir}/mne_*_plugins once installed and finds # nothing. Redirect both apps to %{_libdir}/%{name}/mne_*_plugins, where # the install section below drops the plugins. Fail prep loudly if it does not apply # (e.g. upstream refactors the call) so we never silently ship empty apps. _scan_mw=src/applications/mne_scan/mne_scan/mainwindow.cpp sed -i -E 's#qApp->applicationDirPath\(\)[[:space:]]*\+[[:space:]]*pluginDir#QStringLiteral("%{_libdir}/%{name}/mne_scan_plugins")#' \ "$_scan_mw" grep -q '%{_libdir}/%{name}/mne_scan_plugins' "$_scan_mw" || \ { echo "ERROR: mne_scan plugin-path patch did not apply to $_scan_mw" >&2; exit 1; } _anz_core=src/applications/mne_analyze/mne_analyze/analyzecore.cpp sed -i -E 's#qApp->applicationDirPath\(\)[[:space:]]*\+[[:space:]]*pluginsDir#QStringLiteral("%{_libdir}/%{name}/mne_analyze_plugins")#' \ "$_anz_core" grep -q '%{_libdir}/%{name}/mne_analyze_plugins' "$_anz_core" || \ { echo "ERROR: mne_analyze plugin-path patch did not apply to $_anz_core" >&2; exit 1; } # 10b. Same defect, third application: mne_rt_server. # loadConnectors(qApp->applicationDirPath()+connectorDir) resolves to # %{_bindir}/mne_rt_server_plugins, which does not exist, so the server # starts, LISTENS on its command port 4217 and its data port 4218, and # then answers nothing -- zero connectors were loaded. Measured: six # text commands (help, clist, both line endings, both ports), six-second # waits, zero bytes back every time. It looks alive from the outside, # which is why this survived a suite that already checked the binary # runs and links. _rts=src/applications/mne_rt_server/mne_rt_server/mne_rt_server.cpp sed -i -E 's#qApp->applicationDirPath\(\)[[:space:]]*\+[[:space:]]*connectorDir#QStringLiteral("%{_libdir}/%{name}/mne_rt_server_plugins")#' \ "$_rts" grep -q 'QStringLiteral("%{_libdir}/%{name}/mne_rt_server_plugins")' "$_rts" || \ { echo "ERROR: mne_rt_server connector-path patch did not apply to $_rts" >&2; exit 1; } # The fiffsimulator connector then reads its config through # applicationDirPath() + "/../resources/mne_rt_server/...", i.e. # /usr/resources/..., which is not a path Fedora has. Rewritten to # %{_datadir}/%{name}, where the install section puts it. # # Both of these replace the expression with a bare string literal, not # with QStringLiteral(...): the matched text sits inside an existing # call -- QFile file(EXPR) and .arg(EXPR) -- so adding an opening # parenthesis leaves it unbalanced and the file does not compile. That # was the first version of this patch. _rts_dir=src/applications/mne_rt_server/mne_rt_server sed -i -E 's#QCoreApplication::applicationDirPath\(\)[[:space:]]*\+[[:space:]]*"/\.\./resources/mne_rt_server#"%{_datadir}/%{name}/mne_rt_server#g' \ "$_rts_dir/main.cpp" sed -i -E 's#"%1/\.\./resources/mne_rt_server/plugins/"\+configFileName\)\.arg\(QCoreApplication::applicationDirPath\(\)\)#"%{_datadir}/%{name}/mne_rt_server/plugins/"+configFileName)#' \ "$_rts_dir/connectormanager.cpp" if grep -rn '\.\./resources/mne_rt_server' "$_rts_dir"/*.cpp; then echo "ERROR: mne_rt_server resource-path patch left a relative path behind" >&2; exit 1 fi %build export CXXFLAGS="%{optflags} -Wno-error -fpermissive -include cstdint -Wno-error=format-security" # Add disp3D_rhi source dir to includes — brainview.h does #include "core/viewstate.h" # which needs the disp3D_rhi root in the search path when included transitively export CXXFLAGS="$CXXFLAGS -I$(pwd)/src/libraries/disp3D_rhi" %cmake -S src -GNinja \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_SHARED_LIBS:BOOL=ON \ -DBUILD_APPLICATIONS:BOOL=ON \ -DBUILD_EXAMPLES:BOOL=OFF \ -DBUILD_TESTS:BOOL=OFF \ -DUSE_FFTW:BOOL=ON \ -DCMAKE_SKIP_INSTALL_RPATH:BOOL=ON \ -DCMAKE_SKIP_RPATH:BOOL=ON %cmake_build %install %cmake_install # Some libraries install to /usr/lib/ instead of /usr/lib64/ due to upstream # cmake install rules. Move any stray libs to the correct location. if [ -d %{buildroot}%{_prefix}/lib ] && [ "%{_prefix}/lib" != "%{_libdir}" ]; then mkdir -p %{buildroot}%{_libdir} for f in %{buildroot}%{_prefix}/lib/libmne_*.so*; do [ -e "$f" ] && mv "$f" %{buildroot}%{_libdir}/ done fi # Remove bundled Eigen headers — system eigen3-devel provides these rm -rf %{buildroot}%{_includedir}/Eigen rm -rf %{buildroot}%{_includedir}/unsupported rm -f %{buildroot}%{_includedir}/signature_of_eigen3_matrix_library # Remove stray resources dir installed next to the binaries rm -rf %{buildroot}%{_bindir}/resources # --- Install the mne_scan / mne_analyze plugins --------------------------- # Upstream builds these into /out/_plugins but ships no # install() rule, so the cmake-install step skips them and the GUI apps come # up empty. Harvest them from the build tree into the absolute FHS location the # prep-section source patch points at. Hard-fail if none are found so this gap # cannot silently return. for _fam in mne_scan mne_analyze mne_rt_server; do _pdir=$(find %{_vpath_builddir} ./out -type d -name "${_fam}_plugins" 2>/dev/null | head -1) if [ -n "$_pdir" ] && ls "$_pdir"/*.so >/dev/null 2>&1; then install -d %{buildroot}%{_libdir}/%{name}/${_fam}_plugins install -m 0755 "$_pdir"/*.so %{buildroot}%{_libdir}/%{name}/${_fam}_plugins/ echo "mne-cpp: installed $(ls "$_pdir"/*.so | wc -l) ${_fam} plugin(s) from $_pdir" else echo "ERROR: no built plugins found for ${_fam} (expected ${_fam}_plugins/*.so in the build tree)" >&2 exit 1 fi done # --- Install the shared libraries those plugins link against --------------- # The plugins are not self-contained: mne_analyze's link against libanShared.so and # mne_scan's against libscShared.so, libscDisp.so and libscMeas.so. Upstream builds # all four and, exactly as with the plugins above, ships no install() rule for them, # so cmake-install skips them. The result is that 25 plugins are packaged and the 4 # libraries they need are not, rpm generates Requires for them from DT_NEEDED with # nothing providing them, and the WHOLE mne-cpp package becomes uninstallable: # # package: mne-cpp-0.1.9^20260304git1daca51-5.fc44.x86_64 # unresolved deps: libanShared.so libscDisp.so libscMeas.so libscShared.so # # Found by repoclosure (ci/verify-installed-runtime.sh), not by the build, which is # perfectly happy -- the same shape as the plugin gap the block above fixes. # # Harvested rather than patched into upstream's cmake, because that is the established # approach in this spec. Installed into %{_libdir} rather than the private # %{_libdir}/%{name} beside the plugins: the plugins are dlopen'd, so their DT_NEEDED # entries resolve through the plugin's own RPATH or the global search path, and the # private directory is on neither. %{_libdir} also gets them auto-Provides, which is # what actually closes the repoclosure failure. Unversioned .so in %{_libdir} is # already this package's acknowledged compromise for libmne_*.so (see %files), so this # is consistent rather than a new exception. for _lib in libanShared libscShared libscDisp libscMeas; do _found=$(find %{_vpath_builddir} ./out ./lib -name "${_lib}.so*" -type f 2>/dev/null | head -1) if [ -n "$_found" ]; then install -m 0755 "$_found" %{buildroot}%{_libdir}/ echo "mne-cpp: installed ${_lib} from $_found" else # Print the inventory before failing. A build failure that does not say what # IS there costs another full round trip to learn the same thing. echo "ERROR: ${_lib}.so not found in the build tree." >&2 echo " Shared libraries that ARE present, for reference:" >&2 find %{_vpath_builddir} ./out ./lib -name 'lib*.so*' -type f 2>/dev/null \ | xargs -r -n1 basename | sort -u | sed 's/^/ /' >&2 exit 1 fi done # --- Ensure mne_forward_solution ships ------------------------------------ # It is in the injected install() foreach but target scope can miss it; copy # straight from the build tree as a fallback if the cmake-install step did not. if [ ! -x %{buildroot}%{_bindir}/mne_forward_solution ]; then _fbin=$(find %{_vpath_builddir} ./out -type f -name mne_forward_solution -perm -u+x 2>/dev/null | head -1) [ -n "$_fbin" ] && install -m 0755 "$_fbin" %{buildroot}%{_bindir}/mne_forward_solution fi # --- Runtime resources ---------------------------------------------------- # Copy from the SOURCE resources/ tree, which holds the real files: coil # definitions, 2D/3D layouts, selection groups, sensor surfaces, default # filters, HPI alignment data. # # This used to prefer a *built* resources tree under out/ and fall back to # source only when that was ABSENT. That shipped exactly one file -- # general/explanations/fiff_explanations.txt -- and silently lost everything # else, including coil_def.dat, which mne_forward_solution and dipole fitting # need. Two reasons it failed, and the old comment had them backwards: # 1. The symlinks were in out/, not in source. They were created by the # "Add symbolic links to project resources" CMake block that %%prep # deletes (see above), so after that deletion out/resources is sparse. # 2. The fallback tested for an EMPTY $_ressrc, not a sparse one, so finding # a near-empty out/resources counted as success and source was never used. # Verified at commit %%{shortcommit}: resources/general/coilDefinitions/ holds # coil_def.dat (38978 bytes) and coil_def_Elekta.dat (5462 bytes) as real files. mkdir -p %{buildroot}%{_datadir}/%{name} cp -a resources/general %{buildroot}%{_datadir}/%{name}/ # mne_rt_server's connector configs (FiffSimulation.cfg, fiffsimulator.json, # plugin.cfg). The prep section points the server at this path. cp -a resources/mne_rt_server %{buildroot}%{_datadir}/%{name}/ test -s %{buildroot}%{_datadir}/%{name}/mne_rt_server/plugins/fiffsimulator/FiffSimulation.cfg # --- Desktop integration ------------------------------------------------- # mne-cpp ships 18 binaries, three of which are Qt GUI applications, and had # no desktop entry, icon or MIME association at all: mne_analyze was # reachable only by typing its name in a terminal. # # Only mne_analyze gets an entry. mne_scan and mne_browse are also Qt GUIs, # but `--help` on either HANGS rather than printing anything, so their # command-line contract is unverified and a menu entry for them would be a # guess. Recorded in docs/upstream/ instead. # # Exec uses `--file %%f`, not a bare %%F: mne_analyze takes the path through # a --file option (`mne_analyze --help` confirms) and ignores a positional # argument, so a plain %%F entry would open the application empty and look # like the file failed to load. desktop-file-install --dir=%{buildroot}%{_datadir}/applications %{SOURCE1} install -Dpm 0644 resources/design/logos/MNE-CPP_Logo.svg \ %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/mne-cpp.svg install -Dpm 0644 %{SOURCE2} %{buildroot}%{_metainfodir}/mne-analyze.metainfo.xml appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/mne-analyze.metainfo.xml # drop any dangling symlinks that survived the copy find %{buildroot}%{_datadir}/%{name} -xtype l -delete # Fail the build rather than ship a package whose forward modelling is broken. test -s %{buildroot}%{_datadir}/%{name}/general/coilDefinitions/coil_def.dat %ldconfig_scriptlets %check desktop-file-validate %{buildroot}%{_datadir}/applications/mne-analyze.desktop # The icon named by Icon= must exist, or the menu entry is a blank tile. test -f %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/mne-cpp.svg # And the Exec must name a binary this package actually installs. test -x %{buildroot}%{_bindir}/mne_analyze %files %license LICENSE %doc README.md %{_bindir}/mne_* # Libraries install without SOVERSION (upstream lacks VERSION/SOVERSION on all targets; # our injection only partially works). For now, ship unversioned .so in main package. %{_libdir}/libmne_*.so # The four application-shared libraries the plugins link against; upstream builds them # and installs none of them, which made every build of this package uninstallable. %{_libdir}/libanShared.so %{_libdir}/libscShared.so %{_libdir}/libscDisp.so %{_libdir}/libscMeas.so # mne_scan / mne_analyze plugins (installed to an absolute FHS path; the GUI # apps are patched in the prep section to load from here) %dir %{_libdir}/%{name} %{_libdir}/%{name}/mne_scan_plugins/ %{_libdir}/%{name}/mne_analyze_plugins/ %{_libdir}/%{name}/mne_rt_server_plugins/ %{_datadir}/%{name}/ %{_datadir}/applications/mne-analyze.desktop %{_datadir}/icons/hicolor/scalable/apps/mne-cpp.svg %{_metainfodir}/mne-analyze.metainfo.xml %files devel # Upstream installs headers under include/mne_/ %{_includedir}/mne_*/ %changelog * Sun Oct 04 2026 Morgan Hough - 0.1.9^20260304git1daca51-9 - mne_rt_server was shipping non-functional and looked fine. It starts, binds command port 4217 and data port 4218, accepts connections -- and answers nothing: six text commands across both ports with six-second waits returned zero bytes every time. Cause is the defect already fixed here twice for mne_scan and mne_analyze: loadConnectors() resolves qApp->applicationDirPath()+"/mne_rt_server_plugins" to %%{_bindir}/mne_rt_server_plugins, which does not exist, so no connector loads and there is nothing behind the port. Upstream's fiffsimulator connector was built into out/mne_rt_server_plugins and thrown away at install time. Redirected to %%{_libdir}/%%{name}/mne_rt_server_plugins and added mne_rt_server to the plugin harvest loop. - Patch0: stop mne_anonymize dereferencing a null FiffTag::toInt(). On ds000117 sub-01 run-01 (860 MB) it SIGSEGVs in censorTag() and leaves a 6.8 MB truncated output, which is the shape of a successful run if you only look at the exit status and at whether a file appeared. gdb puts the fault at `call FiffTag::toInt()` returning 0 in %%rax followed by `movslq (%%rax),%%rsi`. toInt() returns (qint32*)data(), and data() is null for a tag carrying none -- no diagnostic is printed on that path, so the "Expected an integer tag" message never appears. All seven call sites in censorTag() dereferenced it unchecked; all seven now take the anonymised default instead. - %%autosetup gains -p1, without which Patch0 would be applied at strip 0, reported as "ignored", and skipped silently. - Ship resources/mne_rt_server. The connector reads FiffSimulation.cfg through applicationDirPath()+"/../resources/...", i.e. /usr/resources, which is not a path Fedora has; rewritten to %%{_datadir}/%%{name}. Replaced with a bare string literal rather than QStringLiteral(...): the expression sits inside QFile file(...) and .arg(...), so wrapping it adds an unbalanced parenthesis. The first version of this patch did exactly that and would not have compiled. * Thu Oct 01 2026 Morgan Hough - 0.1.9^20260304git1daca51-8 - Desktop integration for mne_analyze, which had none: 18 binaries, three of them Qt GUI applications, and no desktop entry, icon or file association, so the only way to start it was to type its name. - Exec is "mne_analyze --file %%f", not a bare %%F. mne_analyze takes the path through a --file option and ignores a positional argument, so a %%F entry would open empty and look like the file failed to load. - Declares application/x-fiff and application/x-ctf-meg, both defined by neuro-mime-types. Without a MimeType declaration a default application entry is silently ignored by the desktop. - Icon from upstream's own resources/design/logos/MNE-CPP_Logo.svg. - AppStream metainfo, validated at build time, so the application appears in GNOME Software and KDE Discover rather than only in the menu. - Adds a %%check, which this package did not have: the desktop file must validate, the icon it names must exist, and Exec must name a binary we install. - NOT shipping entries for mne_scan or mne_browse. Both are Qt GUIs, but `--help` on either hangs instead of printing, so their command-line contract is unverified and an entry would be a guess. * Mon Sep 28 2026 Morgan Hough - 0.1.9^20260304git1daca51-7 - Ship the runtime resources. The package installed exactly ONE file under %%{_datadir}/mne-cpp -- general/explanations/fiff_explanations.txt -- and silently omitted everything else, including coil_def.dat, which mne_forward_solution and dipole fitting require. tests/mne-cpp.bats has been failing "forward-model coil definitions are present" on that. The %%install block preferred a *built* resources tree under out/ and fell back to source only when that was ABSENT. Both halves of its reasoning were wrong: the symlinks live in out/, not in source -- they were created by the "Add symbolic links to project resources" CMake block that %%prep deletes, so out/resources is sparse afterwards -- and the fallback tested for an empty variable rather than a sparse tree, so a near-empty out/resources counted as success. Now copies resources/general from the source tree, verified at commit 1daca51 to hold coil_def.dat (38978 bytes) and coil_def_Elekta.dat (5462 bytes) as real files, plus 2D/3D layouts, selection groups, sensor surfaces, default filters and HPI alignment data. - %%install now fails if coil_def.dat is missing, rather than shipping a package whose forward modelling is broken. * Sun Sep 27 2026 Morgan Hough - 0.1.9^20260304git1daca51-6 - Install the four application-shared libraries the plugins link against: libanShared.so (mne_analyze) and libscShared.so, libscDisp.so, libscMeas.so (mne_scan). Upstream builds all four and ships no install() rule for any of them -- the same gap this spec already works around for the plugins themselves -- so 25 plugins were packaged while the libraries they need were not. rpm generated Requires from DT_NEEDED with nothing providing them, which made EVERY build of this package uninstallable: package: mne-cpp-...-5.fc44.x86_64 unresolved deps: libanShared.so libscDisp.so libscMeas.so libscShared.so Found by repoclosure via the new ci/verify-installed-runtime.sh, not by the build, which succeeds either way. - Installed into %%{_libdir} rather than the private %%{_libdir}/%%{name} beside the plugins. The plugins are dlopen'd, so their DT_NEEDED resolves through their own RPATH or the global path and the private directory is on neither; %%{_libdir} also gets them auto-Provides, which is what closes the repoclosure failure. - The harvest prints the inventory of libraries that ARE in the build tree before failing, so a wrong search path costs one build instead of two. * Mon Aug 03 2026 Morgan Hough - 0.1.9^20260304git1daca51-5 - Install the mne_scan (19) and mne_analyze (13) plugins, which upstream builds but never installs; the GUI apps previously shipped as empty shells - Patch both GUI apps to load plugins from %{_libdir}/%{name}/mne_*_plugins instead of the applicationDirPath-relative path that resolved into %{_bindir} - Hard-fail the build if no plugins are harvested, so the gap cannot recur - Ship mne_forward_solution and the built runtime resources (coil defs, layouts, selection groups) that were previously dropped * Mon May 04 2026 Morgan Hough - 2.2.1-1 - Update to stable release v2.2.1 - Set SOVERSION to 2 for libraries * Mon Mar 16 2026 Morgan Hough - 0.1.9^20260304git1daca51-3 - Fix lib/ to lib64/ move: mkdir -p target directory first - Remove Eigen signature_of_eigen3_matrix_library marker file - Remove broken dev symlinks from resources before copying * Sun Mar 15 2026 Morgan Hough - 0.1.9^20260304git1daca51-2 - Fix cmake parse errors: properly delete git hash execute_process blocks and entire resource symlink block (not selective line deletion) * Wed Mar 04 2026 Morgan Hough - 0.1.9^20260304git1daca51-1 - Rewrite spec from git snapshot (no releases since v0.1.9 in 2021) - Use system Eigen3 instead of bundled copy - Inject SOVERSION 0 on all shared libraries - Inject cmake install targets (upstream lacks install rules) - Upgrade C++ standard to C++17 for GCC 15 compatibility - Split into main and devel subpackages * Tue Jan 06 2026 Morgan Hough - 1.1.0-1 - Initial RPM build attempt