From 9e9edd558663aa2bf68a0c829e8d3fbe287ea177 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sat, 31 Oct 2026 19:26:02 +0000 Subject: [PATCH 1/8] Add Android wheels --- .github/workflows/posix.yml | 18 +-- .gitignore | 1 + CHANGELOG.md | 4 + build-openblas.sh | 5 + ci-before-build.sh | 8 ++ ci-test.sh | 7 +- local/scipy_openblas64/__init__.py | 21 +++- pyproject.toml | 28 +++-- tools/build_prepare_wheel.sh | 19 +++- tools/build_steps.sh | 171 ++++++++++++++--------------- 10 files changed, 159 insertions(+), 123 deletions(-) diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index cddbf3b2..ea6303ca 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -57,12 +57,15 @@ jobs: - { os: ubuntu-24.04-arm, PLAT: riscv64, INTERFACE64: '0', MB_ML_VER: '_2_39', MB_ML_LIBC: manylinux} - { os: ubuntu-24.04-arm, PLAT: riscv64, INTERFACE64: '1', MB_ML_VER: '_2_39', MB_ML_LIBC: manylinux} + - { os: ubuntu-latest, PLAT: x86_64, INTERFACE64: '0', MB_ML_LIBC: android} + - { os: ubuntu-latest, PLAT: x86_64, INTERFACE64: '1', MB_ML_LIBC: android} + + - { os: ubuntu-latest, PLAT: arm64_v8a, INTERFACE64: '0', MB_ML_LIBC: android} + - { os: ubuntu-latest, PLAT: arm64_v8a, INTERFACE64: '1', MB_ML_LIBC: android} + env: NIGHTLY: ${{ inputs.publish == false && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} - MB_ML_LIBC: ${{ matrix.MB_ML_LIBC }} - MB_ML_VER: ${{ matrix.MB_ML_VER }} INTERFACE64: ${{ matrix.INTERFACE64 }} - PLAT: ${{ matrix.PLAT }} steps: - uses: actions/checkout@v6.0.1 @@ -82,10 +85,6 @@ jobs: with: platforms: all - - name: Print some Environment variable - run: | - echo "PLAT: ${PLAT}" - # - name: Setup tmate session # if: ${{ failure() }} # uses: mxschmitt/action-tmate@v3 @@ -96,11 +95,12 @@ jobs: uses: pypa/cibuildwheel@v4.1.0 timeout-minutes: 60 with: + # For Android, cibuildwheel has a minimum of Python 3.13, but the exact version + # doesn't matter because setup.py will replace the tag with "py3". + only: "cp313-${{ matrix.MB_ML_LIBC }}_${{matrix.PLAT}}" output-dir: dist env: - CIBW_ARCHS: ${{matrix.PLAT}} CIBW_BUILD_VERBOSITY: 1 - CIBW_BUILD: "cp39-${{ matrix.MB_ML_LIBC }}_${{matrix.PLAT}}" CIBW_MANYLINUX_I686_IMAGE: ${{ matrix.MB_ML_LIBC }}${{matrix.MB_ML_VER}} CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.MB_ML_LIBC }}${{matrix.MB_ML_VER}} diff --git a/.gitignore b/.gitignore index 6f0c81a0..c10dea46 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ local/scipy_openblas64/*.so local/scipy_openblas64/*.pyd local/scipy_openblas64/*.dylib objconv/ +wheelhouse/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 39d527e6..85a318e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## OpenBLAS v0.3.34 (v0.3.34) +### 0.3.34.481.1 (2026-11-01) + +- Add Android wheels + ### 0.3.34.481.0 (2026-10-20) - update to latest develop HEAD, fix `NO_LIBGFORTRAN` patch diff --git a/build-openblas.sh b/build-openblas.sh index 20145b33..3c422c88 100755 --- a/build-openblas.sh +++ b/build-openblas.sh @@ -1,6 +1,11 @@ #! /bin/bash set -xeo pipefail + +# Check the required environment variables are set. +: ${MB_ML_LIBC:?} +: ${PLAT:?} + source tools/build_steps.sh echo "------ BEFORE BUILD ---------" before_build diff --git a/ci-before-build.sh b/ci-before-build.sh index f9d7e988..fb488210 100755 --- a/ci-before-build.sh +++ b/ci-before-build.sh @@ -29,6 +29,14 @@ fi export OPENBLAS_VERSION=$version echo "creating wheel from $OPENBLAS_COMMIT (NIGHTLY is $NIGHTLY)" +libc_plat=${CIBUILDWHEEL_BUILD_IDENTIFIER#*-} +MB_ML_LIBC=${libc_plat%%_*} +PLAT=${libc_plat#*_} + +BUILD_PREFIX="$(pwd)/build/prefix/$libc_plat" +rm -rf "$BUILD_PREFIX" +mkdir -p "$BUILD_PREFIX" + case "$PLAT" in loongarch64|ppc64le|s390x|riscv64) manylinux-install-clang -v 21.1.6.0 diff --git a/ci-test.sh b/ci-test.sh index 8330f6fb..82dc2c72 100755 --- a/ci-test.sh +++ b/ci-test.sh @@ -2,13 +2,8 @@ # Test that the wheel works with a different python set -xe -if [ "${PLAT}" == "arm64" ]; then - # Cannot test - exit 0 -fi - PYTHON=python3.9 -if [ "$(uname)" == "Darwin" -a "${PLAT}" == "x86_64" ]; then +if [ "$CIBUILDWHEEL_BUILD_IDENTIFIER" = "macosx_x86_64" ]; then which python3.9 PYTHON="arch -x86_64 python3.9" fi diff --git a/local/scipy_openblas64/__init__.py b/local/scipy_openblas64/__init__.py index 59e7214a..d578ee59 100644 --- a/local/scipy_openblas64/__init__.py +++ b/local/scipy_openblas64/__init__.py @@ -5,6 +5,7 @@ import ctypes import os from pathlib import Path +import re import sys from textwrap import dedent import platform @@ -91,11 +92,18 @@ def get_pkg_config(use_preloading=False, use_prefix=False): # Don't use `$prefix` since the file may be located outside the package tree libdir = get_lib_dir() incdir = get_include_dir() + + variables = {} + with open(f"{get_lib_dir()}/pkgconfig/scipy-openblas.pc") as pc_file: + for line in pc_file: + if match := re.fullmatch(r"(.+?)=(.*)", line.strip()): + variables[match[1].strip()] = match[2].strip() + return dedent(f"""\ libdir={libdir} includedir={incdir} - openblas_config= {get_openblas_config()} - version={get_openblas_config().split(" ")[1]} + openblas_config= {variables["openblas_config"]} + version={variables["version"]} extralib={extralib} Name: openblas Description: OpenBLAS is an optimized BLAS library based on GotoBLAS2 1.13 BSD version @@ -146,5 +154,10 @@ def get_openblas_config(): bytes = openblas_config() return bytes.decode("utf8") -# Import the DLL which will make the namespace available to NumPy/SciPy -get_openblas_config() + +if "CIBUILDWHEEL" in os.environ and sys.platform == "android": + # We're cross-compiling, so we can't load the library. + pass +else: + # Import the DLL which will make the namespace available to NumPy/SciPy + get_openblas_config() diff --git a/pyproject.toml b/pyproject.toml index cbf16624..f09b5d66 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [project] name = "scipy-openblas64" # 0.3.34-481-g5137bb62 -version = "0.3.34.481.0" +version = "0.3.34.481.1" requires-python = ">=3.7" description = "Provides OpenBLAS for python packaging" readme = "README.md" @@ -45,15 +45,27 @@ before-build = "bash ci-before-build.sh" repair-wheel-command = "bash ci-repair-wheel.sh {dest_dir} {wheel}" test-command = "cd {package} && bash ci-test.sh" environment-pass = [ - "OPENBLAS_COMMIT", "NIGHTLY", - "MB_ML_LIBC", - "MB_ML_VER", "INTERFACE64", - "BUILD_DIR", - "PLAT", - "OS-NAME", - "RUNNER_ARCH", ] + +[tool.cibuildwheel.android] +# The Linux code in ci-repair-wheel.sh isn't relevant to Android, so use the default +# repair command from the cibuildwheel documentation. +repair-wheel-command = "auditwheel repair --ldpaths {ldpaths} -w {dest_dir} {wheel}" + +# Mobile platforms only support Python test-commands, not shell scripts. +test-command = """python -c ' +import runpy + +def run_module(mod_name): + runpy.run_module(mod_name, run_name="__main__", alter_sys=True) + +try: + run_module("scipy_openblas32") +except ImportError: + run_module("scipy_openblas64") +'""" + [project.entry-points.pkg_config] scipy-openblas = 'scipy_openblas64.lib.pkgconfig' diff --git a/tools/build_prepare_wheel.sh b/tools/build_prepare_wheel.sh index ca5a5002..66dcac7f 100644 --- a/tools/build_prepare_wheel.sh +++ b/tools/build_prepare_wheel.sh @@ -12,7 +12,8 @@ PYTHON=${PYTHON:-python3.9} mkdir -p local/openblas mkdir -p dist -tar -C local/scipy_openblas64 --strip-components=2 -xf libs/openblas.tar.gz +rm -rf local/scipy_openblas64/{include,lib} +tar -C local/scipy_openblas64 -xf libs/openblas.tar.gz # do not package the static libs and symlinks, only take the shared object find local/scipy_openblas64/lib -maxdepth 1 -type l -delete @@ -32,7 +33,7 @@ if [[ $py_version != $pkg_version* ]]; then exit 1 fi -if [ $(uname) == "Darwin" ]; then +if [ "$MB_ML_LIBC" == "macosx" ]; then soname=$(cd local/scipy_openblas64/lib; ls libscipy_openblas*.dylib) echo otool -D local/scipy_openblas64/lib/$soname otool -D local/scipy_openblas64/lib/$soname @@ -41,8 +42,6 @@ if [ $(uname) == "Darwin" ]; then install_name_tool -id @rpath/$soname local/scipy_openblas64/lib/$soname fi -rm -rf local/scipy_openblas64/lib/pkgconfig -mkdir local/scipy_openblas64/lib/pkgconfig echo "" >> LICENSE.txt echo "----" >> LICENSE.txt echo "" >> LICENSE.txt @@ -67,11 +66,19 @@ if [ "${INTERFACE64}" != "1" ]; then sed -e "s/openblas64/openblas32/" -i.bak local/scipy_openblas32/__main__.py sed -e "s/openblas64/openblas32/" -i.bak local/scipy_openblas32/__init__.py rm local/scipy_openblas32/*.bak - PYTHONPATH=$PWD/local $PYTHON -c "import scipy_openblas32 as s; print(s.get_pkg_config(use_prefix=True))" > local/scipy_openblas32/lib/pkgconfig/scipy-openblas.pc + module="scipy_openblas32" else - PYTHONPATH=$PWD/local $PYTHON -c "import scipy_openblas64 as s; print(s.get_pkg_config(use_prefix=True))" > local/scipy_openblas64/lib/pkgconfig/scipy-openblas.pc + module="scipy_openblas64" fi +# get_pkg_config reads from the original pc_file generated by the OpenBLAS build, so +# redirect the output to a temporary file and then rename it over the original. +pc_file="local/$module/lib/pkgconfig/scipy-openblas.pc" +PYTHONPATH=$PWD/local $PYTHON -c \ + "import $module as s; print(s.get_pkg_config(use_prefix=True))" \ + > "$pc_file.tmp" +mv "$pc_file.tmp" "$pc_file" + rm -rf dist/* diff --git a/tools/build_steps.sh b/tools/build_steps.sh index 3758253d..34949ac2 100644 --- a/tools/build_steps.sh +++ b/tools/build_steps.sh @@ -1,9 +1,9 @@ #!bash -# Build script for manylinux and OSX +# Build script for posix platforms BUILD_PREFIX=${BUILD_PREFIX:-/usr/local} -ROOT_DIR=$(dirname $(dirname "${BASH_SOURCE[0]}")) +ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) MB_PYTHON_VERSION=3.9 @@ -11,7 +11,7 @@ source $(dirname "${BASH_SOURCE[0]}")/gfortran_compat.sh function before_build { # install gfortran, objconv on macOS - if [ "$(uname -s)" == "Darwin" ]; then + if [ "$MB_ML_LIBC" == "macosx" ]; then if [ ! -e /usr/local/lib ]; then sudo mkdir -p /usr/local/lib sudo chmod 777 /usr/local/lib @@ -55,7 +55,7 @@ function before_build { if [[ ! -x objconv/objconv ]]; then (cd ${ROOT_DIR}/objconv && bash ../tools/build_objconv.sh) fi - else + elif [ "$MB_ML_LIBC" == *linux ]; then # Red Hat keeps static libraries in separate *-static subpackages, so # the images on a system gcc rather than a gcc-toolset (manylinux_2_39, # i.e. riscv64) have no libgfortran.a. The archive is in the CRB repo, @@ -76,38 +76,12 @@ function clean_code { pushd OpenBLAS git fetch origin --tags git checkout $build_commit - git clean -fxd + git checkout . + git clean -fxdq git submodule update --init --recursive popd } -function get_plat_tag { - # Copied from gfortran-install/gfortran_utils.sh, modified for MB_ML_LIBC - - # Modify fat architecture tags on macOS to reflect compiled architecture - # For non-darwin, report manylinux version - local plat=$1 - local mb_ml_ver=${MB_ML_VER:-1} - local mb_ml_libc=${MB_ML_LIBC:-manylinux} - case $plat in - i686|x86_64|arm64|universal2|intel|aarch64|s390x|ppc64le|loongarch64|riscv64) ;; - *) echo Did not recognize plat $plat; return 1 ;; - esac - local uname=${2:-$(uname)} - if [ "$uname" != "Darwin" ]; then - if [ "$plat" == "intel" ]; then - echo plat=intel not allowed for Manylinux - return 1 - fi - echo "${mb_ml_libc}${mb_ml_ver}_${plat}" - return - fi - # macOS 32-bit arch is i386 - [ "$plat" == "i686" ] && plat="i386" - local target=$(echo $MACOSX_DEPLOYMENT_TARGET | tr .- _) - echo "macosx_${target}_${plat}" -} - function patch_source { # Runs inside OpenBLAS directory # Make the patches by git format-patch @@ -119,27 +93,26 @@ function patch_source { } function build_lib { - # OSX or manylinux build - # # Input arg # plat - one of i686, x86_64, arm64 # interface64 - 1 if build with INTERFACE64 and SYMBOLSUFFIX # # Depends on globals # BUILD_PREFIX - install suffix e.g. "/usr/local" - # MB_ML_VER + # MB_ML_LIBC set -x local plat=${1:-$PLAT} local interface64=${2:-$INTERFACE64} - case $(uname -s)-$plat in - Linux-x86_64) + local libc_plat=${MB_ML_LIBC}_$plat + case $libc_plat in + *linux_x86_64) local bitness=64 local target="PRESCOTT" local dynamic_list="PRESCOTT NEHALEM SANDYBRIDGE HASWELL SKYLAKEX" ;; - Darwin-x86_64) + macosx_x86_64) local bitness=64 local target="CORE2" CFLAGS="$CFLAGS -arch x86_64" @@ -147,39 +120,45 @@ function build_lib { export SDKROOT=${SDKROOT:-$(xcrun --show-sdk-path)} local dynamic_list="CORE2 NEHALEM SANDYBRIDGE HASWELL SKYLAKEX" ;; - *-i686) + android_x86_64) + # This target corresponds to the instruction set extensions listed at + # https://developer.android.com/ndk/guides/abis#86-64. + local bitness=64 + local target="NEHALEM" + ;; + *_i686) local bitness=32 local target="PRESCOTT" local dynamic_list="PRESCOTT NEHALEM SANDYBRIDGE HASWELL" ;; - Linux-aarch64) + *linux_aarch6 | android_arm64_v8a) local bitness=64 local target="ARMV8" ;; - Darwin-arm64) + macosx_arm64) local bitness=64 local target="VORTEX" CFLAGS="$CFLAGS -ftrapping-math -mmacos-version-min=11.0" MACOSX_DEPLOYMENT_TARGET="11.0" export SDKROOT=${SDKROOT:-$(xcrun --show-sdk-path)} ;; - *-s390x) + *_s390x) # The TargetList.txt has only ZARCH_GENERIC, Z13, Z14. Not worth # messing with dynamic lists. local bitness=64 local target="ZARCH_GENERIC" ;; - *-ppc64le) + *_ppc64le) local bitness=64 local target="POWER8" ;; - Linux-loongarch64) + *linux_loongarch64) local target="GENERIC" ;; - Linux-riscv64) + *linux_riscv64) local target="GENERIC" ;; - *) echo "Strange plat value $plat"; exit 1 ;; + *) echo "Strange libc_plat value '$libc_plat'"; exit 1 ;; esac case $interface64 in 1) @@ -193,17 +172,32 @@ function build_lib { esac interface_flags="$interface_flags SYMBOLPREFIX=scipy_ LIBNAMEPREFIX=scipy_ FIXED_LIBNAME=1" - # Replace the libgfortran/libquadmath runtime dependency with the compat - # archive; a toolchain that cannot supply it fails the build. TIMER=NONE - # selects LAPACK's second_NONE.f/dsecnd_NONE.f, removing the only - # reference to _gfortran_etime. See tools/gfortran_compat.sh. - local compat_lib - if ! compat_lib=$(build_gfortran_compat "$PWD/build/gfortran_compat" "$interface64"); then - echo "ERROR: cannot build the libgfortran compat archive for" \ - "${MB_ML_LIBC:-manylinux}-$plat" >&2 - exit 1 + if [ "$MB_ML_LIBC" = "android" ]; then + # It's difficult to install a Fortran compiler for this platform. + interface_flags+=" NOFORTRAN=1" + + # This is the compiler for the build machine, which is used to generate some + # build files. + interface_flags+=" HOSTCC=cc" + + # OpenBLAS assumes objcopy has the same filename prefix as CC. + interface_flags+=" OBJCOPY=$(dirname "$CC")/llvm-objcopy" + else + # DYNAMIC_ARCH isn't enabled on Android because library size is more important + # on that platform. + interface_flags+=" DYNAMIC_ARCH=1" + + # Replace the libgfortran/libquadmath runtime dependency with the compat + # archive; a toolchain that cannot supply it fails the build. TIMER=NONE + # selects LAPACK's second_NONE.f/dsecnd_NONE.f, removing the only + # reference to _gfortran_etime. See tools/gfortran_compat.sh. + local compat_lib + if ! compat_lib=$(build_gfortran_compat "$PWD/build/gfortran_compat" "$interface64"); then + echo "ERROR: cannot build the libgfortran compat archive for $libc_plat" >&2 + exit 1 + fi + interface_flags="$interface_flags NO_LIBGFORTRAN=1 LIBGFORTRAN_COMPAT=$compat_lib TIMER=NONE" fi - interface_flags="$interface_flags NO_LIBGFORTRAN=1 LIBGFORTRAN_COMPAT=$compat_lib TIMER=NONE" mkdir -p libs set -x @@ -225,23 +219,16 @@ function build_lib { echo "QEMU does not support the 'lper' /'lpdr' instructions used" fi if [ -n "$dynamic_list" ]; then - CFLAGS="$CFLAGS -fvisibility=protected -Wno-uninitialized" \ - make BUFFERSIZE=20 DYNAMIC_ARCH=1 QUIET_MAKE=1 \ - USE_OPENMP=0 NUM_THREADS=64 \ - DYNAMIC_LIST="$dynamic_list" \ - BINARY="$bitness" $interface_flags \ - TARGET="$target" - else - CFLAGS="$CFLAGS -fvisibility=protected -Wno-uninitialized" \ - make BUFFERSIZE=20 DYNAMIC_ARCH=1 QUIET_MAKE=1 \ - USE_OPENMP=0 NUM_THREADS=64 \ - BINARY="$bitness" $interface_flags \ - TARGET="$target" + interface_flags+=" DYNAMIC_LIST=$dynamic_list" fi + CFLAGS="$CFLAGS -fvisibility=protected -Wno-uninitialized" \ + make BUFFERSIZE=20 QUIET_MAKE=1 \ + USE_OPENMP=0 NUM_THREADS=64 \ + BINARY="$bitness" $interface_flags \ + TARGET="$target" make PREFIX=$BUILD_PREFIX $interface_flags install popd mv $BUILD_PREFIX/lib/pkgconfig/openblas*.pc $BUILD_PREFIX/lib/pkgconfig/scipy-openblas.pc - local plat_tag=$(get_plat_tag $plat) if [ "$interface64" = "1" ]; then # OpenBLAS does not install the symbol suffixed static library, # do it ourselves @@ -251,29 +238,33 @@ function build_lib { sed -e "s/\(^Cflags.*\)/\1 -DBLAS_SYMBOL_PREFIX=scipy_ -DBLAS_SYMBOL_SUFFIX=64_/" -i.bak $BUILD_PREFIX/lib/pkgconfig/scipy-openblas.pc else sed -e "s/\(^Cflags.*\)/\1 -DBLAS_SYMBOL_PREFIX=scipy_/" -i.bak $BUILD_PREFIX/lib/pkgconfig/scipy-openblas.pc - rm $BUILD_PREFIX/lib/pkgconfig/scipy-openblas.pc.bak fi + rm $BUILD_PREFIX/lib/pkgconfig/scipy-openblas.pc.bak - # The shared library picked these up from the link line (see - # LIBGFORTRAN_COMPAT_LINK in the patch), but the static one is assembled - # from OpenBLAS's own objects only; add them there too, so that linking - # libscipy_openblas*.a does not need libgfortran either. Must come after - # the INTERFACE64 branch above, which overwrites the installed archive. - 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 + 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 one is assembled + # from OpenBLAS's own objects only; add them there too, so that linking + # libscipy_openblas*.a does not need libgfortran either. Must come after + # the INTERFACE64 branch above, which overwrites the installed archive. + 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* \ - $BUILD_PREFIX/include/*lapack* \ - $BUILD_PREFIX/lib/libscipy_openblas* \ - $BUILD_PREFIX/lib/pkgconfig/scipy-openblas* \ - $BUILD_PREFIX/lib/cmake/openblas + pushd $BUILD_PREFIX + tar zcvf $ROOT_DIR/libs/$out_name \ + include/*blas* \ + include/*lapack* \ + lib/libscipy_openblas* \ + lib/pkgconfig/scipy-openblas* \ + lib/cmake/openblas + popd } From 1836f2ce58a272c1ddad7a24affac863cd28fca8 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sat, 31 Oct 2026 19:37:55 +0000 Subject: [PATCH 2/8] Temporarily run workflows on all branches --- .github/workflows/checks.yml | 1 + .github/workflows/posix.yml | 1 - .github/workflows/windows-arm.yml | 1 - .github/workflows/windows.yml | 1 - 4 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 045d18ab..947c2e70 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,6 +1,7 @@ name: Check Changelog on: + push: pull_request: branches: - main diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index ea6303ca..bf922f79 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -2,7 +2,6 @@ name: posix on: push: - branches: [ main ] pull_request: branches: - main diff --git a/.github/workflows/windows-arm.yml b/.github/workflows/windows-arm.yml index 863f7bef..acee4a6d 100644 --- a/.github/workflows/windows-arm.yml +++ b/.github/workflows/windows-arm.yml @@ -2,7 +2,6 @@ name: Windows-on-ARM on: push: - branches: [ main ] pull_request: branches: - main diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 62eeecd3..43c2e285 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -2,7 +2,6 @@ name: Win on: push: - branches: [ main ] pull_request: branches: - main From 0c759955355134a905ec2b5fd42175ce71294084 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sat, 31 Oct 2026 22:33:29 +0000 Subject: [PATCH 3/8] Linux and macOS fixes --- .github/workflows/posix.yml | 1 + ci-repair-wheel.sh | 4 +--- ci-test.sh | 6 ++---- tools/build_prepare_wheel.sh | 1 - tools/build_steps.sh | 27 ++++++++++++++------------- 5 files changed, 18 insertions(+), 21 deletions(-) diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index bf922f79..3e451a22 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -19,6 +19,7 @@ on: jobs: build: + name: ${{ matrix.MB_ML_LIBC }}_${{ matrix.PLAT}}, ${{ matrix.INTERFACE64 == '1' && '64' || '32' }} runs-on: ${{ matrix.os }} defaults: run: diff --git a/ci-repair-wheel.sh b/ci-repair-wheel.sh index 0758ee1a..05f3bce2 100755 --- a/ci-repair-wheel.sh +++ b/ci-repair-wheel.sh @@ -1,10 +1,8 @@ #! /bin/bash set -xe -PYTHON=${PYTHON:-python3.9} - if [ $(uname) == "Darwin" ]; then - $PYTHON -m pip install delocate + python3 -m pip install delocate # move the mis-named scipy_openblas64-none-any.whl to a platform-specific name # if [ "${PLAT}" == "arm64" ]; then # for f in $2/*.whl; do mv $f "${f/%any.whl/macosx_11_0_$PLAT.whl}"; done diff --git a/ci-test.sh b/ci-test.sh index 82dc2c72..3b58d565 100755 --- a/ci-test.sh +++ b/ci-test.sh @@ -1,11 +1,9 @@ #!/bin/bash -# Test that the wheel works with a different python set -xe -PYTHON=python3.9 +PYTHON=python3 if [ "$CIBUILDWHEEL_BUILD_IDENTIFIER" = "macosx_x86_64" ]; then - which python3.9 - PYTHON="arch -x86_64 python3.9" + PYTHON="arch -x86_64 python3" fi # Make sure the module works and that the version strings match diff --git a/tools/build_prepare_wheel.sh b/tools/build_prepare_wheel.sh index 66dcac7f..deab8f62 100644 --- a/tools/build_prepare_wheel.sh +++ b/tools/build_prepare_wheel.sh @@ -7,7 +7,6 @@ if [ "$?" != "0" ]; then # inside docker cd /project fi -PYTHON=${PYTHON:-python3.9} mkdir -p local/openblas mkdir -p dist diff --git a/tools/build_steps.sh b/tools/build_steps.sh index 34949ac2..60ca84a7 100644 --- a/tools/build_steps.sh +++ b/tools/build_steps.sh @@ -5,8 +5,6 @@ BUILD_PREFIX=${BUILD_PREFIX:-/usr/local} ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) -MB_PYTHON_VERSION=3.9 - source $(dirname "${BASH_SOURCE[0]}")/gfortran_compat.sh function before_build { @@ -22,9 +20,6 @@ function before_build { sudo chmod 777 /usr/local/include touch /usr/local/include/.dir_exists fi - # get_macpython_environment ${MB_PYTHON_VERSION} venv - python3.9 -m venv venv - source venv/bin/activate unalias gfortran 2>/dev/null || true source tools/gfortran_utils.sh @@ -55,7 +50,7 @@ function before_build { if [[ ! -x objconv/objconv ]]; then (cd ${ROOT_DIR}/objconv && bash ../tools/build_objconv.sh) fi - elif [ "$MB_ML_LIBC" == *linux ]; then + elif [[ "$MB_ML_LIBC" == *linux ]]; then # Red Hat keeps static libraries in separate *-static subpackages, so # the images on a system gcc rather than a gcc-toolset (manylinux_2_39, # i.e. riscv64) have no libgfortran.a. The archive is in the CRB repo, @@ -131,7 +126,7 @@ function build_lib { local target="PRESCOTT" local dynamic_list="PRESCOTT NEHALEM SANDYBRIDGE HASWELL" ;; - *linux_aarch6 | android_arm64_v8a) + *linux_aarch64 | android_arm64_v8a) local bitness=64 local target="ARMV8" ;; @@ -219,13 +214,19 @@ function build_lib { echo "QEMU does not support the 'lper' /'lpdr' instructions used" fi if [ -n "$dynamic_list" ]; then - interface_flags+=" DYNAMIC_LIST=$dynamic_list" + CFLAGS="$CFLAGS -fvisibility=protected -Wno-uninitialized" \ + make BUFFERSIZE=20 QUIET_MAKE=1 \ + USE_OPENMP=0 NUM_THREADS=64 \ + DYNAMIC_LIST="$dynamic_list" \ + BINARY="$bitness" $interface_flags \ + TARGET="$target" + else + CFLAGS="$CFLAGS -fvisibility=protected -Wno-uninitialized" \ + make BUFFERSIZE=20 QUIET_MAKE=1 \ + USE_OPENMP=0 NUM_THREADS=64 \ + BINARY="$bitness" $interface_flags \ + TARGET="$target" fi - CFLAGS="$CFLAGS -fvisibility=protected -Wno-uninitialized" \ - make BUFFERSIZE=20 QUIET_MAKE=1 \ - USE_OPENMP=0 NUM_THREADS=64 \ - BINARY="$bitness" $interface_flags \ - TARGET="$target" make PREFIX=$BUILD_PREFIX $interface_flags install popd mv $BUILD_PREFIX/lib/pkgconfig/openblas*.pc $BUILD_PREFIX/lib/pkgconfig/scipy-openblas.pc From 8da2f0afebee431b12034690587147c17a2d37e9 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sat, 31 Oct 2026 23:16:08 +0000 Subject: [PATCH 4/8] Windows fixes --- .github/workflows/windows.yml | 9 ++++----- tools/build_prepare_wheel.sh | 16 +--------------- tools/get_pkg_config.sh | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 20 deletions(-) create mode 100644 tools/get_pkg_config.sh diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 43c2e285..e0099838 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -118,7 +118,6 @@ jobs: rm local/scipy_openblas64/lib/*.a rm -f local/scipy_openblas64/lib/*.exp # may not exist? rm local/scipy_openblas64/lib/*.def - rm -rf local/scipy_openblas64/lib/pkgconfig if [[ -d local/scipy_openblas64/64 ]]; then rm -rf local/scipy_openblas64/64 else @@ -126,7 +125,7 @@ jobs: fi sed -e "s/bin/lib/" -i local/scipy_openblas64/lib/cmake/openblas/OpenBLASConfig.cmake sed -e "s/dll/lib/" -i local/scipy_openblas64/lib/cmake/openblas/OpenBLASConfig.cmake - mkdir local/scipy_openblas64/lib/pkgconfig + if [[ "${INTERFACE64}" != "1" ]]; then mv local/scipy_openblas64 local/scipy_openblas32 # rewrite the name of the project to scipy-openblas32 @@ -137,10 +136,10 @@ jobs: sed -e "s/cflags =.*/cflags = '-DBLAS_SYMBOL_PREFIX=scipy_'/" -i local/scipy_openblas32/__init__.py sed -e "s/openblas64/openblas32/" -i local/scipy_openblas32/__init__.py sed -e "s/openblas64/openblas32/" -i local/scipy_openblas32/__main__.py - PYTHONPATH=$PWD/local python -c "import scipy_openblas32 as s; print(s.get_pkg_config(use_prefix=True))" > local/scipy_openblas32/lib/pkgconfig/scipy-openblas.pc - else - PYTHONPATH=$PWD/local python -c "import scipy_openblas64 as s; print(s.get_pkg_config(use_prefix=True))" > local/scipy_openblas64/lib/pkgconfig/scipy-openblas.pc fi + + source tools/get_pkg_config.sh + echo "" >> LICENSE.txt echo "----" >> LICENSE.txt echo "" >> LICENSE.txt diff --git a/tools/build_prepare_wheel.sh b/tools/build_prepare_wheel.sh index deab8f62..93e39448 100644 --- a/tools/build_prepare_wheel.sh +++ b/tools/build_prepare_wheel.sh @@ -46,11 +46,6 @@ echo "----" >> LICENSE.txt echo "" >> LICENSE.txt cat tools/LICENSE_posix.txt >> LICENSE.txt -if [ "$(uname)" == "Darwin" -a "${PLAT}" == "x86_64" ]; then - PYTHON="arch -x86_64 python3" -else - PYTHON=python3 -fi if [ "${INTERFACE64}" != "1" ]; then # rewrite the name of the project to scipy-openblas32 # this is a hack, but apparently there is no other way to change the name @@ -65,18 +60,9 @@ if [ "${INTERFACE64}" != "1" ]; then sed -e "s/openblas64/openblas32/" -i.bak local/scipy_openblas32/__main__.py sed -e "s/openblas64/openblas32/" -i.bak local/scipy_openblas32/__init__.py rm local/scipy_openblas32/*.bak - module="scipy_openblas32" -else - module="scipy_openblas64" fi -# get_pkg_config reads from the original pc_file generated by the OpenBLAS build, so -# redirect the output to a temporary file and then rename it over the original. -pc_file="local/$module/lib/pkgconfig/scipy-openblas.pc" -PYTHONPATH=$PWD/local $PYTHON -c \ - "import $module as s; print(s.get_pkg_config(use_prefix=True))" \ - > "$pc_file.tmp" -mv "$pc_file.tmp" "$pc_file" +source tools/get_pkg_config.sh rm -rf dist/* diff --git a/tools/get_pkg_config.sh b/tools/get_pkg_config.sh new file mode 100644 index 00000000..d15e4735 --- /dev/null +++ b/tools/get_pkg_config.sh @@ -0,0 +1,19 @@ +if [ "$(uname)" == "Darwin" -a "${PLAT}" == "x86_64" ]; then + PYTHON="arch -x86_64 python" +else + PYTHON=python +fi + +if [ "${INTERFACE64}" != "1" ]; then + module="scipy_openblas32" +else + module="scipy_openblas64" +fi + +# get_pkg_config reads from the original pc_file generated by the OpenBLAS build, so +# redirect the output to a temporary file and then rename it over the original. +pc_file="local/$module/lib/pkgconfig/scipy-openblas.pc" +PYTHONPATH=$PWD/local $PYTHON -c \ + "import $module as s; print(s.get_pkg_config(use_prefix=True))" \ + > "$pc_file.tmp" +mv "$pc_file.tmp" "$pc_file" From c1b4e59cb40355344a3d8ae15fac06e50b2419f0 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sun, 1 Nov 2026 00:26:02 +0000 Subject: [PATCH 5/8] Linux emulation and Windows ARM64 fixes --- ci-before-build.sh | 2 ++ pyproject.toml | 4 ++++ tools/build_steps_win_arm64.bat | 21 ++------------------- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/ci-before-build.sh b/ci-before-build.sh index fb488210..9a7cd67e 100755 --- a/ci-before-build.sh +++ b/ci-before-build.sh @@ -37,6 +37,8 @@ BUILD_PREFIX="$(pwd)/build/prefix/$libc_plat" rm -rf "$BUILD_PREFIX" mkdir -p "$BUILD_PREFIX" +# On emulated architectures, RUNNER_ARCH makes manylinux-install-clang install a +# compiler for the native architecture, which will be much faster. case "$PLAT" in loongarch64|ppc64le|s390x|riscv64) manylinux-install-clang -v 21.1.6.0 diff --git a/pyproject.toml b/pyproject.toml index 88fb1db9..c15e9b18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,10 @@ test-command = "cd {package} && bash ci-test.sh" environment-pass = [ "NIGHTLY", "INTERFACE64", + + # On emulated architectures, RUNNER_ARCH makes manylinux-install-clang install a + # compiler for the native architecture, which will be much faster. + "RUNNER_ARCH", ] [tool.cibuildwheel.android] diff --git a/tools/build_steps_win_arm64.bat b/tools/build_steps_win_arm64.bat index 94b1c0f3..41b9289c 100755 --- a/tools/build_steps_win_arm64.bat +++ b/tools/build_steps_win_arm64.bat @@ -238,30 +238,13 @@ xcopy /Y "..\lapack-netlib\lapacke\include\*.h" "%DEST_DIR%\include\" if errorlevel 1 exit /b 1 echo Create pkgconfig scipy-openblas.pc -cd ../../local -if errorlevel 1 ( - echo Current directory %CD%, cannot cd ../../local - exit /b 1 -) -if "%if_bits%"=="32" ( - mkdir scipy_openblas32\lib\pkgconfig - python -c "import scipy_openblas32 as s; print(s.get_pkg_config(use_prefix=True))" > scipy_openblas32/lib/pkgconfig/scipy-openblas.pc -) else ( - mkdir scipy_openblas64\lib\pkgconfig - python -c "import scipy_openblas64 as s; print(s.get_pkg_config(use_prefix=True))" > scipy_openblas64/lib/pkgconfig/scipy-openblas.pc -) +cd ../.. +bash tools\get_pkg_config.sh if errorlevel 1 ( echo could not construct scipy-openblas.pc exit /b 1 ) -REM Move back to the root directory -cd .. -if errorlevel 1 ( - echo Current directory %CD%, cannot cd .. - exit /b 1 -) - REM Build the Wheel & Install It echo Running 'python -m build' to build the wheel in %CD% python -c "import build" 2>NUL || pip install build From 5d92f36c0749667be1e7f27832b350c9d6b6e969 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sun, 1 Nov 2026 01:14:21 +0000 Subject: [PATCH 6/8] Windows ARM debugging --- tools/build_steps_win_arm64.bat | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/build_steps_win_arm64.bat b/tools/build_steps_win_arm64.bat index 41b9289c..218e4c32 100755 --- a/tools/build_steps_win_arm64.bat +++ b/tools/build_steps_win_arm64.bat @@ -238,8 +238,13 @@ xcopy /Y "..\lapack-netlib\lapacke\include\*.h" "%DEST_DIR%\include\" if errorlevel 1 exit /b 1 echo Create pkgconfig scipy-openblas.pc + +echo FIXME +bash -c "find . -name *.pc | xargs ls -l" +bash -c "find . -name *.pc | xargs cat" + cd ../.. -bash tools\get_pkg_config.sh +bash -e tools\get_pkg_config.sh if errorlevel 1 ( echo could not construct scipy-openblas.pc exit /b 1 From bb49192d32a1ed290dbd8a3bf96290f2b4670551 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sun, 1 Nov 2026 01:50:26 +0000 Subject: [PATCH 7/8] Windows ARM fixes --- local/scipy_openblas64/__init__.py | 6 ++++-- tools/build_steps_win_arm64.bat | 10 ++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/local/scipy_openblas64/__init__.py b/local/scipy_openblas64/__init__.py index d578ee59..06bb744e 100644 --- a/local/scipy_openblas64/__init__.py +++ b/local/scipy_openblas64/__init__.py @@ -96,14 +96,16 @@ def get_pkg_config(use_preloading=False, use_prefix=False): variables = {} with open(f"{get_lib_dir()}/pkgconfig/scipy-openblas.pc") as pc_file: for line in pc_file: - if match := re.fullmatch(r"(.+?)=(.*)", line.strip()): + # The CMake build used by Windows ARM64 produces a Version: keyword but not + # a version= variable, so parse both kinds of line. + if match := re.fullmatch(r"(.+?)[:=](.*)", line.strip()): variables[match[1].strip()] = match[2].strip() return dedent(f"""\ libdir={libdir} includedir={incdir} openblas_config= {variables["openblas_config"]} - version={variables["version"]} + version={variables.get("version") or variables["Version"]} extralib={extralib} Name: openblas Description: OpenBLAS is an optimized BLAS library based on GotoBLAS2 1.13 BSD version diff --git a/tools/build_steps_win_arm64.bat b/tools/build_steps_win_arm64.bat index 218e4c32..5284eb16 100755 --- a/tools/build_steps_win_arm64.bat +++ b/tools/build_steps_win_arm64.bat @@ -224,7 +224,7 @@ REM Copy CMake configuration files echo Copying CMake configuration files... if exist openblasconfig.cmake copy /Y openblasconfig.cmake "%DEST_DIR%\lib\cmake\openblas\" if exist openblasconfigversion.cmake copy /Y openblasconfigversion.cmake "%DEST_DIR%\lib\cmake\openblas\" - + REM Copy header files echo Copying generated header files... if exist generated xcopy /E /Y generated "%DEST_DIR%\include\" @@ -238,11 +238,9 @@ xcopy /Y "..\lapack-netlib\lapacke\include\*.h" "%DEST_DIR%\include\" if errorlevel 1 exit /b 1 echo Create pkgconfig scipy-openblas.pc - -echo FIXME -bash -c "find . -name *.pc | xargs ls -l" -bash -c "find . -name *.pc | xargs cat" - +if not exist "%DEST_DIR%\lib\pkgconfig" mkdir "%DEST_DIR%\lib\pkgconfig" +copy /Y openblas*.pc "%DEST_DIR%\lib\pkgconfig\scipy-openblas.pc" +if errorlevel 1 exit /b 1 cd ../.. bash -e tools\get_pkg_config.sh if errorlevel 1 ( From ea560de7bb8a343780b6e91dcc939410d3033e28 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Sun, 1 Nov 2026 17:45:55 +0000 Subject: [PATCH 8/8] Revert temporary changes --- .github/workflows/checks.yml | 1 - .github/workflows/posix.yml | 1 + .github/workflows/windows-arm.yml | 1 + .github/workflows/windows.yml | 1 + tools/build_steps_win_arm64.bat | 2 +- 5 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 947c2e70..045d18ab 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,7 +1,6 @@ name: Check Changelog on: - push: pull_request: branches: - main diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index 3e451a22..c1e0a7ea 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -2,6 +2,7 @@ name: posix on: push: + branches: [ main ] pull_request: branches: - main diff --git a/.github/workflows/windows-arm.yml b/.github/workflows/windows-arm.yml index acee4a6d..863f7bef 100644 --- a/.github/workflows/windows-arm.yml +++ b/.github/workflows/windows-arm.yml @@ -2,6 +2,7 @@ name: Windows-on-ARM on: push: + branches: [ main ] pull_request: branches: - main diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index e0099838..68178609 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -2,6 +2,7 @@ name: Win on: push: + branches: [ main ] pull_request: branches: - main diff --git a/tools/build_steps_win_arm64.bat b/tools/build_steps_win_arm64.bat index 5284eb16..6446b3ca 100755 --- a/tools/build_steps_win_arm64.bat +++ b/tools/build_steps_win_arm64.bat @@ -224,7 +224,7 @@ REM Copy CMake configuration files echo Copying CMake configuration files... if exist openblasconfig.cmake copy /Y openblasconfig.cmake "%DEST_DIR%\lib\cmake\openblas\" if exist openblasconfigversion.cmake copy /Y openblasconfigversion.cmake "%DEST_DIR%\lib\cmake\openblas\" - + REM Copy header files echo Copying generated header files... if exist generated xcopy /E /Y generated "%DEST_DIR%\include\"