Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
## OpenBLAS v0.3.34 (v0.3.34)

### 0.3.34.237.0 (2026-09~06)
### 0.3.34.237.1 (2026-10-09)

- Build every manylinux and musllinux wheel without a runtime dependency on
libgfortran, and therefore without libquadmath, which is only ever pulled in
as a dependency of libgfortran itself. This drops ~1MB (compressed) from the
wheels that bundled both and, since libquadmath is LGPL-2.1-or-later with no
GCC Runtime Library Exception, removes an LGPL obligation along with it. The
two `x**n` entry points are taken as-is out of the toolchain's libgfortran.a
so results stay bit-for-bit identical; see tools/gfortran_compat.sh. macOS
is unchanged for now (ld64 has no -Wl,--exclude-libs), and Windows already
static-links libgfortran. A toolchain that cannot support it falls back to
linking libgfortran as before.
= Update windows devstudio 2022 to 2026


### 0.3.34.237.0 (2026-09-06)
- pick up a fix for SVE indentification

### 0.3.34.106.0 (2026-08~08)
### 0.3.34.106.0 (2026-08-08)
- pick up a fix for windows threading

### 0.3.34.0.0 (2026-07-17)
Expand Down
14 changes: 12 additions & 2 deletions ci-repair-wheel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,22 @@ else
# Add an RPATH to libgfortran:
# https://github.com/pypa/auditwheel/issues/451
# Use zipfile since the manylinux images do not have `zip`
python3 -c "
#
# Platforms built with NO_LIBGFORTRAN=1 bundle no libgfortran (and so no
# libquadmath, which is the only reason this fixup is needed at all), so
# there is nothing to repair there. Count what we extracted and skip the
# rest when the wheel is already clean.
n_gfortran=$(python3 -c "
import re, sys, zipfile, pathlib
whl = next(pathlib.Path(sys.argv[1]).glob('*.whl'))
with zipfile.ZipFile(whl, 'a') as z:
members = [m for m in z.namelist() if re.search(r'libgfortran', m)]
z.extractall(members=members)
" "$1"
print(len(members))
" "$1")
if [ "$n_gfortran" = "0" ]; then
echo "wheel bundles no libgfortran; skipping the auditwheel #451 rpath fixup"
else
patchelf --force-rpath --set-rpath '$ORIGIN' */lib/libgfortran*
python3 -c "
import sys, zipfile, pathlib, glob
Expand All @@ -46,6 +55,7 @@ with zipfile.ZipFile(whl, 'w', compression=zipfile.ZIP_DEFLATED) as z:
for item, data in entries.items():
z.writestr(item, data)
" "$1"
fi
mkdir -p /output
# copy libs/openblas*.tar.gz to dist/
cp libs/openblas*.tar.gz /output/
Expand Down
40 changes: 40 additions & 0 deletions ci-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,43 @@ fi

$PYTHON -m pip install pkgconf
$PYTHON -m pkgconf scipy-openblas --cflags

# Check the Fortran-runtime story of the installed wheel.
#
# Note this asks the *library* what happened rather than asking
# want_no_libgfortran what we intended: build_gfortran_compat falls back to
# linking libgfortran on toolchains it cannot support (no static
# libgfortran.a on riscv64, for instance), and such a wheel legitimately
# still bundles the runtime. The build already asserts the clean case on the
# library it produces; repeating it here catches anything
# auditwheel/delocate might reintroduce while repairing the wheel.
source tools/gfortran_compat.sh

if [ "${INTERFACE64}" != "1" ]; then
pkgdir=$($PYTHON -c "import scipy_openblas32 as m, pathlib; print(pathlib.Path(m.__file__).parent)")
else
pkgdir=$($PYTHON -c "import scipy_openblas64 as m, pathlib; print(pathlib.Path(m.__file__).parent)")
fi
echo "checking installed package $pkgdir"

openblas_lib=$(ls "$pkgdir"/lib/libscipy_openblas*.so "$pkgdir"/lib/libscipy_openblas*.dylib 2>/dev/null | head -1 || true)
if [ -z "$openblas_lib" ]; then
echo "FAIL: no OpenBLAS library found under $pkgdir/lib"
exit 1
fi

if [ -n "$(fortran_runtime_deps "$openblas_lib")" ]; then
echo "NOTE: $(basename "$openblas_lib") links the Fortran runtime, so this"
echo " platform took the fallback path in build_gfortran_compat and the"
echo " wheel is expected to bundle it. Nothing to assert."
else
# The library needs no Fortran runtime, so the wheel must not ship one.
found=$(find "$pkgdir" \( -name 'libgfortran*' -o -name 'libquadmath*' \) -print)
if [ -n "$found" ]; then
echo "FAIL: $(basename "$openblas_lib") needs no Fortran runtime, yet the"
echo " wheel still bundles it:"
echo "$found"
exit 1
fi
assert_no_fortran_runtime "$openblas_lib"
fi
68 changes: 68 additions & 0 deletions patches/0001-allow-building-without-libgfortran.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
diff --git a/Makefile.system b/Makefile.system
index e8e732e9c..90c814ebd 100644
--- a/Makefile.system
+++ b/Makefile.system
@@ -1207,15 +1207,17 @@ FCOMMON_OPT += -Wall
FCOMMON_OPT += -frecursive
# work around ABI problem with passing single-character arguments
FCOMMON_OPT += -fno-optimize-sibling-calls
-#Don't include -lgfortran, when NO_LAPACK=1 or lsbcc
+#Don't include -lgfortran, when NO_LAPACK=1 or lsbcc or NO_LIBGFORTRAN=1
ifneq ($(NOFORTRAN), 1)
ifneq ($(NOFORTRAN), 2)
ifneq ($(NO_LAPACK), 1)
+ifneq ($(NO_LIBGFORTRAN), 1)
EXTRALIB += -lgfortran
endif
endif
endif
endif
+endif
ifdef NO_BINARY_MODE
ifeq ($(ARCH), $(filter $(ARCH),mips64))
ifdef BINARY64
@@ -1917,6 +1919,31 @@ export CROSS
export CROSS_SUFFIX
export NOFORTRAN
export C_LAPACK
+# NO_LIBGFORTRAN=1 builds a library with no runtime dependency on the Fortran
+# runtime. LIBGFORTRAN_COMPAT must point at an archive supplying the few
+# libgfortran entry points the Fortran LAPACK sources reference; the link
+# fails loudly if it does not cover them all. libquadmath goes away along
+# with libgfortran, being only ever a dependency of libgfortran itself.
+#
+# The archive is kept out of EXTRALIB on purpose: EXTRALIB is what
+# Makefile.install writes into the installed .pc file, and a build-time
+# absolute path has no business being there. It goes into
+# LIBGFORTRAN_COMPAT_LINK, which the shared-library link rules add directly,
+# and into FEXTRALIB, which is what the macOS dylib rule uses.
+#
+# Kept here, at the end and at top level, so it sees the final value of
+# FEXTRALIB whatever the USE_OPENMP and compiler conditionals above did.
+ifeq ($(NO_LIBGFORTRAN), 1)
+FEXTRALIB := $(filter-out -lgfortran -lquadmath,$(FEXTRALIB))
+ifneq ($(LIBGFORTRAN_COMPAT), )
+LIBGFORTRAN_COMPAT_LINK = -Wl,--exclude-libs,$(notdir $(LIBGFORTRAN_COMPAT)) $(LIBGFORTRAN_COMPAT)
+FEXTRALIB += $(LIBGFORTRAN_COMPAT_LINK)
+endif
+endif
+
+export LIBGFORTRAN_COMPAT
+export LIBGFORTRAN_COMPAT_LINK
+export NO_LIBGFORTRAN
export NO_FBLAS
export EXTRALIB
export CEXTRALIB
diff --git a/exports/Makefile b/exports/Makefile
index c4633143b..55f086297 100644
--- a/exports/Makefile
+++ b/exports/Makefile
@@ -213,7 +213,7 @@ else
ifneq ($(C_COMPILER), LSB)
$(CC) $(CFLAGS) $(LDFLAGS) -shared -o ../$(LIBSONAME) \
-Wl,--whole-archive $< -Wl,--no-whole-archive \
- -Wl,-soname,$(INTERNALNAME) $(EXTRALIB)
+ -Wl,-soname,$(INTERNALNAME) $(EXTRALIB) $(LIBGFORTRAN_COMPAT_LINK)
$(CC) $(CFLAGS) $(LDFLAGS) -w -o linktest linktest.c ../$(LIBSONAME) $(FEXTRALIB) && echo OK.
else
#for LSB
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "scipy-openblas64"
# v0.3.34-237-g1ad90fb1
version = "0.3.34.237.0"
version = "0.3.34.237.1"
requires-python = ">=3.7"
description = "Provides OpenBLAS for python packaging"
readme = "README.md"
Expand Down
71 changes: 71 additions & 0 deletions tools/build_steps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,38 @@ ROOT_DIR=$(dirname $(dirname "${BASH_SOURCE[0]}"))

MB_PYTHON_VERSION=3.9

source $(dirname "${BASH_SOURCE[0]}")/gfortran_compat.sh

# Whether to build without a runtime dependency on libgfortran (and so
# without libquadmath, which is only ever a dependency of libgfortran itself).
#
# Enabled for every manylinux and musllinux target. The set of libgfortran
# entry points the Fortran LAPACK sources reference is identical on all of
# them -- _gfortran_concat_string and _gfortran_etime, plus _gfortran_pow_r*_i8
# for INTERFACE64 -- because it comes from the LAPACK Fortran itself
# (`SIDE // TRANS`, `x**n`) rather than from anything architecture specific.
# If a toolchain cannot support it anyway, build_gfortran_compat returns
# non-zero and the build falls back to linking libgfortran as before.
#
# macOS is excluded for now: the link needs -Wl,--exclude-libs to keep the
# compat symbols out of the dylib's export table, and ld64 has no equivalent,
# so it would be a hard link failure rather than a fallback. See
# tools/gfortran_compat.sh. Windows never gets here -- it uses
# tools/build_steps_windows.sh, which already static-links libgfortran and
# asserts that libquadmath is not pulled in.
#
# Set NO_LIBGFORTRAN=0 in a CI matrix row to opt that row out, or
# NO_LIBGFORTRAN=1 to force it on.
function want_no_libgfortran {
# want_no_libgfortran <plat> (plat accepted but not currently needed --
# the decision is per-OS, and callers already have it to hand)
[ -n "$NO_LIBGFORTRAN" ] && { [ "$NO_LIBGFORTRAN" = "1" ]; return $?; }
case "$(uname -s)" in
Linux) return 0 ;;
*) return 1 ;;
esac
}

function before_build {
# install gfortran, objconv on macOS
if [ "$(uname -s)" == "Darwin" ]; then
Expand Down Expand Up @@ -169,6 +201,25 @@ function build_lib {
;;
esac
interface_flags="$interface_flags SYMBOLPREFIX=scipy_ LIBNAMEPREFIX=scipy_ FIXED_LIBNAME=1"

# Drop the libgfortran/libquadmath runtime dependency where we can. If
# the toolchain cannot support it (no static libgfortran.a, or the
# objects we need are no longer self-contained) fall back to the old
# behaviour rather than failing the build -- see tools/gfortran_compat.sh.
local compat_lib=""
if want_no_libgfortran "$plat"; then
if compat_lib=$(build_gfortran_compat "$PWD/build/gfortran_compat" "$interface64"); then
# TIMER=NONE selects LAPACK's second_NONE.f/dsecnd_NONE.f, which
# removes the only reference to _gfortran_etime. SECOND/DSECND
# then return 0.0, as they already do in a C_LAPACK build.
interface_flags="$interface_flags NO_LIBGFORTRAN=1 LIBGFORTRAN_COMPAT=$compat_lib TIMER=NONE"
echo "building without a libgfortran runtime dependency"
else
compat_lib=""
echo "WARNING: cannot drop libgfortran on ${MB_ML_LIBC:-manylinux}-$plat, linking it as usual"
fi
fi

mkdir -p libs
set -x
git config --global --add safe.directory '*'
Expand Down Expand Up @@ -218,6 +269,26 @@ function build_lib {
rm $BUILD_PREFIX/lib/pkgconfig/scipy-openblas.pc.bak
fi

if [ -n "$compat_lib" ]; then
# The shared library picked these up from the link line (see
# LIBGFORTRAN_COMPAT_LINK in the patch), but the static library is
# assembled from OpenBLAS's own objects only. Add them so
# that linking libscipy_openblas*.a does not need libgfortran either
# -- the .pc file no longer advertises -lgfortran in Libs.private.
# Done after the INTERFACE64 branch above, which overwrites the
# installed archive with the symbol-renamed one. The renaming does
# not touch _gfortran_* names, so order is safe either way.
local compat_dir=$(dirname "$compat_lib")
for static_lib in $BUILD_PREFIX/lib/libscipy_openblas*.a; do
# An unmatched glob arrives as a literal, and `ar crs` would
# happily create an archive at that bogus path.
[ -f "$static_lib" ] || continue
ar crs "$static_lib" "$compat_dir"/*.o
done
assert_no_fortran_runtime $BUILD_PREFIX/lib/libscipy_openblas*.so \
$BUILD_PREFIX/lib/libscipy_openblas*.dylib
fi

local out_name="openblas.tar.gz"
tar zcvf libs/$out_name \
$BUILD_PREFIX/include/*blas* \
Expand Down
49 changes: 45 additions & 4 deletions tools/build_steps_win_arm64.bat
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ REM sed -e "s/^VERSION = .*/VERSION = ${version}/" -i.bak Makefile.rule

for /f "tokens=3 delims= " %%v in ('findstr /b "version = " ..\pyproject.toml') do set version=%%v
set version=%version:"=%
powershell -Command "(Get-Content OpenBLAS/Makefile.rule) -replace '^VERSION = .*', 'VERSION = %version%' | Set-Content Makefile.rule"
REM cwd is already OpenBLAS here, so the source is Makefile.rule, not
REM OpenBLAS/Makefile.rule -- that path never existed, Get-Content errored,
REM and the VERSION substitution silently never happened.
powershell -Command "(Get-Content Makefile.rule) -replace '^VERSION = .*', 'VERSION = %version%' | Set-Content Makefile.rule"

REM Set suffixed-ILP64 flags
if "%if_bits%"=="64" (
Expand All @@ -94,9 +97,47 @@ if exist build (rmdir /S /Q build || exit /b 1)
mkdir build || exit /b 1 & cd build || exit /b 1

echo Setting up ARM64 Developer Command Prompt and running CMake...

REM Initialize VS ARM64 environment
CALL "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsarm64.bat"

REM Initialize VS ARM64 environment.
REM
REM Ask vswhere where Visual Studio is rather than hardcoding the version and
REM edition: the windows-11-arm image migrated from VS 2022 to VS 2026 in
REM September 2026 (actions/runner-images#14602), which broke the old
REM hardcoded 2022\Enterprise path. That failed silently -- CALL on a missing
REM batch file sets no errorlevel worth checking here -- so the vcvars
REM environment was simply never applied and the first visible symptom was
REM CMake reporting "CMAKE_MT-NOTFOUND" and "Detecting C compiler ABI info -
REM failed", because mt.exe and the MSVC/SDK paths were missing. Fail loudly
REM instead, so the next image change is obvious from the log.
set "VSWHERE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
if not exist "%VSWHERE%" set "VSWHERE=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe"
if not exist "%VSWHERE%" (
echo Error: cannot find vswhere.exe, so cannot locate Visual Studio.
exit /b 1
)
set "VSINSTALL="
for /f "usebackq delims=" %%i in (`"%VSWHERE%" -latest -prerelease -products * -property installationPath`) do set "VSINSTALL=%%i"
if not defined VSINSTALL (
echo Error: vswhere.exe reported no Visual Studio installation.
exit /b 1
)
set "VCVARSALL=%VSINSTALL%\VC\Auxiliary\Build\vcvarsall.bat"
if not exist "%VCVARSALL%" (
echo Error: no VC\Auxiliary\Build\vcvarsall.bat under "%VSINSTALL%".
exit /b 1
)
echo Using Visual Studio at "%VSINSTALL%"
CALL "%VCVARSALL%" arm64
if errorlevel 1 (
echo Error: "%VCVARSALL%" arm64 failed.
exit /b 1
)
where mt.exe >NUL 2>&1
if errorlevel 1 (
echo Error: mt.exe is not on PATH after vcvarsall; CMake will fail with
echo CMAKE_MT-NOTFOUND. The Windows SDK component may be missing.
exit /b 1
)

REM Prefer LLVM flang
PATH=C:\Program Files\LLVM\bin;%PATH%
Expand Down
Loading
Loading