From 162783a414969464ce3aa5adf5c2554afa5ee93e Mon Sep 17 00:00:00 2001 From: vathomass <55991321+vathomass@users.noreply.github.com> Date: Sun, 21 Jan 2024 11:58:38 +0200 Subject: [PATCH] Python module mutli-platform setup (#519) * Change to pyproject.toml file * Switch to cmake for building the extension * Update readme * Update CHANGELOG * Add hint fot CLBlast discovery * Update README about detecting the library * Switch to scikit-build-core for using CMake --- CHANGELOG | 2 ++ src/pyclblast/CMakeLists.txt | 67 ++++++++++++++++++++++++++++++++++++ src/pyclblast/MANIFEST.in | 3 +- src/pyclblast/README.md | 36 ++++++++++++------- src/pyclblast/pyproject.toml | 32 +++++++++++++++++ src/pyclblast/setup.py | 61 -------------------------------- 6 files changed, 126 insertions(+), 75 deletions(-) create mode 100644 src/pyclblast/CMakeLists.txt create mode 100644 src/pyclblast/pyproject.toml delete mode 100644 src/pyclblast/setup.py diff --git a/CHANGELOG b/CHANGELOG index cc919b77..d4155a4b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,8 @@ Development version (next version) * Convert float scalar values to cl_half for fp16 routines * Amax/amin, max/min routines accept unsigned integer buffers for index - Generator script now always use LF endings, independent of the platform +- Switch to pyproject.toml file for installing python bindings +- Build python binding using Cmake, adding Windows support Version 1.6.1 - Fix pointer error in pyclblast on Arm diff --git a/src/pyclblast/CMakeLists.txt b/src/pyclblast/CMakeLists.txt new file mode 100644 index 00000000..0fa67297 --- /dev/null +++ b/src/pyclblast/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 3.20) +project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX) + +# Find python and numpy +find_package( + Python3 + REQUIRED + COMPONENTS Interpreter Development.Module NumPy +) + +# Run the cython compiler +cmake_path(APPEND CMAKE_CURRENT_SOURCE_DIR "./src" + OUTPUT_VARIABLE Cython_SOURCE_DIR) +find_program(CYTHON "cython") +add_custom_command( + OUTPUT "${Cython_SOURCE_DIR}/pyclblast.cpp" + DEPENDS "${Cython_SOURCE_DIR}/pyclblast.pyx" + VERBATIM + COMMAND "${CYTHON}" -3 "${Cython_SOURCE_DIR}/pyclblast.pyx" + --output-file "${Cython_SOURCE_DIR}/pyclblast.cpp") + + +# Add module target +Python3_add_library(pyclblast MODULE WITH_SOABI + "${Cython_SOURCE_DIR}/pyclblast.cpp") + +# Numpy libraries - NOTE: clean NPY_LIBRARIES cache (may fail for venv) +cmake_path(GET Python3_NumPy_INCLUDE_DIRS PARENT_PATH Python3_NumPy_CORE_DIR) +unset(NPY_LIBRARIES CACHE) +find_library(NPY_LIBRARIES + NAMES npymath + PATHS ${Python3_NumPy_CORE_DIR} + PATH_SUFFIXES lib + DOC "Numpy math library" + REQUIRED + NO_DEFAULT_PATH) +target_link_libraries(pyclblast PRIVATE ${NPY_LIBRARIES}) +target_include_directories(pyclblast PRIVATE ${Python3_NumPy_INCLUDE_DIRS}) + +# CLBlast library +set(CLBLAST_HINTS + ${CLBLAST_ROOT} + $ENV{CLBLAST_ROOT} +) +find_package(CLBlast CONFIG REQUIRED HINTS ${CLBLAST_HINTS}) +target_link_libraries(pyclblast PRIVATE clblast) + +install(TARGETS pyclblast DESTINATION .) + +# In windows pyclblast cannot find the dll, even on path. +# Probably related to change in 3.8, that loads dll only for trusted location +# see https://stackoverflow.com/questions/41365446/how-to-resolve-importerror-dll-load-failed-on-python +# One workaround is to copy the dll to the same dir as the module. +# TODO: add python version check +if (WIN32) + cmake_path(APPEND CLBlast_DIR "../../../bin" OUTPUT_VARIABLE CLBlast_BINDIR) + cmake_path(SET CLBlast_BINDIR NORMALIZE "${CLBlast_BINDIR}") + unset(CLBlast_SHARED_LIBPATH CACHE) + find_file(CLBlast_SHARED_LIBPATH + NAMES clblast.dll + PATHS ${CLBlast_BINDIR} + DOC "CLBlast shared library" + REQUIRED) + + # copy dll to build + install(FILES ${CLBlast_SHARED_LIBPATH} DESTINATION .) +endif() diff --git a/src/pyclblast/MANIFEST.in b/src/pyclblast/MANIFEST.in index fb20923f..bd198ad2 100644 --- a/src/pyclblast/MANIFEST.in +++ b/src/pyclblast/MANIFEST.in @@ -1,2 +1,3 @@ -include README.md setup.py src/*.pyx +include README.md src/*.pyx include samples/*.py +include CMakeLists.txt diff --git a/src/pyclblast/README.md b/src/pyclblast/README.md index fe4cd7e3..c150d30f 100644 --- a/src/pyclblast/README.md +++ b/src/pyclblast/README.md @@ -15,29 +15,39 @@ Non-Python requirements: * OpenCL * [CLBlast](https://github.com/CNugteren/CLBlast) -Python requirements: - -* Cython -* [PyOpenCL](https://github.com/pyopencl/pyopencl/) - Getting started ------------- -After installation OpenCL and CLBlast, simply use pip to install PyCLBlast, e.g.: +After installing OpenCL and CLBlast, simply use pip to install PyCLBlast, e.g.: pip install --user pyclblast -To start using the library, browse the [CLBlast](https://github.com/CNugteren/CLBlast) documentation or check out the PyCLBlast samples provides in the `samples` subfolder. +To start using the library, browse the [CLBlast](https://github.com/CNugteren/CLBlast) documentation or check out the PyCLBlast samples provided in the `samples` subfolder. -For developers, first install CLBlast, followed by the Python requirements (e.g. in a Python3 virtualenv): +For developers, install CLBlast and [cython](https://cython.org/) (e.g. in a Python3 virtualenv): - pip install Cython numpy pybind11 - pip install pyopencl + pip install Cython -And then compile the library from this location using the `setup.py` file: +And then compile the bindings from this location using pip: - python setup.py install + pip install . + + +Detecting CLBlast +------------- + +The CLBlast library should be present and detectable to your system, to successfully install the PyCLBlast bindings. In some systems, this is done automatically. But if the CLBlast library cannot be detected, the PyCLBlast installation will fail. To ensure detection, one can apply either of the following: + +* Add the CLBLast root directory to the environment path. +* Create the environment variable `CLBLAST_ROOT` that holds the path to the CLBLast root directory. +* Define the `cmake` variables `CMAKE_PREFIX_PATH` or the `CLBLAST_ROOT` variable that point to the CLBlast root directory, as: + + pip install . -C skbuild.cmake.args="-DCMAKE_PREFIX_PATH=/root/path/to/clblast" + +* Create the environment variable `CLBlast_DIR` that holds the path to the directory where either of the `CLBlastConfig.cmake` or `clblast-config.cmake` files reside. + +Note that the aforementioned environment variables should be set only during the installation of PyCLBlast and can be unset during normal use. Testing PyCLBlast @@ -53,6 +63,6 @@ How to release a new version on PyPi Following [the guide](https://packaging.python.org/tutorials/packaging-projects/), in essence doing (after changing the version number in `setup.py`): - python3 setup.py sdist bdist_wheel + python3 -m build python3 -m twine upload --repository pypi dist/pyclblast-1.4.0.tar.gz # use '__token__' as username and supply the token from your PyPi account diff --git a/src/pyclblast/pyproject.toml b/src/pyclblast/pyproject.toml new file mode 100644 index 00000000..02b1aac5 --- /dev/null +++ b/src/pyclblast/pyproject.toml @@ -0,0 +1,32 @@ +[build-system] +requires = ["scikit-build-core", "cython", "numpy"] +build-backend = "scikit_build_core.build" + +[project] +name = "pyclblast" +version = "1.4.0" +description = "Python bindings for CLBlast, the tuned OpenCL BLAS library" +authors = [ + {name = "Cedric Nugteren", email = "web@cedricnugteren.nl"} +] +license = {text = "Apache Software License"} +readme = "README.md" +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", +] +keywords = ["OpenCL", "BLAS", "CLBlast", "GEMM", "matrix-multiplication"] +dependencies = [ + "numpy", + "pyopencl" +] + +[project.urls] +Homepage = "https://github.com/CNugteren/CLBlast/blob/master/src/pyclblast" + +[tool.setuptools.packages.find] +where = ["src"] diff --git a/src/pyclblast/setup.py b/src/pyclblast/setup.py deleted file mode 100644 index c6811935..00000000 --- a/src/pyclblast/setup.py +++ /dev/null @@ -1,61 +0,0 @@ - -# This file is part of the CLBlast project. The project is licensed under Apache Version 2.0. -# This file follows the PEP8 Python style guide and uses a max-width of 100 characters per line. -# -# Author(s): -# Cedric Nugteren - -from setuptools import setup - -from distutils.extension import Extension -from Cython.Distutils import build_ext -import platform -import numpy -import os - -np_incdir = numpy.get_include() -np_libdir = os.path.join(np_incdir, '..', 'lib', '') - -runtime_library_dirs = list() -if platform.system() == "Linux": - runtime_library_dirs.append("/usr/local/lib") -elif platform.system() == "Windows": - runtime_library_dirs.append("C:/Program Files/clblast/lib") - runtime_library_dirs.append("C:/Program Files (x86)/clblast/lib") - -ext_modules = list() -ext_modules.append( - Extension( - "pyclblast", - ["src/pyclblast.pyx"], - libraries=["clblast", "npymath"], - runtime_library_dirs=runtime_library_dirs, - library_dirs=[np_libdir], - include_dirs=[np_incdir], - language="c++" - ) -) - -setup( - name="pyclblast", - version="1.4.0", - author="Cedric Nugteren", - author_email="web@cedricnugteren.nl", - url="https://github.com/CNugteren/CLBlast/blob/master/src/pyclblast", - description="Python bindings for CLBlast, the tuned OpenCL BLAS library", - license="Apache Software License", - requires=["numpy", "pyopencl", "cython"], - package_dir={'': 'src'}, - scripts=[], - ext_modules=ext_modules, - cmdclass={"build_ext": build_ext}, - classifiers=[ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'Topic :: Software Development :: Libraries', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 3', - ], - keywords="OpenCL BLAS CLBlast GEMM matrix-multiplication" -)