diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..4bce0d61 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Patches must reach the build byte-identical: their line endings have to match +# the files they patch, and checkout conversion would break that. +*.patch -text diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index 2e77ec60..1e1b8b3b 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -119,8 +119,19 @@ jobs: name: wheels-${{ matrix.os }}-${{ matrix.PLAT }}-${{ matrix.INTERFACE64 }}-${{ matrix.MB_ML_LIBC }}-${{ matrix.MB_ML_VER }} path: dist/scipy_openblas*.whl + upload: + name: Upload to anaconda.org + needs: build + if: ${{ inputs.publish == false && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + with: + pattern: wheels-* + merge-multiple: true + path: dist + - name: Upload to ananconda.org - if: ${{ inputs.publish == false && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} uses: scientific-python/upload-nightly-action@0.6.4 with: artifacts_path: dist diff --git a/.github/workflows/windows-arm.yml b/.github/workflows/windows-arm.yml index d2f2a512..060ff817 100644 --- a/.github/workflows/windows-arm.yml +++ b/.github/workflows/windows-arm.yml @@ -81,22 +81,27 @@ jobs: name: wheels-${{ env.PLAT }}-${{ env.INTERFACE64 }} path: dist/scipy_openblas*.whl - - name: Install Anaconda client - run: | - # Rust installation needed for rpds-py. - Invoke-WebRequest https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe -UseBasicParsing -Outfile rustup-init.exe - .\rustup-init.exe -y - $env:PATH="$env:PATH;$env:USERPROFILE\.cargo\bin" - # cryptography dropped win-arm64 for 46.0.4, make sure we don't try to compile from source - pip install --only-binary :all: cryptography - pip install anaconda-client + upload: + name: Upload to anaconda.org + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6.0.1 + + - uses: actions/setup-python@v6.2.0 + with: + python-version: 3.11 + + - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + with: + pattern: wheels-* + merge-multiple: true + path: dist - name: Upload - # see https://github.com/marketplace/actions/setup-miniconda for why - # `-el {0}` is required. - shell: bash -el {0} env: ANACONDA_SCIENTIFIC_PYTHON_UPLOAD: ${{ secrets.ANACONDA_SCIENTIFIC_PYTHON_UPLOAD }} run: | + python -m pip install anaconda-client source tools/upload_to_anaconda_staging.sh upload_wheels diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9601ac82..303c422f 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -168,20 +168,27 @@ jobs: python -m pip install pkgconf python -m pkgconf scipy-openblas --cflags - - uses: conda-incubator/setup-miniconda@v4.0.1 + upload: + name: Upload to anaconda.org + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6.0.1 + + - uses: actions/setup-python@v6.2.0 + with: + python-version: 3.11 + + - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: - channels: conda-forge - channel-priority: true - activate-environment: upload - miniforge-version: latest + pattern: wheels-* + merge-multiple: true + path: dist - name: Upload - # see https://github.com/marketplace/actions/setup-miniconda for why - # `-el {0}` is required. - shell: bash -el {0} env: ANACONDA_SCIENTIFIC_PYTHON_UPLOAD: ${{ secrets.ANACONDA_SCIENTIFIC_PYTHON_UPLOAD }} run: | - conda install -y anaconda-client + python -m pip install anaconda-client source tools/upload_to_anaconda_staging.sh upload_wheels diff --git a/CHANGELOG.md b/CHANGELOG.md index 9570f875..da5c7414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## OpenBLAS v0.3.34 (v0.3.34) +### 0.3.34.237.2 (2026-10-16) + +- Patch windows to use a per-thread queue instead of mistakenly sharing the + thread queue. Also simplify windows-arm64 upload to anaconda + ### 0.3.34.237.1 (2026-10-09) - Build every manylinux, musllinux and macOS wheel without a runtime diff --git a/patches-windows/openblas-make-libs.patch b/patches-windows/openblas-make-libs.patch index 1e4d4aeb..9fd3ba99 100644 --- a/patches-windows/openblas-make-libs.patch +++ b/patches-windows/openblas-make-libs.patch @@ -1,8 +1,7 @@ diff --git a/exports/Makefile b/exports/Makefile -index 668a4866e..956b51bf4 100644 --- a/exports/Makefile +++ b/exports/Makefile -@@ -126,8 +126,9 @@ dll : ../$(LIBDLLNAME) +@@ -129,8 +129,9 @@ dll : ../$(LIBDLLNAME) ../$(LIBDLLNAME) : ../$(LIBNAME) $(LIBPREFIX).def dllinit.$(SUFFIX) $(RANLIB) ../$(LIBNAME) $(CC) $(CFLAGS) $(LDFLAGS) $(LIBPREFIX).def dllinit.$(SUFFIX) \ @@ -12,4 +11,4 @@ index 668a4866e..956b51bf4 100644 + dlltool -D $(LIBDLLNAME) -d $(LIBPREFIX).def -l ../$(LIBDLLNAME).a $(LIBPREFIX).def : $(GENSYM) - ./$(GENSYM) win2k $(ARCH) dummy $(EXPRECISION) $(NO_CBLAS) $(NO_LAPACK) $(NO_LAPACKE) $(NEED2UNDERSCORES) $(ONLY_CBLAS) "$(SYMBOLPREFIX)" "$(SYMBOLSUFFIX)" $(BUILD_LAPACK_DEPRECATED) $(BUILD_BFLOAT16) $(BUILD_SINGLE) $(BUILD_DOUBLE) $(BUILD_COMPLEX) $(BUILD_COMPLEX16) > $(@F) + ./$(GENSYM) win2k $(ARCH) dummy $(EXPRECISION) $(NO_CBLAS) $(NO_LAPACK) $(NO_LAPACKE) $(NEED2UNDERSCORES) $(ONLY_CBLAS) "$(SYMBOLPREFIX)" "$(SYMBOLSUFFIX)" $(BUILD_LAPACK_DEPRECATED) $(BUILD_BFLOAT16) $(BUILD_HFLOAT16) $(BUILD_SINGLE) $(BUILD_DOUBLE) $(BUILD_COMPLEX) $(BUILD_COMPLEX16) > $(@F) diff --git a/patches-windows/openblas-win32-pool-queue-link.patch b/patches-windows/openblas-win32-pool-queue-link.patch new file mode 100644 index 00000000..98f78387 --- /dev/null +++ b/patches-windows/openblas-win32-pool-queue-link.patch @@ -0,0 +1,95 @@ +Give the Windows thread pool its own queue link, see MacPython/openblas-libs#283 + +diff --git a/common_thread.h b/common_thread.h +--- a/common_thread.h ++++ b/common_thread.h +@@ -119,6 +119,14 @@ typedef struct blas_queue { + struct blas_queue *next; + + #if defined( __WIN32__) || defined(__CYGWIN32__) || defined(_WIN32) || defined(__CYGWIN__) ++ /* ++ * Link used exclusively by the Windows thread pool to chain the jobs that ++ * are waiting to be picked up. It is deliberately kept separate from ++ * "next" above, which is owned (and rebuilt on every call) by whoever ++ * submitted the job: appending to the pool must never write into the ++ * queue nodes of another caller. ++ */ ++ struct blas_queue *pool_next; + CRITICAL_SECTION lock; + HANDLE finish; + volatile int finished; +diff --git a/driver/others/blas_server_win32.c b/driver/others/blas_server_win32.c +--- a/driver/others/blas_server_win32.c ++++ b/driver/others/blas_server_win32.c +@@ -57,6 +57,7 @@ typedef struct{ + HANDLE killed; + + blas_queue_t *queue; /* Parameter Pointer */ ++ blas_queue_t *tail; /* Last job waiting in "queue", for O(1) append */ + int shutdown; /* server shutdown flag */ + + } blas_pool_t; +@@ -239,7 +240,10 @@ static DWORD WINAPI blas_thread_server(void *arg){ + EnterCriticalSection(&pool.lock); + + queue = pool.queue; +- if (queue) pool.queue = queue->next; ++ if (queue) { ++ pool.queue = queue->pool_next; ++ if (pool.queue == NULL) pool.tail = NULL; ++ } + + LeaveCriticalSection(&pool.lock); + +@@ -374,6 +378,7 @@ int blas_thread_init(void){ + + pool.shutdown = 0; + pool.queue = NULL; ++ pool.tail = NULL; + + for(i = 0; i < blas_cpu_number - 1; i++){ + blas_threads[i] = CreateThread(NULL, 0, +@@ -405,7 +410,7 @@ int exec_blas_async(BLASLONG pos, blas_queue_t *queue){ + if (unlikely(blas_server_avail == 0)) blas_thread_init(); + #endif + +- blas_queue_t *current; ++ blas_queue_t *current, *last = NULL; + + current = queue; + +@@ -419,19 +424,22 @@ int exec_blas_async(BLASLONG pos, blas_queue_t *queue){ + __asm__ __volatile__ ("stmxcsr %0" : "=m" (current -> sse_mode)); + #endif + ++ /* Build the pool's own chain over this caller's jobs */ ++ current -> pool_next = current -> next; ++ last = current; ++ + current = current -> next; + pos ++; + } + + EnterCriticalSection(&pool.lock); + +- if (pool.queue) { +- current = pool.queue; +- while (current -> next) current = current -> next; +- current -> next = queue; ++ if (pool.tail) { ++ pool.tail -> pool_next = queue; + } else { + pool.queue = queue; + } ++ pool.tail = last; + + LeaveCriticalSection(&pool.lock); + +@@ -568,6 +576,7 @@ void goto_set_num_threads(int num_threads) + + pool.shutdown = 0; + pool.queue = NULL; ++ pool.tail = NULL; + blas_server_avail = 1; + } + diff --git a/pyproject.toml b/pyproject.toml index 13381375..69ed4ff2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [project] name = "scipy-openblas64" # v0.3.34-237-g1ad90fb1 -version = "0.3.34.237.1" +version = "0.3.34.237.2" requires-python = ">=3.7" description = "Provides OpenBLAS for python packaging" readme = "README.md" diff --git a/tools/build_steps_win_arm64.bat b/tools/build_steps_win_arm64.bat index 9edc1260..d585a71c 100755 --- a/tools/build_steps_win_arm64.bat +++ b/tools/build_steps_win_arm64.bat @@ -71,8 +71,12 @@ if errorlevel 1 ( ) REM Patch -for /r %%f in (..\patches\*) do git apply %%f -if errorlevel 1 exit /b 1 +set patch_rc=0 +for %%f in (..\patches\*.patch ..\patches-windows\*.patch) do ( + git apply --ignore-whitespace %%f + if errorlevel 1 set patch_rc=1 +) +if not "%patch_rc%"=="0" exit /b 1 REM Patch VERSION REM version=$(grep "^version =" ../pyproject.toml | sed 's/version = "//;s/"//') diff --git a/tools/build_steps_windows.sh b/tools/build_steps_windows.sh index bfa1b8e8..7fbb9ae8 100644 --- a/tools/build_steps_windows.sh +++ b/tools/build_steps_windows.sh @@ -90,9 +90,13 @@ interface_flags="$interface_flags SYMBOLPREFIX=scipy_ LIBNAMEPREFIX=scipy_ FIXED GCC_TAG="gcc_$(gcc -dumpversion | tr .- _)" OPENBLAS_VERSION=$(git describe --tags --abbrev=8) -# Patch OpenBLAS build to resolve all symbols and avoid linking -# with libquadmath -patch -p1 < ../patches-windows/openblas-make-libs.patch +# --ignore-whitespace because the files patched here do not agree on line +# endings (blas_server_win32.c is CRLF, common_thread.h is not) and neither +# does checkout across platforms. +for p in ../patches-windows/*.patch; do + [ -e "$p" ] || continue + git apply --ignore-whitespace "$p" +done # Patch VERSION version=$(grep "^version =" ../pyproject.toml | sed 's/version = "//;s/"//') diff --git a/tools/upload_to_anaconda_staging.sh b/tools/upload_to_anaconda_staging.sh index 31435a69..bf6da2ed 100644 --- a/tools/upload_to_anaconda_staging.sh +++ b/tools/upload_to_anaconda_staging.sh @@ -9,9 +9,13 @@ upload_wheels() { cd $our_wd fi - pushd OpenBLAS - VERSION=$(git describe --tags --abbrev=8) - popd + # The upload job only checks out openblas-libs, not the submodule; + # openblas_commit.txt holds the same string git describe would print. + if [ -d OpenBLAS/.git ]; then + VERSION=$(git -C OpenBLAS describe --tags --abbrev=8) + else + VERSION=$(cat openblas_commit.txt) + fi if [ "$ANACONDA_SCIENTIFIC_PYTHON_UPLOAD" == "" ]; then echo "ANACONDA_SCIENTIFIC_PYTHON_UPLOAD is not defined: skipping."