From 2e84e219632f807d699aeef0a1aa531d71f07a2d Mon Sep 17 00:00:00 2001 From: Matti Picus Date: Tue, 13 Jun 2023 19:08:01 +0300 Subject: [PATCH] reuse parts of scipy's scripts for windows includding delvewheel; fix non-wheel build --- azure-pipelines.yml | 4 ++-- azure-steps-windows.yml | 35 +++++++++++++++++-------------- pyproject.toml | 5 +++-- tools/openblas_support.py | 9 ++++---- tools/wheels/cibw_before_build.sh | 32 +++++++++++++++++++++------- tools/wheels/repair_windows.sh | 32 ++++++++++++++++++++++++++++ 6 files changed, 86 insertions(+), 31 deletions(-) create mode 100644 tools/wheels/repair_windows.sh diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4d1be81bc984..df0da2123d09 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -261,13 +261,13 @@ stages: PYTHON_ARCH: 'x64' TEST_MODE: full BITS: 64 - NPY_USE_BLAS_ILP64: '1' + # NPY_USE_BLAS_ILP64: '1' PyPy39-64bit-fast: PYTHON_VERSION: 'pypy3.9' PYTHON_ARCH: 'x64' TEST_MODE: fast BITS: 64 - NPY_USE_BLAS_ILP64: '1' + # NPY_USE_BLAS_ILP64: '1' steps: - template: azure-steps-windows.yml diff --git a/azure-steps-windows.yml b/azure-steps-windows.yml index 999c436d105e..11d9e9fd8db6 100644 --- a/azure-steps-windows.yml +++ b/azure-steps-windows.yml @@ -17,35 +17,38 @@ steps: # Note that rtools 42+ does not support 32 bits builds. We dropped testing # those, but if there's a need to go back on that, use version 4.0.0.20220206 choco install --confirm --no-progress --side-by-side rtools --version=4.3.5550 + choco install unzip -y + choco install -y --checksum 6004DF17818F5A6DBF19CB335CC92702 pkgconfiglite echo "##vso[task.setvariable variable=RTOOLS43_HOME]c:\rtools43" - displayName: 'Install rtools' + displayName: 'Install utilities' - powershell: | $ErrorActionPreference = "Stop" - # Download and get the path to "openblas". We cannot copy it - # to $PYTHON_EXE's directory since that is on a different drive which - # mingw does not like. Instead copy it to a directory and set OPENBLAS, - # since OPENBLAS will be picked up by the openblas discovery - $target = $(python tools/openblas_support.py) - mkdir openblas - echo "Copying $target to openblas/" - cp -r $target/* openblas/ - $env:OPENBLAS = $target + mkdir C:/opt/openblas/openblas_dll + mkdir C:/opt/32/lib/pkgconfig + mkdir C:/opt/64/lib/pkgconfig + # TBD: support 32 bit testing + $target=$(python -c "import tools.openblas_support as obs; plat=obs.get_plat(); ilp64=obs.get_ilp64(); target=f'openblas_{plat}.zip'; obs.download_openblas(target, plat, ilp64);print(target)") + unzip -o -d c:/opt/ $target + echo "##vso[task.setvariable variable=PKG_CONFIG_PATH]c:/opt/64/lib/pkgconfig" + copy C:/opt/64/bin/*.dll C:/opt/openblas/openblas_dll displayName: 'Download / Install OpenBLAS' - powershell: | - ls openblas - # Not strictly needed since we are forcing use of vsenv - $env:PATH = "$env:RTOOLS43_HOME\\x86_64-w64-mingw32.static.posix\\bin;$env:PATH" - python -c"import os, pprint; pprint.pprint(os.environ['PATH'])" - gcc --version If ( Test-Path env:NPY_USE_BLAS_ILP64 ) { - $env:OPENBLAS64_ = "openblas" + $env:OPENBLAS64_="openblas" + # This does not work, the flags are passed together and not separately + $env:CFLAGS="-DBLAS_SYMBOL_SUFFIX=64_ -DHAVE_BLAS_ILP64" + $env:CXXFLAGS="-DBLAS_SYMBOL_SUFFIX=64_ -DHAVE_BLAS_ILP64" } else { $env:OPENBLAS = "openblas" } python -c "from tools import openblas_support; openblas_support.make_init('numpy')" python -m pip install . --config-settings=setup-args="--vsenv" + # copy from c:/opt/openblas/openblas_dll to numpy/.libs + $target = $(python -c "import sysconfig; print(sysconfig.get_path('platlib'))") + mkdir $target/numpy/.libs + copy C:/opt/openblas/openblas_dll/*.dll $target/numpy/.libs displayName: 'Build NumPy' - script: | diff --git a/pyproject.toml b/pyproject.toml index 99e34868476a..97190615c7a4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -166,12 +166,13 @@ test-skip = "*_universal2:arm64" environment = { CFLAGS="-std=c99 -fno-strict-aliasing -DBLAS_SYMBOL_SUFFIX=64_ -DHAVE_BLAS_ILP64", CXXFLAGS="-DBLAS_SYMBOL_SUFFIX=64_ -DHAVE_BLAS_ILP64", OPENBLAS64_="/usr/local", NPY_USE_BLAS_ILP64="1", CC="clang", CXX = "clang++", RUNNER_OS="macOS"} [tool.cibuildwheel.windows] -environment = { OPENBLAS64_="openblas", OPENBLAS="", NPY_USE_BLAS_ILP64="1", CFLAGS="-DBLAS_SYMBOL_SUFFIX=64_ -DHAVE_BLAS_ILP64", CXXFLAGS="-DBLAS_SYMBOL_SUFFIX=64_ -DHAVE_BLAS_ILP64", LDFLAGS="", PKG_CONFIG_PATH=D:/a/numpy/numpy/openblas/lib/pkgconfig} +environment = { OPENBLAS64_="openblas", OPENBLAS="", NPY_USE_BLAS_ILP64="1", CFLAGS="-DBLAS_SYMBOL_SUFFIX=64_ -DHAVE_BLAS_ILP64", CXXFLAGS="-DBLAS_SYMBOL_SUFFIX=64_ -DHAVE_BLAS_ILP64", LDFLAGS="", PKG_CONFIG_PATH="C:/opt/64/lib/pkgconfig"} config-settings = "setup-args=--vsenv" +repair-wheel-command = "bash ./tools/wheels/repair_windows.sh {wheel} {dest_dir}" [[tool.cibuildwheel.overrides]] select = "*-win32" -environment = { OPENBLAS64_="", OPENBLAS="openblas", NPY_USE_BLAS_ILP64="0", CFLAGS="-m32", LDFLAGS="-m32"} +environment = { OPENBLAS64_="", OPENBLAS="openblas", NPY_USE_BLAS_ILP64="0", CFLAGS="-m32", LDFLAGS="-m32", PKG_CONFIG_PATH="/opt/32/lib/pkgconfig"} [tool.meson-python.args] # These should be the defaults for a normal NumPy build diff --git a/tools/openblas_support.py b/tools/openblas_support.py index fd8c6a97a34e..c793805585da 100644 --- a/tools/openblas_support.py +++ b/tools/openblas_support.py @@ -182,10 +182,11 @@ def unpack_windows_zip(fname, plat): # Copy the lib to openblas.lib. Once we can properly use pkg-config # this will not be needed lib = glob.glob(os.path.join(target, 'lib', '*.lib')) - assert len(lib) == 1 - for f in lib: - shutil.copy(f, os.path.join(target, 'lib', 'openblas.lib')) - shutil.copy(f, os.path.join(target, 'lib', 'openblas64_.lib')) + if len(lib) == 1: + # The 64-bit tarball already has these copied, no need to do it + for f in lib: + shutil.copy(f, os.path.join(target, 'lib', 'openblas.lib')) + shutil.copy(f, os.path.join(target, 'lib', 'openblas64_.lib')) # Copy the dll from bin to lib so system_info can pick it up dll = glob.glob(os.path.join(target, 'bin', '*.dll')) for f in dll: diff --git a/tools/wheels/cibw_before_build.sh b/tools/wheels/cibw_before_build.sh index 0d76ec76538e..372b25af09b8 100644 --- a/tools/wheels/cibw_before_build.sh +++ b/tools/wheels/cibw_before_build.sh @@ -29,14 +29,32 @@ if [[ $RUNNER_OS == "Linux" || $RUNNER_OS == "macOS" ]] ; then cp $basedir/include/* /usr/local/include fi elif [[ $RUNNER_OS == "Windows" ]]; then + # delvewheel is the equivalent of delocate/auditwheel for windows. + python -m pip install delvewheel + + # make the DLL available for tools/wheels/repair_windows.sh. If you change + # this location you need to alter that script. + mkdir -p /c/opt/openblas/openblas_dll + PYTHONPATH=tools python -c "import openblas_support; openblas_support.make_init('numpy')" - target=$(python tools/openblas_support.py) - mkdir -p openblas - # bash on windows does not like cp -r $target/* openblas - for f in $(ls $target); do - cp -r $target/$f openblas - done - ls openblas + mkdir -p /c/opt/32/lib/pkgconfig + mkdir -p /c/opt/64/lib/pkgconfig + target=$(python -c "import tools.openblas_support as obs; plat=obs.get_plat(); ilp64=obs.get_ilp64(); target=f'openblas_{plat}.zip'; obs.download_openblas(target, plat, ilp64);print(target)") + if [[ $PLATFORM == 'win-32' ]]; then + # 32-bit openBLAS + # Download 32 bit openBLAS and put it into c/opt/32/lib + unzip -o -d /c/opt/ $target + cp /c/opt/32/bin/*.dll /c/opt/openblas/openblas_dll + else + # 64-bit openBLAS + unzip -o -d /c/opt/ $target + if [[ -f /c/opt/64/lib/pkgconfig/openblas64.pc ]]; then + # As of v0.3.23, the 64-bit interface has a openblas64.pc file, + # but this is wrong. It should be openblas.pc + cp /c/opt/64/lib/pkgconfig/openblas{64,}.pc + fi + cp /c/opt/64/bin/*.dll /c/opt/openblas/openblas_dll + fi fi if [[ $RUNNER_OS == "macOS" ]]; then diff --git a/tools/wheels/repair_windows.sh b/tools/wheels/repair_windows.sh new file mode 100644 index 000000000000..a7aa209d21d9 --- /dev/null +++ b/tools/wheels/repair_windows.sh @@ -0,0 +1,32 @@ +set -xe + +WHEEL="$1" +DEST_DIR="$2" + +# create a temporary directory in the destination folder and unpack the wheel +# into there +pushd $DEST_DIR +mkdir -p tmp +pushd tmp +wheel unpack $WHEEL +pushd numpy* + +# To avoid DLL hell, the file name of libopenblas that's being vendored with +# the wheel has to be name-mangled. delvewheel is unable to name-mangle PYD +# containing extra data at the end of the binary, which frequently occurs when +# building with mingw. +# We therefore find each PYD in the directory structure and strip them. + +for f in $(find ./scipy* -name '*.pyd'); do strip $f; done + + +# now repack the wheel and overwrite the original +wheel pack . +mv -fv *.whl $WHEEL + +cd $DEST_DIR +rm -rf tmp + +# the libopenblas.dll is placed into this directory in the cibw_before_build +# script. +delvewheel repair --add-path /c/opt/openblas/openblas_dll -w $DEST_DIR $WHEEL