|
| 1 | +diff --git a/CMakeLists.txt b/CMakeLists.txt |
| 2 | +index e83c95b..cc990c4 100644 |
| 3 | +--- a/CMakeLists.txt |
| 4 | ++++ b/CMakeLists.txt |
| 5 | +@@ -23,6 +23,7 @@ set(CMAKE_CXX_FLAGS_DEBUG ${SYMENGINE_CXX_FLAGS_DEBUG}) |
| 6 | + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SYMENGINE_CXX_FLAGS}") |
| 7 | + include_directories(${SYMENGINE_INCLUDE_DIRS}) |
| 8 | + |
| 9 | ++set(WITH_PY_LIMITED_API OFF CACHE STRING "Use CPython's limited API") |
| 10 | + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") |
| 11 | + find_package(Python REQUIRED) |
| 12 | + find_package(Cython REQUIRED) |
| 13 | +diff --git a/cmake/FindCython.cmake b/cmake/FindCython.cmake |
| 14 | +index beac6c5..e35d44b 100644 |
| 15 | +--- a/cmake/FindCython.cmake |
| 16 | ++++ b/cmake/FindCython.cmake |
| 17 | +@@ -24,6 +24,19 @@ IF (CYTHON_BIN) |
| 18 | + else (CYTHON_RESULT EQUAL 0) |
| 19 | + SET(Cython_Compilation_Failed TRUE) |
| 20 | + endif (CYTHON_RESULT EQUAL 0) |
| 21 | ++ execute_process( |
| 22 | ++ COMMAND ${CYTHON_BIN} --version |
| 23 | ++ RESULT_VARIABLE CYTHON_VERSION_RESULT |
| 24 | ++ OUTPUT_VARIABLE CYTHON_VERSION_OUTPUT |
| 25 | ++ ERROR_VARIABLE CYTHON_VERSION_ERROR |
| 26 | ++ ) |
| 27 | ++ if (CYTHON_VERSION_RESULT EQUAL 0) |
| 28 | ++ string(STRIP ${CYTHON_VERSION_OUTPUT} CYTHON_VERSION_OUTPUT) |
| 29 | ++ if ("${CYTHON_VERSION_OUTPUT}" MATCHES "Cython version") |
| 30 | ++ string(SUBSTRING "${CYTHON_VERSION_OUTPUT}" 15 -1 CYTHON_VERSION) |
| 31 | ++ endif () |
| 32 | ++ endif () |
| 33 | ++ message(STATUS "Cython version: ${CYTHON_VERSION}") |
| 34 | + ENDIF (CYTHON_BIN) |
| 35 | + |
| 36 | + |
| 37 | +@@ -31,6 +44,11 @@ IF (Cython_FOUND) |
| 38 | + IF (NOT Cython_FIND_QUIETLY) |
| 39 | + MESSAGE(STATUS "Found CYTHON: ${CYTHON_BIN}") |
| 40 | + ENDIF (NOT Cython_FIND_QUIETLY) |
| 41 | ++ IF (WITH_PY_LIMITED_API AND "${CYTHON_VERSION}" VERSION_LESS "3.1") |
| 42 | ++ MESSAGE(FATAL_ERROR |
| 43 | ++ "Your Cython version (${CYTHON_VERSION}) is too old. Please upgrade Cython to 3.1 or newer." |
| 44 | ++ ) |
| 45 | ++ ENDIF () |
| 46 | + ELSE (Cython_FOUND) |
| 47 | + IF (Cython_FIND_REQUIRED) |
| 48 | + if(Cython_Compilation_Failed) |
| 49 | +diff --git a/cmake/FindPython.cmake b/cmake/FindPython.cmake |
| 50 | +index c1f6c43..7ed8287 100644 |
| 51 | +--- a/cmake/FindPython.cmake |
| 52 | ++++ b/cmake/FindPython.cmake |
| 53 | +@@ -48,15 +48,17 @@ if ("${PY_GIL_DISABLED}" STREQUAL "True") |
| 54 | + endif() |
| 55 | + |
| 56 | + if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") |
| 57 | +- FIND_LIBRARY(PYTHON_LIBRARY NAMES |
| 58 | +- python${PYTHON_VERSION}${PY_THREAD} |
| 59 | +- python${PYTHON_VERSION}m |
| 60 | +- python${PYTHON_VERSION_WITHOUT_DOTS}${PY_THREAD} |
| 61 | +- PATHS ${PYTHON_LIB_PATH} ${PYTHON_PREFIX_PATH}/lib ${PYTHON_PREFIX_PATH}/libs |
| 62 | +- PATH_SUFFIXES ${CMAKE_LIBRARY_ARCHITECTURE} |
| 63 | +- NO_DEFAULT_PATH |
| 64 | +- NO_SYSTEM_ENVIRONMENT_PATH |
| 65 | +- ) |
| 66 | ++ if (WITH_PY_LIMITED_API) |
| 67 | ++ set(PYTHON_LIBRARY_NAMES python3) |
| 68 | ++ else() |
| 69 | ++ set(PYTHON_LIBRARY_NAMES python${PYTHON_VERSION}${PY_THREAD} python${PYTHON_VERSION}m python${PYTHON_VERSION_WITHOUT_DOTS}${PY_THREAD}) |
| 70 | ++ endif() |
| 71 | ++ FIND_LIBRARY(PYTHON_LIBRARY NAMES ${PYTHON_LIBRARY_NAMES} |
| 72 | ++ PATHS ${PYTHON_LIB_PATH} ${PYTHON_PREFIX_PATH}/lib ${PYTHON_PREFIX_PATH}/libs |
| 73 | ++ PATH_SUFFIXES ${CMAKE_LIBRARY_ARCHITECTURE} |
| 74 | ++ NO_DEFAULT_PATH |
| 75 | ++ NO_SYSTEM_ENVIRONMENT_PATH |
| 76 | ++ ) |
| 77 | + endif() |
| 78 | + |
| 79 | + execute_process( |
| 80 | +@@ -74,6 +76,14 @@ execute_process( |
| 81 | + ) |
| 82 | + string(STRIP ${PYTHON_EXTENSION_SOABI_tmp} PYTHON_EXTENSION_SOABI_tmp) |
| 83 | + |
| 84 | ++if (WITH_PY_LIMITED_API) |
| 85 | ++ if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") |
| 86 | ++ set(PYTHON_EXTENSION_SOABI_tmp "") |
| 87 | ++ else() |
| 88 | ++ set(PYTHON_EXTENSION_SOABI_tmp ".abi3") |
| 89 | ++ endif() |
| 90 | ++endif() |
| 91 | ++ |
| 92 | + set(PYTHON_EXTENSION_SOABI ${PYTHON_EXTENSION_SOABI_tmp} |
| 93 | + CACHE STRING "Suffix for python extensions") |
| 94 | + |
| 95 | +@@ -143,5 +153,12 @@ macro(ADD_PYTHON_LIBRARY name) |
| 96 | + target_compile_definitions(${name} PRIVATE Py_GIL_DISABLED=1) |
| 97 | + ENDIF() |
| 98 | + ENDIF() |
| 99 | +- |
| 100 | ++ IF(WITH_PY_LIMITED_API) |
| 101 | ++ target_compile_definitions( |
| 102 | ++ ${name} |
| 103 | ++ PRIVATE |
| 104 | ++ Py_LIMITED_API=${WITH_PY_LIMITED_API} |
| 105 | ++ CYTHON_LIMITED_API=1 |
| 106 | ++ ) |
| 107 | ++ ENDIF() |
| 108 | + endmacro(ADD_PYTHON_LIBRARY) |
| 109 | +diff --git a/setup.py b/setup.py |
| 110 | +index 23d948e..b0eb020 100644 |
| 111 | +--- a/setup.py |
| 112 | ++++ b/setup.py |
| 113 | +@@ -10,6 +10,18 @@ if sys.version_info[:2] < (3, 9): |
| 114 | + "Python %d.%d detected" % sys.version_info[:2]) |
| 115 | + sys.exit(-1) |
| 116 | + |
| 117 | ++def _get_limited_api(): |
| 118 | ++ value = os.environ.get("SYMENGINE_PY_LIMITED_API") |
| 119 | ++ if not value: |
| 120 | ++ return None |
| 121 | ++ else: |
| 122 | ++ version = tuple(map(int, value.split("."))) |
| 123 | ++ if version < (3, 11): |
| 124 | ++ raise ValueError(f"symengine needs at least python 3.11 limited API support. Got {value}") |
| 125 | ++ return version |
| 126 | ++ |
| 127 | ++limited_api = _get_limited_api() |
| 128 | ++ |
| 129 | + # use setuptools by default as per the official advice at: |
| 130 | + # packaging.python.org/en/latest/current.html#packaging-tool-recommendations |
| 131 | + use_setuptools = True |
| 132 | +@@ -65,6 +77,7 @@ global_user_options = [ |
| 133 | + ('build-type=', None, 'build type: Release or Debug'), |
| 134 | + ('define=', 'D', |
| 135 | + 'options to cmake <var>:<type>=<value>'), |
| 136 | ++ ('py-limited-api=', None, 'Use Py_LIMITED_API with given version.'), |
| 137 | + ] |
| 138 | + |
| 139 | + def _process_define(arg): |
| 140 | +@@ -122,6 +135,11 @@ class BuildExtWithCmake(_build_ext): |
| 141 | + cmake_cmd.extend(process_opts(cmake_opts)) |
| 142 | + if not path.exists(path.join(build_dir, "CMakeCache.txt")): |
| 143 | + cmake_cmd.extend(self.get_generator()) |
| 144 | ++ |
| 145 | ++ if limited_api: |
| 146 | ++ h = limited_api[0] * 16**6 + limited_api[1] * 16**4 |
| 147 | ++ cmake_cmd.append(f"-DWITH_PY_LIMITED_API={h}") |
| 148 | ++ |
| 149 | + if subprocess.call(cmake_cmd, cwd=build_dir) != 0: |
| 150 | + raise OSError("error calling cmake") |
| 151 | + |
| 152 | +@@ -202,11 +220,17 @@ cmdclass={ |
| 153 | + } |
| 154 | + |
| 155 | + try: |
| 156 | +- from wheel.bdist_wheel import bdist_wheel |
| 157 | ++ try: |
| 158 | ++ from setuptools.command.bdist_wheel import bdist_wheel |
| 159 | ++ except ImportError: |
| 160 | ++ from wheel.bdist_wheel import bdist_wheel |
| 161 | ++ |
| 162 | + class BdistWheelWithCmake(bdist_wheel): |
| 163 | + def finalize_options(self): |
| 164 | + bdist_wheel.finalize_options(self) |
| 165 | + self.root_is_pure = False |
| 166 | ++ if limited_api: |
| 167 | ++ self.py_limited_api = "cp" + "".join(str(c) for c in limited_api) |
| 168 | + cmdclass["bdist_wheel"] = BdistWheelWithCmake |
| 169 | + except ImportError: |
| 170 | + pass |
0 commit comments