# AFNI - Analysis of Functional NeuroImages # Production multi-subpackage Fedora RPM spec # # Subpackages: afni (meta), afni-libs, afni-core, afni-gui, afni-suma, # python3-afni, afni-tcsh, afni-rstats, afni-data, # afni-doc, afni-devel, afni-desktop %global debug_package %{nil} # Python bytecompile for noarch scripts %global __python %{python3} # Some afnipy files have Python 2 print statements (dead code); don't fail on bytecompile errors %global _python_bytecompile_errors_terminate_build 0 Name: afni Version: 26.2.09 Release: 10%{?dist} Summary: Analysis of Functional NeuroImages License: GPL-2.0-or-later AND LicenseRef-Fedora-Public-Domain URL: https://afni.nimh.nih.gov/ Source0: https://github.com/afni/afni/archive/refs/tags/AFNI_%{version}.tar.gz # Desktop integration (menu entries, icons, MIME types, AppStream metainfo and # a file-manager launcher). Upstream ships none of it; it is maintained as its # own small project and pulled in as a versioned release. The tarball carries # the icons pre-rasterised, so the build needs no SVG tooling. %global desktop_ver 1.0 Source1: https://github.com/m9h/fedora-afni-desktop-integration/archive/v%{desktop_ver}/fedora-afni-desktop-integration-%{desktop_ver}.tar.gz # Bundled libraries (not available as system packages or incompatible) Provides: bundled(f2c) Provides: bundled(nifticlib) Provides: bundled(gifticlib) Provides: bundled(XmHTML) Provides: bundled(GLw) BuildRequires: gcc >= 14 BuildRequires: gcc-c++ BuildRequires: cmake >= 3.14.7 BuildRequires: ninja-build BuildRequires: git-core BuildRequires: patchelf BuildRequires: libstdc++-static # X11 / GUI deps BuildRequires: libXp-devel BuildRequires: libXpm-devel BuildRequires: libXext-devel BuildRequires: libXt-devel # Fedora's Motif package is "motif"; the openmotif-devel compatibility Provides # ended with motif 2.5 (rawhide/f46), where openmotif-devel resolves to nothing. BuildRequires: motif-devel # OpenGL / SUMA deps BuildRequires: mesa-libGL-devel BuildRequires: mesa-libGLU-devel BuildRequires: freeglut-devel # Image / math / misc libs BuildRequires: libpng-devel BuildRequires: libjpeg-turbo-devel BuildRequires: expat-devel BuildRequires: gsl-devel BuildRequires: glib2-devel BuildRequires: gts-devel BuildRequires: libomp-devel BuildRequires: zlib-devel # Neuroimaging libs (from COPR neurofedora) # BuildRequires: nifticlib-devel # gifticlib/nifticlib bundled (system versions too old for AFNI) # BuildRequires: gifticlib-devel # BuildRequires: gifticlib-cmake-devel # Python build BuildRequires: python3-devel BuildRequires: python3-setuptools BuildRequires: python3-wheel BuildRequires: python3-pip # R (for COMP_RSTATS) BuildRequires: R-devel # Script deps BuildRequires: tcsh BuildRequires: netpbm-progs # objdump, used in %%install to sort the ~458 installed programs into # core / gui / suma by the libraries they actually link. BuildRequires: binutils # Validate the desktop entries and AppStream metainfo in %%check. BuildRequires: desktop-file-utils BuildRequires: libappstream-glib # ----------------------------------------------------------------------- # Meta-package: pulls in everything for AFNI bootcamp # ----------------------------------------------------------------------- Requires: %{name}-core = %{version}-%{release} Requires: %{name}-gui = %{version}-%{release} Requires: %{name}-suma = %{version}-%{release} Requires: python3-%{name} = %{version}-%{release} Requires: %{name}-tcsh = %{version}-%{release} Requires: %{name}-data = %{version}-%{release} Requires: %{name}-doc = %{version}-%{release} Requires: %{name}-desktop = %{version}-%{release} %description AFNI (Analysis of Functional NeuroImages) is a comprehensive suite of programs for processing, analyzing, and displaying functional MRI (FMRI) data. This meta-package installs all AFNI subpackages needed for bootcamp courses. # ----------------------------------------------------------------------- # afni-libs: core shared libraries + model/plugin DSOs + profile.d # ----------------------------------------------------------------------- %package libs Summary: Core shared libraries for AFNI # nifticlib/gifticlib bundled (system versions too old) %description libs Core shared libraries for AFNI including libmri, lib3DEdge, libeispack, libmpeg_encode, and 29 model DSOs for nonlinear fitting. Also installs profile.d scripts that set AFNI environment variables. %ldconfig_scriptlets libs # ----------------------------------------------------------------------- # afni-core: ~290 C binaries in /usr/libexec/afni/ + key symlinks # ----------------------------------------------------------------------- %package core Summary: Core AFNI command-line tools Requires: %{name}-libs%{?_isa} = %{version}-%{release} %description core Core AFNI command-line programs (3dcalc, 3dresample, 3dinfo, 3dcopy, 3dvolreg, 3dQwarp, etc.). Most programs are installed in /usr/libexec/afni/ and accessible via the profile.d PATH addition. Key commands have /usr/bin symlinks. # ----------------------------------------------------------------------- # afni-gui: AFNI GUI + X11/Motif programs + plugins # ----------------------------------------------------------------------- %package gui Summary: AFNI graphical user interface and X11 programs Requires: %{name}-core%{?_isa} = %{version}-%{release} %description gui The AFNI GUI for interactive neuroimaging data visualization, plus X11/Motif dependent programs (3dAllineate, 3dDeconvolve, to3d, etc.) and 52 GUI plugins. # ----------------------------------------------------------------------- # afni-suma: SUMA surface analysis + OpenGL tools # ----------------------------------------------------------------------- %package suma Summary: SUMA surface analysis and OpenGL tools Requires: %{name}-gui%{?_isa} = %{version}-%{release} %description suma SUMA (Surface Mapping) GUI for cortical surface analysis, plus ~90 OpenGL dependent programs for surface-based analysis and tractography. # ----------------------------------------------------------------------- # python3-afni: afnipy module + Python scripts (noarch) # ----------------------------------------------------------------------- %package -n python3-%{name} Summary: AFNI Python module and scripts BuildArch: noarch Requires: %{name}-libs = %{version}-%{release} Requires: python3-numpy Requires: python3-matplotlib # The upstream distribution is named afnipy, so the generated provide is # python3dist(afnipy) and nothing answers to python3dist(afni). That is correct -- # a provide for a distribution that does not exist would be worse -- but this # subpackage is called python3-afni, so a search by the upstream name found nothing. Provides: python3-afnipy = %{version}-%{release} %description -n python3-%{name} The afnipy Python module and ~100 Python scripts for AFNI processing pipelines, including afni_proc.py and afni_system_check.py. # ----------------------------------------------------------------------- # afni-tcsh: tcsh scripts (noarch) # ----------------------------------------------------------------------- %package tcsh Summary: AFNI tcsh shell scripts BuildArch: noarch Requires: %{name}-libs = %{version}-%{release} Requires: tcsh %description tcsh Over 150 tcsh shell scripts for AFNI workflows, including @SSwarper, @chauffeur_afni, @auto_tlrc, and bootcamp data installers. # ----------------------------------------------------------------------- # afni-rstats: R scripts (opt-in, NOT in meta-package) # ----------------------------------------------------------------------- %package rstats Summary: AFNI R statistical analysis scripts BuildArch: noarch Requires: %{name}-libs = %{version}-%{release} # The .R scripts' shebangs point at the AFNI_Batch_R/afni_run_R wrappers, which # afni-core ships. RPM generates file Requires for them, and dnf does not reliably # resolve a non-primary-metadata path to a package in another subpackage: afni-rstats # was uninstallable on every chroot. Name the provider. Requires: %{name}-core = %{version}-%{release} Requires: R-core %description rstats R scripts for AFNI statistical analyses including 3dLME, 3dMVM, 3dMEMA, 3dISC, and other mixed-effects and Bayesian group analysis tools. # ----------------------------------------------------------------------- # afni-data: atlas configs and templates (noarch) # ----------------------------------------------------------------------- %package data Summary: AFNI atlas configuration and reference data BuildArch: noarch %description data Atlas space configuration files, NIML templates, and reference data for AFNI. Large bootcamp datasets are not included and should be downloaded via install_bootcamp_data.tcsh. # ----------------------------------------------------------------------- # afni-doc: documentation (noarch) # ----------------------------------------------------------------------- %package doc Summary: AFNI documentation BuildArch: noarch %description doc Documentation files for AFNI including README files and environment configuration guides. # ----------------------------------------------------------------------- # afni-devel: headers for libmri etc. # ----------------------------------------------------------------------- # ----------------------------------------------------------------------- # afni-desktop: menu entries, icons, MIME types, AppStream metadata (noarch) # ----------------------------------------------------------------------- %package desktop Summary: Desktop integration for the AFNI and SUMA viewers BuildArch: noarch Requires: %{name}-gui = %{version}-%{release} Requires: %{name}-suma = %{version}-%{release} # The launcher maps files a file manager passes to the directories afni opens. Requires: /bin/sh %description desktop Applications-menu entries and icons for the AFNI and SUMA viewers, MIME types for AFNI BRIK/HEAD datasets and NIfTI volumes so a file manager can open them with AFNI, and AppStream metadata so the application appears in software centres. Upstream AFNI ships none of this. %package devel Summary: AFNI development headers Requires: %{name}-libs%{?_isa} = %{version}-%{release} %description devel Development headers for AFNI libraries (libmri, etc.) for building programs that link against AFNI's core libraries. %prep %setup -q -n afni-AFNI_%{version} # AFNI takes its version from `git describe --tags` and falls back to the # literal 99.99.99 when that fails, which is every tarball build. The # GIT_DESCRIBE_TAG escape hatch is itself broken (missing `$`), so stamp the # fallback directly. The grep fails the build if upstream changes the line. sed -i 's/set(GIT_REPO_VERSION "99.99.99")/set(GIT_REPO_VERSION "%{version}")/' \ cmake/get_git_repo_version.cmake grep -q 'set(GIT_REPO_VERSION "%{version}")' cmake/get_git_repo_version.cmake # NOTE: NIFTI and GIFTI are bundled (USE_SYSTEM_*=OFF) because: # - System gifticlib 1.0.9 is too old (missing gifti_rotate_DAs_to_front) # - System nifticlib 3.0.1 is too old (missing nifti_image_write_bricks_status) # - System nifti1_io.h/nifti2_io.h have conflicting type definitions # AFNI's bundled versions are newer and internally consistent. # FetchContent would try to git clone; redirect to bundled source dirs. # FETCHCONTENT_SOURCE_DIR_ overrides the download/clone step. # Also patch bundled gifti's CMakeLists.txt to not re-fetch nifti # (AFNI's outer build already provides nifti targets via FetchContent). sed -i '/FetchContent_Declare.*fetch_nifti_clib_git_repo/,/endif()/c\ # [Fedora] nifti targets provided by outer AFNI build\ find_package(NIFTI QUIET)\ if(NOT TARGET NIFTI::nifti2)\ add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../nifti ${CMAKE_CURRENT_BINARY_DIR}/nifti_clib)\ endif()' src/gifti/CMakeLists.txt # Create GIFTI::giftiio alias target after bundled gifti builds giftiio # (AFNI's CMakeLists_mri.txt expects GIFTI::giftiio but bundled creates giftiio) cat >> src/gifti/CMakeLists.txt << 'ALIAS_EOF' # [Fedora] Add public include dir and alias for AFNI build system compatibility if(TARGET giftiio) target_include_directories(giftiio PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) if(NOT TARGET GIFTI::giftiio) add_library(GIFTI::giftiio ALIAS giftiio) endif() endif() ALIAS_EOF # -- SONAME versioning for shared libraries -- # AFNI upstream does not set VERSION/SOVERSION on shared libs. # Patch CMakeLists_mri.txt (contains the mri library definition) # Ensure trailing newline then append SONAME properties # (some upstream CMakeLists.txt files lack a final newline) for _soname_file in \ src/CMakeLists_mri.txt:mri \ src/3DEdge/CMakeLists.txt:3DEdge \ src/eispack/CMakeLists.txt:eispack \ src/mpeg_encodedir/CMakeLists.txt:mpeg_encode \ src/coxplot/CMakeLists.txt:coxplot \ src/ptaylor/CMakeLists.txt:track_tools \ ; do _file="${_soname_file%%:*}" _target="${_soname_file##*:}" if [ -f "$_file" ]; then printf '\n# Fedora SONAME versioning (added by RPM spec)\nif(TARGET %s)\n set_target_properties(%s PROPERTIES VERSION %s SOVERSION 26)\nendif()\n' \ "$_target" "$_target" "%{version}" >> "$_file" fi done %build export CC=gcc export CXX=g++ export CXXFLAGS="%{optflags} -std=c++17 -include cstdint -fpermissive" export CFLAGS="%{optflags} -std=gnu17 -Wno-error=implicit-function-declaration \ -Wno-error=int-conversion -Wno-error=incompatible-pointer-types \ -Wno-error=format-security" # Component groups, and why each one is where it is: # # COMP_ATLASES stays OFF and must. cmake/afni_project_dependencies.cmake # requires `datalad` and `rsync` and DOWNLOADS the atlas datasets during the # build; a COPR/mock buildroot has no network, and the atlases carry their own # redistribution terms, so they would need to be their own source package. # Upstream defaults it OFF too. # # COMP_FUNSTUFF and COMP_DOCS are upstream-default ON (gated only on COMP_GUI, # which we enable) and are pure file installs -- no extra dependency, no # network. FUNSTUFF installs src/{faces,poems} (19 MB) under # %%{_datadir}/afni/funstuff; the GUI's face/poem features need it. DOCS # installs doc/README/README* under %%{_docdir}. Both were off here for no # recorded reason; enabled 26.2.09-1. # # NB: do NOT put comments between the continued lines below -- a `#` inside a # backslash continuation comments out the REST OF THE COMMAND, silently dropping # every option after it. %cmake -GNinja \ -DAFNI_COMPILER_CHECK=OFF \ -DCOMP_CORELIBS=ON \ -DCOMP_COREBINARIES=ON \ -DCOMP_GUI=ON \ -DCOMP_SUMA=ON \ -DCOMP_PYTHON=ON \ -DCOMP_TCSH=ON \ -DCOMP_RSTATS=ON \ -DCOMP_PLUGINS=ON \ -DCOMP_ATLASES=OFF \ -DCOMP_FUNSTUFF=ON \ -DCOMP_DOCS=ON \ -DUSE_SYSTEM_NIFTI=OFF \ -DUSE_SYSTEM_GIFTI=OFF \ -DFETCHCONTENT_SOURCE_DIR_NIFTI_CLIB=%{_builddir}/%{buildsubdir}/src/nifti \ -DFETCHCONTENT_SOURCE_DIR_GIFTI_CLIB=%{_builddir}/%{buildsubdir}/src/gifti \ -DFETCHCONTENT_FULLY_DISCONNECTED=ON \ -DUSE_SYSTEM_GTS=ON \ -DUSE_SYSTEM_GLUT=ON \ -DUSE_SYSTEM_GLW=OFF \ -DUSE_SYSTEM_XMHTML=OFF \ -DUSE_SYSTEM_F2C=OFF \ -DUSE_OMP=ON \ -DSTANDARD_PYTHON_INSTALL=OFF \ -DCMAKE_SKIP_INSTALL_RPATH=ON \ -DAFNI_INSTALL_RUNTIME_DIR=libexec/afni \ -DAFNI_INSTALL_LIBRARY_DIR=%{_lib}/afni \ -DAFNI_INSTALL_INCLUDE_DIR=include/afni \ -DAFNI_INSTALL_DOC_DIR=share/doc/afni \ -DAFNI_INSTALL_FUNSTUFF_DIR=share/afni/funstuff \ -DREMOVE_BUILD_PARITY_CHECKS=ON %cmake_build # Build afnipy wheel cd src/python_scripts # Upstream hardcodes version='0.0.1' in setup.py, so the wheel -- and the generated # python3dist(afnipy) provide with it -- claimed 0.0.1 while the rpm said 26.2.09. Any # consumer floor such as `python3dist(afnipy) >= 26` could never match, and the # metadata misdescribed what is installed. Stamp the real version in, and assert the # substitution so a changed upstream string fails the build instead of silently # restoring 0.0.1. sed -i "s/version='0.0.1'/version='%{version}'/" setup.py grep -q "version='%{version}'" setup.py %pyproject_wheel cd ../.. %install # -- Full cmake install (all components go to libexec/afni and lib/afni) -- %cmake_install # -- Install afnipy Python package -- cd src/python_scripts %pyproject_install %pyproject_save_files afnipy cd ../.. # -- Drop bundled headers and CLI tools that would collide with system pkgs -- # These collide on path with nifticlib/gifticlib RPMs and aren't part of AFNI's # runtime ABI, so just remove them. rm -rf %{buildroot}%{_includedir}/nifti rm -rf %{buildroot}%{_includedir}/gifti rm -f %{buildroot}%{_bindir}/nifti1_tool rm -f %{buildroot}%{_bindir}/nifti_stats rm -f %{buildroot}%{_bindir}/nifti_tool rm -f %{buildroot}%{_bindir}/gifti_tool rm -f %{buildroot}%{_bindir}/gifti_test rm -rf %{buildroot}%{_datadir}/cmake/NIFTI # -- Rename bundled libs that share SONAMEs with Fedora system packages -- # # AFNI bundles patched versions of gifticlib, nifticlib, and (transitively) f2c. # Each bundle has AFNI-specific symbols that the Fedora system versions don't # carry (gifti_rotate_DAs_to_front, nifti_image_write_bricks_status, etc.). # The bundled libs and system libs share the same SONAMEs (libgiftiio.so.0, # libnifti2.so.2, libniftiio.so.2, libnifticdf.so.2, libznz.so.3, libf2c.so). # # Three failure modes the harness has surfaced as we iterated this: # - ship both → file conflict at install time (f2c) # - delete bundled, rely on system → "undefined symbol" at runtime # - move bundled to /usr/lib64/afni → ldconfig doesn't find them # # Working approach: rename SONAME of the bundled lib to libafni_.so., # patchelf every AFNI ELF that NEEDED the old name to point at the new one, # ship the renamed lib at /usr/lib64/. The bundled and system libs then live # side-by-side, ldconfig finds both, AFNI loads its bundled flavor through # the renamed NEEDED chain, every other package keeps using system. # # The block below is repeated per-lib (rather than functioned out) because # rpmbuild's %install runs under a single sh -c invocation and bash function # definitions are visible to find -exec only via `export -f` which couples # state in a way that's harder to debug than the redundancy. mkdir -p %{buildroot}%{_libdir} # Rename + move one versioned bundled lib (called per-lib below). # Args: # e.g. _afni_rename_lib libgiftiio libafni_giftiio 0 0.0.0 _afni_rename_lib() { local bundled="$1" newpfx="$2" sov="$3" fullv="$4" local src_dir=%{buildroot}%{_prefix}/lib local dst_dir=%{buildroot}%{_libdir} for src in "$src_dir"/"$bundled".so*; do [[ -e "$src" ]] || continue local base new_base base=$(basename "$src") new_base="${base/$bundled/$newpfx}" if [[ ! -L "$src" ]]; then patchelf --set-soname "${newpfx}.so.${sov}" "$src" fi mv "$src" "$dst_dir/$new_base" done # Recreate SONAME symlink (mv breaks the original link target chain) rm -f "$dst_dir/${newpfx}.so.${sov}" [[ -f "$dst_dir/${newpfx}.so.${fullv}" ]] && \ ln -sf "${newpfx}.so.${fullv}" "$dst_dir/${newpfx}.so.${sov}" # Drop the bare devel symlink (nothing outside AFNI ever links to the # renamed bundle — and rpmbuild's check-files step rejects it as # unpackaged if %files's .so.* glob doesn't catch it). rm -f "$dst_dir/${newpfx}.so" } export -f _afni_rename_lib # Rename: gifticlib, nifticlib (4 libs), f2c # Format: bundled-name new-prefix-name SONAME-version full-versioned-suffix _afni_rename_lib libgiftiio libafni_giftiio 0 0.0.0 _afni_rename_lib libnifti2 libafni_nifti2 2 2.1.0 _afni_rename_lib libniftiio libafni_niftiio 2 2.1.0 _afni_rename_lib libnifticdf libafni_nifticdf 2 2.1.0 _afni_rename_lib libznz libafni_znz 3 3.0.0 # Patchelf every AFNI ELF that NEEDED any of the renamed libs to point at # the libafni_* equivalent. Covers libmri.so.26, every 3d* binary in # /usr/libexec/afni, and the model/plugin libs still in /usr/lib64/afni/. # A single pass handles all five renames; consumers that don't reference a # given lib are left alone by the inner conditional. _patch_afni_consumers() { local target="$1" [[ -f "$target" && ! -L "$target" ]] || return file "$target" 2>/dev/null | grep -q ELF || return local entries entries=$(readelf -d "$target" 2>/dev/null) || return grep -q "libgiftiio.so" <<<"$entries" && patchelf --replace-needed libgiftiio.so.0 libafni_giftiio.so.0 "$target" 2>/dev/null || : grep -q "libnifti2.so" <<<"$entries" && patchelf --replace-needed libnifti2.so.2 libafni_nifti2.so.2 "$target" 2>/dev/null || : grep -q "libniftiio.so" <<<"$entries" && patchelf --replace-needed libniftiio.so.2 libafni_niftiio.so.2 "$target" 2>/dev/null || : grep -q "libnifticdf.so" <<<"$entries" && patchelf --replace-needed libnifticdf.so.2 libafni_nifticdf.so.2 "$target" 2>/dev/null || : grep -q "libznz.so" <<<"$entries" && patchelf --replace-needed libznz.so.3 libafni_znz.so.3 "$target" 2>/dev/null || : } export -f _patch_afni_consumers find %{buildroot}%{_libexecdir}/afni -type f -executable -exec bash -c '_patch_afni_consumers "$0"' {} \; find %{buildroot}%{_libdir} -name "*.so*" -type f -exec bash -c '_patch_afni_consumers "$0"' {} \; find %{buildroot}%{_libdir}/afni -name "*.so*" -type f -exec bash -c '_patch_afni_consumers "$0"' {} \; # -- Move shared libraries from private dir to system libdir -- # Core shared libs go to %%{_libdir} so the dynamic linker can find them # without an RPATH or LD_LIBRARY_PATH dance. # # Group A (mri 3DEdge eispack coxplot track_tools) have proper versioned # SONAMEs (libfoo.so.26) — collision-safe, move as-is. # # Group B (mrix SUMA XmHTML segtools) have UNVERSIONED SONAMEs but are # uniquely-named — no other Fedora package ships them. Move as-is. # # Only model_/plugin_ libs (true AFNI plugins loaded via dlopen with # AFNI_PLUGINPATH/AFNI_MODELPATH) stay in /usr/lib64/afni/. mkdir -p %{buildroot}%{_libdir} for lib in mri 3DEdge eispack coxplot track_tools \ mrix SUMA XmHTML segtools; do if ls %{buildroot}%{_prefix}/%{_lib}/afni/lib${lib}.so* 2>/dev/null; then mv %{buildroot}%{_prefix}/%{_lib}/afni/lib${lib}.so* %{buildroot}%{_libdir}/ fi done # f2c lives in /usr/lib64/afni/ (not /usr/lib/ like the bundled gifti/nifti), # so it doesn't fit the _afni_rename_lib path pattern. Handle it inline. # Same approach: rename SONAME, patchelf consumers via the unified # _patch_afni_consumers pass below. if [[ -e %{buildroot}%{_prefix}/%{_lib}/afni/libf2c.so ]]; then patchelf --set-soname libafni_f2c.so %{buildroot}%{_prefix}/%{_lib}/afni/libf2c.so mv %{buildroot}%{_prefix}/%{_lib}/afni/libf2c.so %{buildroot}%{_libdir}/libafni_f2c.so fi # Re-run the consumer-patchelf pass to catch f2c (the earlier pass above # only had the 5 NIFTI/GIFTI lib mappings; f2c was renamed after, so its # consumers still NEEDED the original libf2c.so name). Patch them too. _patch_f2c_consumers() { local target="$1" [[ -f "$target" && ! -L "$target" ]] || return file "$target" 2>/dev/null | grep -q ELF || return if readelf -d "$target" 2>/dev/null | grep -q "libf2c.so"; then patchelf --replace-needed libf2c.so libafni_f2c.so "$target" 2>/dev/null || : fi } export -f _patch_f2c_consumers find %{buildroot}%{_libexecdir}/afni -type f -executable -exec bash -c '_patch_f2c_consumers "$0"' {} \; find %{buildroot}%{_libdir} -name "*.so*" -type f -exec bash -c '_patch_f2c_consumers "$0"' {} \; find %{buildroot}%{_libdir}/afni -name "*.so*" -type f -exec bash -c '_patch_f2c_consumers "$0"' {} \; # -- Symlinks for key commands in /usr/bin -- mkdir -p %{buildroot}%{_bindir} # afni-core symlinks for cmd in 3dcalc 3dresample 3dinfo 3dcopy 3drefit 3dmerge \ 3dAutomask 3dTshift 3dvolreg \ 3dQwarp 3dNwarpApply; do if [ -f %{buildroot}%{_libexecdir}/afni/"$cmd" ]; then ln -s %{_libexecdir}/afni/"$cmd" %{buildroot}%{_bindir}/"$cmd" fi done # afni-gui symlinks (3dDeconvolve is gui component) for cmd in afni to3d 3dAllineate 3dDeconvolve; do if [ -f %{buildroot}%{_libexecdir}/afni/"$cmd" ]; then ln -s %{_libexecdir}/afni/"$cmd" %{buildroot}%{_bindir}/"$cmd" fi done # afni-suma symlinks (3dSkullStrip is suma component) for cmd in suma 3dSkullStrip; do if [ -f %{buildroot}%{_libexecdir}/afni/"$cmd" ]; then ln -s %{_libexecdir}/afni/"$cmd" %{buildroot}%{_bindir}/"$cmd" fi done # python3-afni symlinks (scripts installed by pyproject to /usr/bin already, # but cmake python scripts go to libexec) for cmd in afni_proc.py afni_system_check.py; do if [ -f %{buildroot}%{_libexecdir}/afni/"$cmd" ] && \ [ ! -f %{buildroot}%{_bindir}/"$cmd" ]; then ln -s %{_libexecdir}/afni/"$cmd" %{buildroot}%{_bindir}/"$cmd" fi done # afni-tcsh symlinks for cmd in @SSwarper @chauffeur_afni @auto_tlrc; do if [ -f %{buildroot}%{_libexecdir}/afni/"$cmd" ]; then ln -s %{_libexecdir}/afni/"$cmd" %{buildroot}%{_bindir}/"$cmd" fi done # -- profile.d scripts -- mkdir -p %{buildroot}%{_sysconfdir}/profile.d cat > %{buildroot}%{_sysconfdir}/profile.d/afni.sh << 'PROFILE_EOF' export AFNI_PLUGINPATH="%{_libdir}/afni" export AFNI_MODELPATH="%{_libdir}/afni" export AFNI_GLOBAL_SESSION="%{_datadir}/afni" export AFNI_ATLAS_PATH="%{_datadir}/afni" export PATH="%{_libexecdir}/afni:$PATH" PROFILE_EOF cat > %{buildroot}%{_sysconfdir}/profile.d/afni.csh << 'PROFILE_EOF' setenv AFNI_PLUGINPATH "%{_libdir}/afni" setenv AFNI_MODELPATH "%{_libdir}/afni" setenv AFNI_GLOBAL_SESSION "%{_datadir}/afni" setenv AFNI_ATLAS_PATH "%{_datadir}/afni" set path = ( %{_libexecdir}/afni $path ) PROFILE_EOF # -- Fix shebangs -- # Python scripts in libexec find %{buildroot}%{_libexecdir}/afni -name "*.py" -exec \ sed -i '1s|^#!.*python.*$|#!/usr/bin/python3|' {} + 2>/dev/null || : # R scripts in libexec find %{buildroot}%{_libexecdir}/afni -name "*.R" -exec \ sed -i '1s|^#!.*Rscript.*$|#!/usr/bin/Rscript|' {} + 2>/dev/null || : find %{buildroot}%{_libexecdir}/afni -name "*.R" -exec \ sed -i '1s|^#!.*R --no-save.*$|#!/usr/bin/Rscript|' {} + 2>/dev/null || : # Some .R scripts are launched through AFNI's own R wrappers rather than # Rscript. Their shebang is the ENV form, `#!/usr/bin/env AFNI_Batch_R`, and # Fedora's brp-mangle-shebangs rewrites that to `#!/usr/bin/AFNI_Batch_R` after # %%install finishes -- it assumes an env-style interpreter lives in %%{_bindir}: # # mangling shebang in /usr/libexec/afni/3dICC.R # from /usr/bin/env AFNI_Batch_R to #!/usr/bin/AFNI_Batch_R # # RPM then generates Requires: /usr/bin/AFNI_Batch_R. We DO ship both wrappers, # but at %%{_libexecdir}/afni/, so nothing provides the %%{_bindir} path and # afni-rstats was UNINSTALLABLE. # # Rewrite the env form to the absolute location before brp-mangle-shebangs can # reinterpret it. Matching only the post-mangle `#!/usr/bin/AFNI_Batch_R` spelling # does nothing: that form never exists in %%install, which is exactly how the # first attempt at this (release 6) passed its own guard and shipped broken. # NOTE ON QUOTING, learned the hard way in release 7: do NOT wrap a sed script # across lines with backslash continuations INSIDE the quotes. Bash rewrites that # into an ANSI-C $'...' string with the backslashes doubled, so sed receives # \\s (literal backslash, then s) instead of \s and matches nothing: # # sed -i -E $'1s|^#!\\s*/usr/bin/env\\s+AFNI_Batch_R\\s*$|...; \\\n 1s|...' # # The build log shows exactly that. Continuations go BETWEEN -e options, where # they are outside any quoting, and [[:space:]] is used instead of \s so the # expression carries no backslashes at all. find %{buildroot}%{_libexecdir}/afni -name "*.R" -print0 | xargs -0 -r sed -i -E \ -e '1s|^#![[:space:]]*/usr/bin/env[[:space:]]+AFNI_Batch_R[[:space:]]*$|#!%{_libexecdir}/afni/AFNI_Batch_R|' \ -e '1s|^#![[:space:]]*/usr/bin/env[[:space:]]+afni_run_R[[:space:]]*$|#!%{_libexecdir}/afni/afni_run_R|' \ -e '1s|^#![[:space:]]*/usr/bin/AFNI_Batch_R[[:space:]]*$|#!%{_libexecdir}/afni/AFNI_Batch_R|' \ -e '1s|^#![[:space:]]*/usr/bin/afni_run_R[[:space:]]*$|#!%{_libexecdir}/afni/afni_run_R|' # Guard both spellings. This one worked in release 7 -- it is a single-line # pattern, which is precisely why it was not mangled, and it is what caught the # broken sed instead of letting it ship. if grep -lE '^#![[:space:]]*(/usr/bin/env[[:space:]]+)?(AFNI_Batch_R|afni_run_R)[[:space:]]*$|^#![[:space:]]*/usr/bin/(AFNI_Batch_R|afni_run_R)[[:space:]]*$' %{buildroot}%{_libexecdir}/afni/*.R 2>/dev/null | grep -q .; then echo "ERROR: .R scripts still reference an AFNI wrapper outside %{_libexecdir}/afni" >&2 grep -lE '^#!.*(AFNI_Batch_R|afni_run_R)' %{buildroot}%{_libexecdir}/afni/*.R >&2 || : exit 1 fi for w in AFNI_Batch_R afni_run_R; do test -f %{buildroot}%{_libexecdir}/afni/$w || \ { echo "ERROR: expected wrapper %{_libexecdir}/afni/$w is missing" >&2; exit 1; } done grep -q "^#!%{_libexecdir}/afni/AFNI_Batch_R" %{buildroot}%{_libexecdir}/afni/*.R 2>/dev/null || \ { echo "ERROR: no .R script points at %{_libexecdir}/afni/AFNI_Batch_R -- sed matched nothing" >&2; exit 1; } # -- Strip RPATHs from ELF binaries -- find %{buildroot}%{_libexecdir}/afni -type f -executable | while read bin; do if file "$bin" | grep -q "ELF"; then patchelf --remove-rpath "$bin" 2>/dev/null || : fi done find %{buildroot}%{_libdir} -name "*.so*" -type f | while read lib; do patchelf --remove-rpath "$lib" 2>/dev/null || : done # -- Install data files -- mkdir -p %{buildroot}%{_datadir}/afni # Atlas space config if present in source if [ -f AFNI_atlas_spaces.niml ]; then install -m 644 AFNI_atlas_spaces.niml %{buildroot}%{_datadir}/afni/ fi # -- Install documentation -- mkdir -p %{buildroot}%{_docdir}/afni for f in doc/README/README.*; do install -m 644 "$f" %{buildroot}%{_docdir}/afni/ 2>/dev/null || : done # -- rpmlint filters -- mkdir -p %{buildroot}%{_datadir}/rpmlint cat > %{buildroot}%{_datadir}/rpmlint/afni.toml << 'RPMLINT_EOF' # Model and plugin DSOs are loadable modules in a private directory # They intentionally lack SONAME versioning [afni-libs.Filters] "library-without-ldconfig-posttrans" = [".*afni/libmodel_.*", ".*afni/libplug_.*", ".*afni/lib.*\\.so"] "shared-lib-without-dependency-information" = [".*afni/.*"] RPMLINT_EOF # -- Desktop integration (Source1) ------------------------------------------ # The project's own `make install`; icon-cache, MIME and desktop-database # updates are handled by Fedora's file triggers, so no scriptlets are needed. tar -xzf %{SOURCE1} -C %{_builddir} %make_install -C %{_builddir}/fedora-afni-desktop-integration-%{desktop_ver} PREFIX=%{_prefix} # -- Sort the installed programs into core / gui / suma by what they LINK ------ # # afni-gui and afni-suma used to be declared but empty ("binaries included in # core"), so the split announced nothing: installing afni-core dragged in the # whole X11 + OpenGL stack even on a headless box. Classify instead of guess -- # a program belongs to suma if it links OpenGL/GLUT, to gui if it links # X11/Motif, and to core otherwise. Generated rather than hardcoded because # upstream adds and removes programs every release; a stale hand-written list # would silently drop new programs out of every subpackage. # # Files claimed by other subpackages (python3-afni, afni-tcsh, afni-rstats) are # skipped here and keep their existing %%files entries. for m in core gui suma; do : > afni-$m.files; done for f in %{buildroot}%{_libexecdir}/afni/*; do [ -f "$f" ] || continue base="${f##*/}" case "$base" in *.py|*.R|@*) continue ;; esac if LC_ALL=C head -c 4 "$f" | grep -q ELF; then needed="$(objdump -p "$f" 2>/dev/null | awk '/NEEDED/{printf "%s ", $2}')" else needed="" fi case "$needed" in *libGL*|*libGLU*|*libglut*) echo "%{_libexecdir}/afni/$base" >> afni-suma.files ;; *libXm*|*libXt*|*libX11*) echo "%{_libexecdir}/afni/$base" >> afni-gui.files ;; *) echo "%{_libexecdir}/afni/$base" >> afni-core.files ;; esac done # A silently empty manifest would mean the classification broke and everything # landed in core again -- fail the build instead of shipping the old mistake. test -s afni-gui.files test -s afni-suma.files test -s afni-core.files printf 'program split: core=%%s gui=%%s suma=%%s\\n' \ "$(wc -l < afni-core.files)" "$(wc -l < afni-gui.files)" "$(wc -l < afni-suma.files)" # Completeness guard. Every entry under libexec/afni must end up in one of the # three manifests or be claimed explicitly by another subpackage (the scripts, # lib_RetroTS, afnipy). If upstream adds a new directory or file shape, this # fails the build here with a clear message instead of producing the much less # obvious "Installed (but unpackaged) file(s) found" at the end. unaccounted="" for f in %{buildroot}%{_libexecdir}/afni/*; do base="${f##*/}" case "$base" in *.py|*.R|@*|lib_RetroTS|afnipy) continue ;; esac grep -qx "%{_libexecdir}/afni/$base" afni-core.files afni-gui.files afni-suma.files \ || unaccounted="$unaccounted $base" done if [ -n "$unaccounted" ]; then echo "ERROR: unclassified entries under %{_libexecdir}/afni:$unaccounted" >&2 echo " add them to a subpackage, or extend the classifier above." >&2 exit 1 fi %check # Verify key binaries were built test -x %{buildroot}%{_libexecdir}/afni/3dcalc test -x %{buildroot}%{_libexecdir}/afni/afni ls %{buildroot}%{_libdir}/libmri.so.* >/dev/null 2>&1 # Desktop integration must be valid, not merely present. desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/*.metainfo.xml # ----------------------------------------------------------------------- # FILE LISTS # ----------------------------------------------------------------------- # --- afni (meta-package) --- %files # empty — just dependencies # --- afni-libs --- %files libs %license doc/README/README.copyright %{_libdir}/libmri.so.* %{_libdir}/lib3DEdge.so.* %{_libdir}/libeispack.so.* %{_libdir}/libcoxplot.so.* %{_libdir}/libtrack_tools.so.* # Bundled libs with SONAMEs renamed to libafni_* to avoid collision with # Fedora's gifticlib/nifticlib RPMs that ship the same paths but lack # AFNI-only symbols (gifti_rotate_DAs_to_front, nifti_image_write_bricks_status, # etc.). See the corresponding %%install section for the full rationale. %{_libdir}/libafni_giftiio.so.* %{_libdir}/libafni_nifti2.so.* %{_libdir}/libafni_niftiio.so.* %{_libdir}/libafni_nifticdf.so.* %{_libdir}/libafni_znz.so.* # Unversioned-SONAME libs: NEEDED directly by /usr/libexec/afni/afni and # many 3d* binaries. Live in %%{_libdir} (not %%{_libdir}/afni) so # ldconfig finds them at runtime. No -devel symlink ships for these # because their SONAME *is* the unversioned name (upstream choice). %{_libdir}/libmrix.so %{_libdir}/libSUMA.so %{_libdir}/libXmHTML.so %{_libdir}/libsegtools.so # Bundled f2c renamed (libf2c → libafni_f2c) — Fedora's f2c-20210928 owns # /usr/lib64/libf2c.so, so we must namespace AFNI's copy to avoid an # install-time file conflict. See the corresponding %%install section. %{_libdir}/libafni_f2c.so %dir %{_libdir}/afni %{_libdir}/afni/*.so* %config(noreplace) %{_sysconfdir}/profile.d/afni.sh %config(noreplace) %{_sysconfdir}/profile.d/afni.csh %{_datadir}/rpmlint/afni.toml # --- afni-core --- # Programs in libexec/afni that link NEITHER X11/Motif NOR OpenGL, so this # subpackage installs on a headless machine without pulling a graphics stack. # The manifest is generated in %%install by inspecting each program's NEEDED # entries; scripts (*.py, *.R, @*) and lib_RetroTS are claimed by # python3-afni / afni-tcsh / afni-rstats and are skipped there. %files core -f afni-core.files %dir %{_libexecdir}/afni # dcm2niix_afni and /usr/bin symlinks for programs that stayed in core %{_bindir}/dcm2niix_afni %{_bindir}/3dcalc %{_bindir}/3dresample %{_bindir}/3dinfo %{_bindir}/3dcopy %{_bindir}/3drefit %{_bindir}/3dmerge %{_bindir}/3dAutomask %{_bindir}/3dTshift %{_bindir}/3dvolreg %{_bindir}/3dQwarp %{_bindir}/3dNwarpApply %{_bindir}/3dAllineate # --- afni-gui --- # Programs that link X11/Motif but not OpenGL: the AFNI GUI itself, the image # viewers and plotters, and the batch programs upstream builds with optional # graphing (3dDeconvolve, 3dNLfim, 3dsvm). Manifest generated in %%install. %files gui -f afni-gui.files %{_bindir}/afni %{_bindir}/to3d %{_bindir}/3dDeconvolve # --- afni-suma --- # Programs that link OpenGL/GLUT: the SUMA surface suite and the surface-aware # 3d* tools. Manifest generated in %%install. %files suma -f afni-suma.files %{_bindir}/suma %{_bindir}/3dSkullStrip # --- python3-afni --- # Don't use %%pyproject_files — some afnipy .py files have Python 2 syntax # and can't be byte-compiled, so the .pyc manifest is incomplete. %files -n python3-%{name} %{python3_sitelib}/afnipy/ %{python3_sitelib}/afnipy-*.dist-info/ %{_libexecdir}/afni/*.py %exclude %{_libexecdir}/afni/@*.py %{_libexecdir}/afni/lib_RetroTS # AFNI's own install drops a second copy of the afnipy modules beside the # programs. afni-core owned it only because its file list was a glob over the # whole directory; it is Python, so it belongs here. %{_libexecdir}/afni/afnipy/ # Python scripts installed to /usr/bin by pyproject_install %{_bindir}/*.py # --- afni-tcsh --- # @-prefixed scripts are the main tcsh content; other tcsh scripts # (3dMax, dsetstat2p, fat_proc_*, etc.) stay in afni-core if installed %files tcsh %{_libexecdir}/afni/@* %{_bindir}/@SSwarper %{_bindir}/@chauffeur_afni %{_bindir}/@auto_tlrc # --- afni-rstats --- # Only *.R files are reliably installed; R wrapper scripts without # .R extension stay in afni-core if installed %files rstats %{_libexecdir}/afni/*.R # --- afni-data --- # Atlas data is not installed with COMP_ATLASES=OFF (it needs datalad and the # network at build time -- see %%build); we just own the directory. COMP_FUNSTUFF # is ON, so the GUI's face images and poems land here: 19 MB of arch-independent # data, which is why it belongs in this noarch subpackage rather than in core. %files data %dir %{_datadir}/afni %{_datadir}/afni/funstuff/ # --- afni-doc --- %files doc %license doc/README/README.copyright %{_docdir}/afni/ # --- afni-devel --- # AFNI cmake does not install public headers, so no include dir. # Unversioned .so symlinks for linking are created during lib move. %files devel %{_libdir}/libmri.so %{_libdir}/lib3DEdge.so %{_libdir}/libeispack.so %{_libdir}/libcoxplot.so %{_libdir}/libtrack_tools.so # --- afni-desktop --- %files desktop %{_bindir}/afni-desktop-open %{_datadir}/applications/gov.nih.nimh.afni.desktop %{_datadir}/applications/gov.nih.nimh.afni.suma.desktop %{_datadir}/metainfo/gov.nih.nimh.afni.metainfo.xml %{_datadir}/mime/packages/gov.nih.nimh.afni.xml %{_datadir}/icons/hicolor/*/apps/gov.nih.nimh.afni.png %{_datadir}/icons/hicolor/*/apps/gov.nih.nimh.afni.suma.png %{_datadir}/icons/hicolor/scalable/apps/gov.nih.nimh.afni.svg %{_datadir}/icons/hicolor/scalable/apps/gov.nih.nimh.afni.suma.svg %changelog * Wed Oct 07 2026 Morgan Hough - 26.2.09-10 - afni-rstats: Requires afni-core explicitly. Its /usr/libexec/afni/AFNI_Batch_R and afni_run_R file Requires were unresolvable by dnf on every chroot, so the package could not be installed. * Tue Oct 06 2026 Morgan Hough - 26.2.09-9 - Stamp the package version into cmake/get_git_repo_version.cmake: every program reported AFNI_99.99.99 because a tarball has no .git for `git describe`. The build fails if the fallback line changes upstream. * Mon Sep 28 2026 Morgan Hough - 26.2.09-8 - Fix the quoting that made release 7's sed a no-op. Continuing a sed script across lines with backslashes INSIDE the quotes makes bash rewrite it as an ANSI-C $'...' string with the backslashes doubled, so sed got \\s rather than \\\\s and matched nothing. Continuations now sit between -e options and the expressions use [[:space:]], so no backslashes are involved at all. Release 7 did at least fail loudly rather than ship broken: its guard is a single-line pattern, escaped the same mangling, and caught the dead sed. * Mon Sep 28 2026 Morgan Hough - 26.2.09-7 - Actually fix afni-rstats this time. Release 6 rewrote '#!/usr/bin/AFNI_Batch_R', a spelling that never exists during %%install: the real shebang is the ENV form '#!/usr/bin/env AFNI_Batch_R', and brp-mangle-shebangs converts it to /usr/bin/AFNI_Batch_R AFTER %%install, assuming env interpreters live in %%{_bindir}. So the sed matched nothing, the guard (which checked only the post-mangle form) passed, and the package shipped just as broken. Now rewrites the env form to %%{_libexecdir}/afni/ before mangling can see it, guards both spellings, and fails if the sed matched nothing at all. * Mon Sep 28 2026 Morgan Hough - 26.2.09-6 - Fix afni-rstats, which was UNINSTALLABLE. Its .R scripts carry #!/usr/bin/AFNI_Batch_R and #!/usr/bin/afni_run_R, so RPM generated Requires on those paths -- but the wrappers install to %%{_libexecdir}/afni/, which is where we already ship them, and nothing provides the %%{_bindir} paths. Rewrite the shebangs to the real location, with guards that fail the build if a /usr/bin wrapper shebang survives or if either wrapper stops being installed. Found by ci/check-installable.sh. * Sun Sep 27 2026 Morgan Hough - 26.2.09-5 - python3-afni advertised python3dist(afnipy) = 0.0.1 against an rpm at 26.2.09, because upstream hardcodes version='0.0.1' in src/python_scripts/setup.py and the wheel is built from it. Stamp %%{version} in first, with an assertion so a changed upstream string fails the build rather than quietly restoring 0.0.1. A floor like `python3dist(afnipy) >= 26` could not have matched anything -- the same failure shape as a dependency floor with no ceiling, reading as a constraint while enforcing nothing. - Add Provides: python3-afnipy. The upstream distribution really is named afnipy, so python3dist(afni) matching nothing is correct rather than a defect, but this subpackage is named python3-afni and nothing answered to the upstream name. - Worth reporting upstream: afnipy's setup.py should carry the AFNI version. * Fri Sep 25 2026 Morgan Hough - 26.2.09-4 - BuildRequires motif-devel instead of openmotif-devel: the compatibility name is gone with motif 2.5 on rawhide (f46), so the rawhide build died in dependency resolution while f44/f45 kept working through the Provides. * Wed Sep 23 2026 Morgan Hough - 26.2.09-3 - afni-desktop now comes from the published m9h/fedora-afni-desktop-integration v1.0 release archive (Source1) instead of seven individual files; the icons ship pre-rasterised so the build no longer needs librsvg2-tools. No change to installed content. * Wed Sep 23 2026 Morgan Hough - 26.2.09-2 - New noarch subpackage afni-desktop: applications-menu entries and icons for the AFNI and SUMA viewers, MIME types for BRIK/HEAD datasets and NIfTI volumes, AppStream metainfo, and afni-desktop-open, a launcher that maps the files a file manager passes to the session directories afni actually opens. Upstream ships none of this; the files live in their own small project (fedora-afni-desktop-integration, Source1-7) and are validated in %%check with desktop-file-validate and appstream-util. The metapackage now pulls it in. Closes the DESKTOP group of tests/afni.bats. * Tue Sep 22 2026 Morgan Hough - 26.2.09-1 - Update to 26.2.09 - Enable COMP_FUNSTUFF and COMP_DOCS. Both are upstream-default ON, gated only on COMP_GUI which we already enable, and are pure file installs with no extra dependency and no network. FUNSTUFF is the GUI's face images and poems (19 MB, arch-independent, so it goes to the noarch afni-data). COMP_ATLASES stays OFF and must: it uses datalad to DOWNLOAD the datasets during the build, which a mock buildroot cannot do, and the atlases carry their own redistribution terms. - Pin AFNI_INSTALL_DOC_DIR and AFNI_INSTALL_FUNSTUFF_DIR to lowercase paths. GNUInstallDirs derives them from the CMake project name, which is "AFNI", so enabling those groups would otherwise have installed into /usr/share/doc/AFNI and /usr/share/AFNI/funstuff and failed the build as unpackaged files. - afni-gui and afni-suma now contain their programs. They were declared but EMPTY, with the binaries left in afni-core, so installing the command-line tools pulled in the whole X11 and OpenGL stack on a headless machine. The three manifests are generated in %%install by inspecting what each program actually links -- OpenGL/GLUT to afni-suma (64), X11/Motif to afni-gui (17), neither to afni-core (376) -- because upstream adds and removes programs every release and a hand-written list would silently go stale. A completeness guard fails the build if anything under libexec/afni is left unclaimed. - Note for headless users: 3dDeconvolve, 3dNLfim and 3dsvm link X11 for their optional graphing, so they now live in afni-gui rather than afni-core. - Move the second copy of the afnipy modules under libexec/afni from afni-core to python3-afni, where the rest of the Python lives. * Tue Sep 08 2026 Morgan Hough - 26.2.07-1 - Update to 26.2.07 * Sun Aug 23 2026 Morgan Hough - 26.2.03-1 - Update to 26.2.03 * Sat Jul 18 2026 Morgan Hough - 26.2.01-1 - Update to 26.2.01 * Sun Jun 14 2026 Morgan Hough - 26.1.04-1 - Update to 26.1.04 (current upstream; freshness sweep 2026-06-14) * Sun May 24 2026 Morgan Hough - 26.1.00-6 - Extend the SONAME-rename treatment to the bundled NIFTI libs (nifti2, niftiio, nifticdf, znz). The harness flipped the libmri.so.26 missing symbol from gifti_rotate_DAs_to_front (-3 fix) to nifti_image_write_bricks_status (still present in -5) — same class of bug, different library. AFNI bundles patched copies of nifticlib with *_status function variants the system nifticlib 3.0.1 doesn't carry; we previously deleted the bundle expecting system to suffice, and the dyld silently fell through to system libnifti2.so.2 → undefined symbol. - Refactor the three rename code paths (gifti, nifti, f2c) into a single _afni_rename_lib helper + a single _patch_afni_consumers pass so future collision-class bundles are a one-line addition. * Sun May 24 2026 Morgan Hough - 26.1.00-5 - Rename bundled libf2c.so → libafni_f2c.so (SONAME + patchelf consumers) to avoid file conflict with Fedora's f2c-20210928 package, which ships /usr/lib64/libf2c.so. The conflict was latent — the AMD test box didn't have f2c installed, so -2/-3/-4 looked clean; the iMac install run surfaced it by rolling back the entire dnf transaction. Same SONAME namespacing approach as the bundled gifticlib in -3. * Sun May 24 2026 Morgan Hough - 26.1.00-4 - Drop the bare libafni_giftiio.so devel symlink — nothing outside AFNI links against the renamed bundled lib and check-files refused to ship it unpackaged. (-3 failed at the same step; this is the trivial fix.) * Sat May 23 2026 Morgan Hough - 26.1.00-3 - Rename bundled gifticlib SONAME (libgiftiio → libafni_giftiio) and patchelf libmri.so.26 + every 3d* binary to consume the new name. System gifticlib 1.0.9 lacks gifti_rotate_DAs_to_front (and other AFNI-only additions) but has identical SONAME; the previous spec deleted the bundled copy and let dyld fall through to the system version, which then crashed at runtime with "undefined symbol". Surfaced by the neurofedora test harness on the same run that validated the -2 libmrix.so fix. * Sat May 23 2026 Morgan Hough - 26.1.00-2 - Move libmrix, libSUMA, libXmHTML, libf2c, libsegtools out of the private /usr/lib64/afni/ subdir to /usr/lib64/ so ldconfig finds them at runtime. Without this, /usr/bin/afni and most 3d* tools fail at startup with "libmrix.so: cannot open shared object file" — the dynamic linker has no RPATH (patchelf strips it) and no LD_LIBRARY_PATH (profile.d only exports AFNI_PLUGINPATH/AFNI_MODELPATH). These libs have unversioned SONAMEs but are NEEDED entries on every afni binary, not dlopen plugins, so they belong on the standard ld search path. Surfaced by the neurofedora test harness. * Thu Apr 23 2026 Morgan Hough - 26.1.00-1 - Update to 26.1.00 * Tue Mar 17 2026 Morgan Hough - 26.0.08-17 - Multi-subpackage rewrite (afni-libs, afni-core, python3-afni, afni-tcsh, afni-rstats, afni-data, afni-doc, afni-devel; afni-gui/afni-suma stubs) - Programs in /usr/libexec/afni/ with profile.d PATH and key /usr/bin symlinks - Bundle nifticlib and gifticlib (system versions too old for AFNI 26.x) - Bundle f2c, XmHTML, GLw (not in Fedora) - SONAME versioning for libmri, lib3DEdge, libeispack, libcoxplot, libtrack_tools - GCC 15 / Fedora 43 flags (-Wno-error=format-security, -std=gnu17) - libstdc++-static BR for dcm2niix static linking * Sun Feb 15 2026 Morgan Hough - 26.0.08-0 - Initial basic spec for AFNI 26.0.08