diff --git a/.github/workflows/multibuild.yml b/.github/workflows/posix.yml similarity index 68% rename from .github/workflows/multibuild.yml rename to .github/workflows/posix.yml index 9ddd1cba..a0fc88ac 100644 --- a/.github/workflows/multibuild.yml +++ b/.github/workflows/posix.yml @@ -2,7 +2,7 @@ name: Linux,macOS build on: push: - branches: [ master ] + branches: [ master, openblas-wheel ] pull_request: branches: [ master ] @@ -15,41 +15,12 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest] platform: [x64] - PLAT: [i686, x86_64] - INTERFACE64: ['', '1'] - MB_ML_VER: ['', 2010, 2014] - include: - - os: macos-latest - PLAT: arm64 - INTERFACE64: '1' - platform: [x64] - - os: macos-latest - PLAT: arm64 - INTERFACE64: '' - platform: [x64] - - os: ubuntu-latest - PLAT: x86_64 - INTERFACE64: '1' - MB_ML_LIBC: musllinux - MB_ML_VER: _1_1 - platform: [x64] - - os: ubuntu-latest - PLAT: x86_64 - INTERFACE64: '' - MB_ML_LIBC: musllinux - MB_ML_VER: _1_1 - platform: [x64] - exclude: - - os: macos-latest - PLAT: i686 - - os: macos-latest - MB_ML_VER: 2010 - - os: macos-latest - MB_ML_VER: 2014 - - PLAT: i686 - INTERFACE64: '1' + PLAT: [x86_64] + INTERFACE64: ['1'] + MB_ML_VER: ['2014'] + env: REPO_DIR: OpenBLAS OPENBLAS_COMMIT: "bfd9c1b58cd3" @@ -87,7 +58,7 @@ jobs: run: | python -m pip install --upgrade pip pip install virtualenv - - name: Build and Install Wheels + - name: Build OpenBLAS run: | if [[ "$PLAT" == "arm64" ]]; then sudo xcode-select -switch /Applications/Xcode_12.5.1.app @@ -101,11 +72,44 @@ jobs: echo "------ BUILD LIB --------" build_lib "$PLAT" "$INTERFACE64" + - name: Build wheel + run: | + mkdir -p local/openblas + tar -C local/openblas --strip-components=2 -xf libs/openblas*.tar.gz + cp local/openblas/lib/libopenblas64_.so local/openblas/libopenblas_python.so + # do not package the static libs and symlinks, they are ~55MB + rm -rf local/openblas/lib/* + mv local/openblas/libopenblas_python.so local/openblas/lib/ + + cat < run_in_docker.sh + cd /openblas + patchelf --set-soname libopenblas_python.so local/openblas/lib/libopenblas_python.so + python3.7 -m pip wheel -w /tmp/wheelhouse -vv . + auditwheel repair -w dist/ /tmp/wheelhouse/openblas-*.whl + python3.10 -m pip install dist/openblas-*.whl + python3.10 -m openblas + EOF + + docker run --rm -v $(pwd):/openblas quay.io/pypa/manylinux2014_x86_64 /bin/bash -xe /openblas/run_in_docker.sh + + # test numpy build + git clone https://github.com/mayeut/numpy.git numpy + cd numpy + git checkout openblas-wheel + git submodule update --init --recursive + cp -rf ../dist ./openblas-wheel + python3 -m pip install --user cibuildwheel + cibuildwheel --only cp310-manylinux_x86_64 + - uses: actions/upload-artifact@v3 with: path: libs/openblas*.tar.gz - - name: Upload tarballs + - uses: actions/upload-artifact@v3 + with: + path: dist/openblas*.whl + + - name: Upload run: | set -ex TOKEN=${{ secrets.MULTIBUILD_WHEELS_STAGING_ACCESS }} diff --git a/.github/workflows/build.yml b/.github/workflows/windows.yml similarity index 100% rename from .github/workflows/build.yml rename to .github/workflows/windows.yml diff --git a/.gitignore b/.gitignore index fc6879df..e8931bec 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,17 @@ builds/ *.pyc *.swp test.exe +dist/ +build/ +libs/ +# These are artifacts when using editable builds +local/lib +local/include +local/openblas.egg-info +local/openblas/lib +local/openblas/include +local/openblas/*.so +local/openblas/*.pyd +local/openblas/*.dylib +# just for test +numpy/ diff --git a/OpenBLAS b/OpenBLAS index 0b678b19..7060ca50 160000 --- a/OpenBLAS +++ b/OpenBLAS @@ -1 +1 @@ -Subproject commit 0b678b19dc03f2a999d6e038814c4c50b9640a4e +Subproject commit 7060ca50020e990f2e4cc7c3f340fcf1a887af7d diff --git a/local/openblas/__init__.py b/local/openblas/__init__.py new file mode 100644 index 00000000..30414a1c --- /dev/null +++ b/local/openblas/__init__.py @@ -0,0 +1,36 @@ +from pathlib import Path + +from . import _init_openblas + + +_HERE = Path(__file__).resolve().parent + + +# Use importlib.metadata to single-source the version + +try: + # importlib.metadata is present in Python 3.8 and later + import importlib.metadata as importlib_metadata +except ImportError: + # use the shim package importlib-metadata pre-3.8 + import importlib_metadata as importlib_metadata + +try: + # __package__ allows for the case where __name__ is "__main__" + __version__ = importlib_metadata.version(__package__ or __name__) +except importlib_metadata.PackageNotFoundError: + __version__ = "0.0.0" + +openblas_config = _init_openblas.get_config() + + +def get_include_dir(): + return str(_HERE / "include") + + +def get_lib_dir(): + return str(_HERE / "lib") + + +def get_library(): + return "openblas_python" diff --git a/local/openblas/__main__.py b/local/openblas/__main__.py new file mode 100644 index 00000000..eff65cab --- /dev/null +++ b/local/openblas/__main__.py @@ -0,0 +1,4 @@ +import openblas + +if __name__ == "__main__": + print(f"OpenBLAS using '{openblas.openblas_config}'") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..3da6bb7b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,39 @@ +[build-system] +# Minimum requirements for the build system to execute. +requires = [ + "setuptools", + "wheel", +] +build-backend = "setuptools.build_meta" + +[project] +name = "openblas" +version = "0.3.20" +requires-python = ">=3.7" +description = "Provides OpenBLAS for python packaging" +readme = "README.md" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Programming Language :: C++", + "License :: OSI Approved :: BSD License", +] +# authors = [ +# ] +# maintainers = [ +# ] + +[project.urls] +homepage = "https://github.com/MacPython/openblas-libs" +upstream = "https://github.com/xianyi/OpenBLAS" + +[tool.setuptools.packages.find] +# scanning for namespace packages is true by default in pyproject.toml, so +# # you do NOT need to include the following line. +namespaces = true +where = ["local"] + +[options] +install_requires = "importlib-metadata ~= 1.0 ; python_version < '3.8'" + +[tool.setuptools.package-data] +openblas = ["lib/*", "include/*", "lib/pkgconfig/*", "lib/cmake/openblas/*"] diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..f0d83c13 --- /dev/null +++ b/setup.py @@ -0,0 +1,41 @@ +import os +import sys +from setuptools import setup, Extension +from wheel.bdist_wheel import bdist_wheel + + +mydir = os.path.abspath(os.path.dirname(__file__)) + + +class bdist_wheel_abi3(bdist_wheel): + def get_tag(self): + python, abi, plat = bdist_wheel.get_tag(self) + return python, "abi3", plat + + +# TODO: determine if we are building 64- or 32- bit interfaces +use_64=True +if use_64: + macros = [("SUFFIX", "64_")] +else: + macros = [] + +if sys.implementation.name == "cpython": + cmdclass = {"bdist_wheel": bdist_wheel_abi3} + py_limited_api = {"py_limited_api": True} + macros.append(('Py_LIMITED_API', '0x03070000')) +else: + cmdclass = {} + py_limited_api = {} + +setup( + cmdclass=cmdclass, + ext_modules=[Extension( + "openblas._init_openblas", ["src/_init_openblas.c"], + libraries=["openblas_python"], + library_dirs=[os.path.join(mydir, 'local', 'openblas', 'lib'),], + extra_link_args=["-Wl,-rpath,$ORIGIN/lib"], + define_macros=macros, + **py_limited_api + )], +) diff --git a/src/_init_openblas.c b/src/_init_openblas.c new file mode 100644 index 00000000..2145b9c2 --- /dev/null +++ b/src/_init_openblas.c @@ -0,0 +1,40 @@ +#include + +#ifdef SUFFIX + #define openblas_get_config openblas_get_config64_ +#endif + +extern const char * openblas_get_config(); + +PyObject * +get_config(PyObject *self, PyObject *args) { + const char * config = openblas_get_config(); + return PyUnicode_FromString(config); +} + +static PyMethodDef InitMethods[] = { + {"get_config", get_config, METH_NOARGS, + "Return openblas_get_config(), see https://github.com/xianyi/OpenBLAS/wiki/OpenBLAS-Extensions"}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +static struct PyModuleDef initmodule = { + PyModuleDef_HEAD_INIT, + "_init_openblas", /* name of module */ + NULL, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + InitMethods +}; + +PyMODINIT_FUNC +PyInit__init_openblas(void) +{ + PyObject *m; + + m = PyModule_Create(&initmodule); + if (m == NULL) + return NULL; + + return m; +}