diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index e6a64bf..8fc995b 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -11,7 +11,7 @@ on: env: REPO_DIR: OpenBLAS - OPENBLAS_COMMIT: "v0.3.29" + OPENBLAS_COMMIT: "v0.3.29-265-gb5456c1b" MACOSX_DEPLOYMENT_TARGET: 10.9 jobs: diff --git a/.github/workflows/windows-arm.yml b/.github/workflows/windows-arm.yml new file mode 100644 index 0000000..39de70d --- /dev/null +++ b/.github/workflows/windows-arm.yml @@ -0,0 +1,98 @@ +name: Windows-on-ARM + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + OPENBLAS_COMMIT: "v0.3.29-265-gb5456c1b" + OPENBLAS_ROOT: "c:\\opt" + # Preserve working directory for calls into bash + # Without this, invoking bash will cd to the home directory + CHERE_INVOKING: "yes" + BASH_PATH: "C:\\Program Files\\Git\\bin\\bash.exe" + PLAT: arm64 + INTERFACE64: 0 + BUILD_BITS: 32 + +jobs: + build: + runs-on: windows-11-arm + timeout-minutes: 90 + + steps: + + - uses: actions/checkout@v4.1.1 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.12 + architecture: arm64 + + - name: Setup visual studio + uses: microsoft/setup-msbuild@v2 + + - name: Download, install 7zip. + run: | + Invoke-WebRequest https://www.7-zip.org/a/7z2409-arm64.exe -UseBasicParsing -OutFile 7z_arm.exe + Start-Process -FilePath ".\7z_arm.exe" -ArgumentList "/S" -Wait + echo "C:\Program Files\7-Zip" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Download and install LLVM installer + run: | + Invoke-WebRequest https://github.com/llvm/llvm-project/releases/download/llvmorg-19.1.5/LLVM-19.1.5-woa64.exe -UseBasicParsing -OutFile LLVM-woa64.exe + Start-Process -FilePath ".\LLVM-woa64.exe" -ArgumentList "/S" -Wait + echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Update CMake for WoA + run: | + pip install cmake + get-command cmake + + - name: Build + run: | + git submodule update --init --recursive + .\tools\build_steps_win_arm64.bat + + - name: Pack + run: | + cd local + cp -r "scipy_openblas${env:BUILD_BITS}" $env:BUILD_BITS + 7z a ../builds/openblas-${env:PLAT}-${env:INTERFACE64}.zip -tzip $env:BUILD_BITS + + - name: Test 32-bit interface wheel + run: | + python -m pip install --no-index --find-links dist scipy_openblas32 + python -m scipy_openblas32 + python -c "import scipy_openblas32; print(scipy_openblas32.get_pkg_config())" + + - uses: actions/upload-artifact@v4.3.0 + with: + name: wheels-${{ env.PLAT }}-${{ env.INTERFACE64 }} + path: dist/scipy_openblas*.whl + + - uses: actions/upload-artifact@v4.3.0 + with: + name: openblas-${{ env.PLAT }}-${{ env.INTERFACE64 }} + path: builds/openblas*.zip + + - 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" + pip install anaconda-client + + - 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: | + source tools/upload_to_anaconda_staging.sh + upload_wheels diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 07bc986..4e28402 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -8,7 +8,7 @@ on: workflow_dispatch: null env: - OPENBLAS_COMMIT: "v0.3.29" + OPENBLAS_COMMIT: "v0.3.29-265-gb5456c1b" OPENBLAS_ROOT: "c:\\opt" # Preserve working directory for calls into bash # Without this, invoking bash will cd to the home directory diff --git a/OpenBLAS b/OpenBLAS index 0929865..b5456c1 160000 --- a/OpenBLAS +++ b/OpenBLAS @@ -1 +1 @@ -Subproject commit 092986582f8fb27dde00918d1f618776fe58c11a +Subproject commit b5456c1b41ea88d4e0041778aa8ec09ee2a111a0 diff --git a/pyproject.toml b/pyproject.toml index 3efd37f..7b2fa54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" [project] name = "scipy-openblas64" # v0.3.29 -version = "0.3.29.0.0" +version = "0.3.29.265.0" 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 old mode 100644 new mode 100755 index 2613865..808e04d --- a/tools/build_steps_win_arm64.bat +++ b/tools/build_steps_win_arm64.bat @@ -1,52 +1,92 @@ :: Build script for scipy_openblas wheel on Windows on ARM64 -:: Usage: build_steps_win_arm64.bat [build_bits] -:: e.g build_steps_win_arm64.bat 64 +:: Usage: build_steps_win_arm64.bat [build_bits] [if_bits] +:: e.g build_steps_win_arm64.bat 64 64 -:: BUILD_BITS (default binary architecture, 32 or 64, unspec -> 64). +:: build_bits (default binary architecture, 32 or 64, unspec -> 64). +:: if_bits (default interface size, 32 or 64, unspec -> 32) +:: If INTERFACE64 environment variable is 1, then if_bits defaults to 64 :: Expects these binaries on the PATH: -:: clang-cl, flang-new, cmake, perl +:: clang-cl, flang-new, cmake, perl +:: Uses environment variable: +:: OPENBLAS_COMMIT (unspec -> current submodule commit, if contains +:: Windows on ARM build fixes (see below), otherwise earliest commit +:: with those fixes). + +:: First commit containing WoA build fixes. +:: Minimum OpenBLAS commit to build; we'll update to this if commit not +:: present. +set first_woa_buildable_commit="de2380e5a6149706a633322a16a0f66faa5591fc" @echo off setlocal enabledelayedexpansion if "%1"=="" ( - set BUILD_BIT=64 + set build_bits=64 +) else ( + set build_bits=%1 +) +if "%INTERFACE64%"=="1" ( + set "if_default=64" +) else ( + set "if_default=32" +) +if "%2"=="" ( + set "if_bits=%if_default%" ) else ( - set BUILD_BIT=%1 + set "if_bits=%2" ) -echo Building for %BUILD_BIT%-bit configuration... +echo Building for %build_bits%-bit binary, %if_bits%-bit interface... :: Define destination directory -move "..\local\scipy_openblas64" "..\local\scipy_openblas32" -set "DEST_DIR=%CD%\..\local\scipy_openblas32" -cd .. - -:: Check if 'openblas' folder exists and is empty -if exist "openblas" ( - dir /b "openblas" | findstr . >nul - if errorlevel 1 ( - echo OpenBLAS folder exists but is empty. Deleting and recloning... - rmdir /s /q "openblas" +pushd "%~dp0\.." +set "ob_out_root=%CD%\local\scipy_openblas" +set "ob_64=%ob_out_root%64" +set "ob_32=%ob_out_root%32" +set "local_dir=%CD%\local" +for /d %%D in ("%local_dir%\*") do ( + if /I not "%%~nxD"=="scipy_openblas64" ( + rmdir /S /Q "%%D" ) ) - -:: Clone OpenBLAS if not present -if not exist "openblas" ( - echo Cloning OpenBLAS repository with submodules... - git clone --recursive https://github.com/OpenMathLib/OpenBLAS.git OpenBLAS - if errorlevel 1 exit /b 1 +if "%if_bits%"=="64" ( + set "DEST_DIR=%ob_64%" +) else ( + if exist "%ob_64%" ( + xcopy /Y /H "%ob_64%\*.py" "%CD%\ob64_backup\" + move "%ob_64%" "%ob_32%" + set "DEST_DIR=%ob_32%" + ) ) + +:: Clone OpenBLAS +echo Cloning OpenBLAS repository with submodules... +git submodule update --init --recursive OpenBLAS +if errorlevel 1 exit /b 1 -:: Enter OpenBLAS directory and checkout develop branch -cd openblas -git checkout develop - -echo Checked out to the latest branch of OpenBLAS. +:: Enter OpenBLAS directory and checkout buildable commit +cd OpenBLAS +if defined OPENBLAS_COMMIT ( + echo Checking out OpenBLAS commit %OPENBLAS_COMMIT% + git checkout %OPENBLAS_COMMIT% +) +git merge-base --is-ancestor %first_woa_buildable_commit% HEAD 2>NUL +if errorlevel 1 ( + echo OpenBLAS commit does not contain WoA build fixes. + echo Commit needs to contain %first_woa_buildable_commit%. + exit /b 2 +) +:: Set suffixed-ILP64 flags +if "%if_bits%"=="64" ( + set "interface_flags=-DINTERFACE64=1 -DSYMBOLSUFFIX=64_" +) else ( + set "interface_flags=" +) + :: Create build directory and navigate to it -if not exist build mkdir build -cd build +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... @@ -55,28 +95,30 @@ for /f "usebackq tokens=*" %%i in (`"C:\Program Files (x86)\Microsoft Visual Stu :: Run CMake and Ninja build cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DTARGET=ARMV8 -DBUILD_SHARED_LIBS=ON -DARCH=arm64 ^ --DBINARY=%BUILD_BIT% -DCMAKE_SYSTEM_PROCESSOR=ARM64 -DCMAKE_C_COMPILER=clang-cl ^ --DCMAKE_Fortran_COMPILER=flang-new -DSYMBOLPREFIX="scipy_" -DLIBNAMEPREFIX="scipy_" +-DBINARY=%build_bits% -DCMAKE_SYSTEM_PROCESSOR=ARM64 -DCMAKE_C_COMPILER=clang-cl ^ +-DCMAKE_Fortran_COMPILER=flang-new -DSYMBOLPREFIX="scipy_" -DLIBNAMEPREFIX="scipy_" %interface_flags% if errorlevel 1 exit /b 1 - + ninja if errorlevel 1 exit /b 1 echo Build complete. Returning to Batch. -:: Rewrite the name of the project to scipy-openblas32 -echo Rewrite to scipy_openblas32 -cd ../.. -powershell -Command "(Get-Content 'pyproject.toml') -replace 'openblas64', 'openblas32' | Set-Content 'pyproject.toml'" -powershell -Command "(Get-Content 'local\scipy_openblas32\__main__.py') -replace 'openblas64', 'openblas32' | Out-File 'local\scipy_openblas32\__main__.py' -Encoding utf8" -powershell -Command "(Get-Content 'local\scipy_openblas32\__init__.py') -replace 'openblas64', 'openblas32' | Out-File 'local\scipy_openblas32\__init__.py' -Encoding utf8" -powershell -Command "(Get-Content 'local\scipy_openblas32\__init__.py') -replace 'openblas_get_config64_', 'openblas_get_config' | Out-File 'local\scipy_openblas32\__init__.py' -Encoding utf8" -powershell -Command "(Get-Content 'local\scipy_openblas32\__init__.py') -replace 'cflags =.*', 'cflags = \"-DBLAS_SYMBOL_PREFIX=scipy_\"' | Out-File 'local\scipy_openblas32\__init__.py' -Encoding utf8" +if "%if_bits%"=="32" ( + echo Rewrite to scipy_openblas32 + cd ../.. + set out_pyproject=pyproject_64_32.toml + powershell -Command "(Get-Content 'pyproject.toml') -replace 'openblas64', 'openblas32' | Set-Content !out_pyproject!" + powershell -Command "(Get-Content 'local\scipy_openblas32\__main__.py') -replace 'openblas64', 'openblas32' | Out-File 'local\scipy_openblas32\__main__.py' -Encoding utf8" + powershell -Command "(Get-Content 'local\scipy_openblas32\__init__.py') -replace 'openblas64', 'openblas32' | Out-File 'local\scipy_openblas32\__init__.py' -Encoding utf8" + powershell -Command "(Get-Content 'local\scipy_openblas32\__init__.py') -replace 'openblas_get_config64_', 'openblas_get_config' | Out-File 'local\scipy_openblas32\__init__.py' -Encoding utf8" + powershell -Command "(Get-Content 'local\scipy_openblas32\__init__.py') -replace 'cflags =.*', 'cflags = \"-DBLAS_SYMBOL_PREFIX=scipy_\"' | Out-File 'local\scipy_openblas32\__init__.py' -Encoding utf8" +) :: Prepare destination directory cd OpenBLAS/build echo Preparing destination directory at %DEST_DIR%... -if not exist "%DEST_DIR%\lib\cmake\openblas" mkdir "%DEST_DIR%\lib\cmake\openblas" +if not exist "%DEST_DIR%\lib\cmake\OpenBLAS" mkdir "%DEST_DIR%\lib\cmake\OpenBLAS" if not exist "%DEST_DIR%\include" mkdir "%DEST_DIR%\include" :: Move library files @@ -84,7 +126,11 @@ echo Moving library files... if exist lib\release ( move /Y lib\release\*.dll "%DEST_DIR%\lib\" if errorlevel 1 exit /b 1 - move /Y lib\release\*.dll.a "%DEST_DIR%\lib\scipy_openblas.lib" + for %%f in (lib\release\*.dll.a) do ( + set "orig_name=%%~nxf" + call set "base_name=%%orig_name:.dll.a=%%" + move /Y "%%f" "%DEST_DIR%\lib\!base_name!.lib" + ) if errorlevel 1 exit /b 1 ) else ( echo Error: lib/release directory not found! @@ -99,7 +145,7 @@ if exist openblasconfigversion.cmake copy /Y openblasconfigversion.cmake "%DEST_ :: Copy header files echo Copying generated header files... if exist generated xcopy /E /Y generated "%DEST_DIR%\include\" -if exist lapacke_mangling copy /Y lapacke_mangling "%DEST_DIR%\include\" +if exist lapacke_mangling.h copy /Y lapacke_mangling.h "%DEST_DIR%\include\" if exist openblas_config.h copy /Y openblas_config.h "%DEST_DIR%\include\" @@ -113,12 +159,32 @@ cd ../.. :: Build the Wheel & Install It echo Running 'python -m build' to build the wheel... -python -m build -if errorlevel 1 exit /b 1 - +python -c "import build" 2>NUL || pip install build +if "%if_bits%"=="64" ( + python -m build + if errorlevel 1 exit /b 1 +) else ( + move /Y pyproject.toml pyproject.toml.bak + move /Y %out_pyproject% pyproject.toml + python -m build + if errorlevel 1 exit /b 1 + move /Y pyproject.toml.bak pyproject.toml +) +if "%if_bits%"=="32" ( + move /Y "%CD%\ob64_backup" "%ob_64%" +) + +:: Rename the wheel +for %%f in (dist\*any.whl) do ( + set WHEEL_FILE=dist\%%f + set "filename=%%~nxf" + set "newname=!filename:any.whl=win_arm64.whl!" + ren "dist\!filename!" "!newname!" +) + :: Locate the built wheel for /f %%f in ('dir /b dist\scipy_openblas*.whl 2^>nul') do set WHEEL_FILE=dist\%%f - + if not defined WHEEL_FILE ( echo Error: No wheel file found in dist folder. exit /b 1 @@ -129,4 +195,4 @@ pip install "%WHEEL_FILE%" if errorlevel 1 exit /b 1 echo Done. -exit /b 0 \ No newline at end of file +exit /b 0