diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml new file mode 100644 index 0000000..917edcc --- /dev/null +++ b/.github/workflows/build-wheels.yml @@ -0,0 +1,71 @@ +name: Build wheels + +on: + push: + branches: [ master, main ] + tags: [ v* ] + pull_request: + branches: [ master, main ] + +jobs: + build_wheels: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest, macos-14] # macos-14 is Apple Silicon + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.8' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pybind11>=2.11.0 setuptools>=40.6.0 + + - name: Build wheels + uses: pypa/cibuildwheel@v2.16.2 + env: + # Skip 32-bit builds and older Python versions + CIBW_SKIP: "*-win32 *-manylinux_i686 pp* cp36-*" + # Apple Silicon specific settings + CIBW_ARCHS_MACOS: "x86_64 arm64" + # Ensure we have CMake 3.18+ and pybind11 2.11+ + CIBW_BEFORE_ALL_MACOS: "brew install cmake && pip install 'pybind11>=2.11.0'" + CIBW_BEFORE_ALL_LINUX: | + if command -v yum > /dev/null; then + yum install -y cmake3; + elif command -v apt > /dev/null; then + apt update && apt install -y cmake; + elif command -v zypper > /dev/null; then + zypper install -y cmake; + else + echo "Unsupported package manager. Please install cmake manually." && exit 1; + fi + pip install 'pybind11>=2.11.0' + CIBW_BEFORE_ALL_WINDOWS: "pip install 'pybind11>=2.11.0'" + # Test that the wheels work + CIBW_TEST_COMMAND: "python -c \"import SymSpellCppPy; symspell = SymSpellCppPy.SymSpell(); print('✅ Import and basic functionality test passed')\"" + + - uses: actions/upload-artifact@v3 + with: + path: ./wheelhouse/*.whl + + upload_pypi: + needs: [build_wheels] + runs-on: ubuntu-latest + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + steps: + - uses: actions/download-artifact@v3 + with: + name: artifact + path: dist + + - uses: pypa/gh-action-pypi-publish@v1.8.10 + with: + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f86869..c8e3f86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.18) project(SymSpellCppPy) set(CMAKE_CXX_STANDARD 14) @@ -6,6 +6,12 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS ON) set(CMAKE_BUILD_TYPE "Release") +# Apple Silicon specific optimizations +if(APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64") + set(CMAKE_OSX_ARCHITECTURES "arm64") + message(STATUS "Detected Apple Silicon, enabling arm64 optimizations") +endif() + option(BUILD_FOR_PYTHON "Build for Python" OFF) option(BUILD_FOR_TEST "Build Tests" ON) @@ -16,17 +22,29 @@ if (BUILD_FOR_PYTHON) set(CMAKE_BUILD_TYPE "Release") Include(FetchContent) + # Use newer pybind11 version with better CMake support FetchContent_Declare( pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git - GIT_TAG v2.10.4) + GIT_TAG v2.11.1) FetchContent_MakeAvailable(pybind11) pybind11_add_module(SymSpellCppPy MODULE SymSpellCppPy.cpp) target_sources(SymSpellCppPy PRIVATE library.cpp library.h) target_link_libraries(SymSpellCppPy PRIVATE SymSpellCpp) - target_compile_options(SymSpellCppPy PRIVATE - $<$:-O3 -DNDEBUG -march=native -mtune=native -fvisibility=hidden - -flto -ffat-lto-objects>) + + # Platform-specific compile options + if(APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64") + # Apple Silicon optimizations + target_compile_options(SymSpellCppPy PRIVATE + $<$:-O3 -DNDEBUG -march=armv8.4-a -fvisibility=hidden + -flto -ffat-lto-objects>) + else() + # Standard x86_64 optimizations + target_compile_options(SymSpellCppPy PRIVATE + $<$:-O3 -DNDEBUG -march=native -mtune=native -fvisibility=hidden + -flto -ffat-lto-objects>) + endif() + message(STATUS "Build for Python = " ${BUILD_FOR_PYTHON}) else () Include(FetchContent) diff --git a/requirements.txt b/requirements.txt index 9ce6a53..d5478dc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -setuptools -pybind11 +setuptools>=40.6.0 +pybind11>=2.11.0 recommonmark sphinx_rtd_theme \ No newline at end of file diff --git a/setup.py b/setup.py index 1a1a0f9..6a8a0e9 100644 --- a/setup.py +++ b/setup.py @@ -32,10 +32,15 @@ def run(self): raise RuntimeError("CMake must be installed to build the following extensions: " + ", ".join(e.name for e in self.extensions)) + cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1)) + if platform.system() == "Windows": - cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1)) if cmake_version < '3.1.0': raise RuntimeError("CMake >= 3.1.0 is required on Windows") + else: + if cmake_version < '3.18.0': + raise RuntimeError("CMake >= 3.18.0 is required for Apple Silicon support. " + "Please update CMake by visiting https://cmake.org/download/ or using your system's package manager.") for ext in self.extensions: self.build_extension(ext) @@ -58,6 +63,10 @@ def build_extension(self, ext): cmake_args += ["-DCMAKE_BUILD_WITH_INSTALL_RPATH=TRUE"] cmake_args += ["-DCMAKE_INSTALL_RPATH={}".format("$ORIGIN")] + # Apple Silicon specific settings + if platform.system() == "Darwin" and platform.machine() == "arm64": + cmake_args += ["-DCMAKE_OSX_ARCHITECTURES=arm64"] + if platform.system() == "Windows": cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)] if sys.maxsize > 2 ** 32: @@ -70,6 +79,11 @@ def build_extension(self, ext): env = os.environ.copy() env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''), self.distribution.get_version()) + + # Add Apple Silicon optimization flags + if platform.system() == "Darwin" and platform.machine() == "arm64": + env['CXXFLAGS'] = '{} -mcpu=apple-a14'.format(env.get('CXXFLAGS', '')) + if not os.path.exists(self.build_temp): os.makedirs(self.build_temp) subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) @@ -88,7 +102,14 @@ def build_extension(self, ext): test_suite="tests/SymSpellCppPyTest.py", ext_modules=[CMakeExtension('SymSpellCppPy')], cmdclass=versioneer.get_cmdclass(dict(build_ext=CMakeBuild)), - python_requires=">=3.4", + python_requires=">=3.7", + install_requires=[ + "pybind11>=2.11.0", + ], + setup_requires=[ + "pybind11>=2.11.0", + "setuptools>=40.6.0", + ], zip_safe=False, url="https://github.com/viig99/SymSpellCppPy", classifiers=[ @@ -98,13 +119,11 @@ def build_extension(self, ext): "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11" + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12" ] )