From 100c96d87eac0ac4f7c30a495804259cc97579af Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 17 Mar 2025 09:25:45 -0500 Subject: [PATCH] switch to cmake I'm sick of the way the simple extension builder in setup.py doesn't understand header dependencies. --- CMakeLists.txt | 12 ++++++++ pyproject.toml | 1 + setup.py | 78 +++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 74 insertions(+), 17 deletions(-) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b43573f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.4...3.18) +project("_piomatter") +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE) + +find_package(pybind11 CONFIG) +pybind11_add_module("_piomatter" + src/pymain.cpp src/piolib/pio_rp1.c src/piolib/piolib.c) +target_include_directories("_piomatter" PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/src/include + ${CMAKE_CURRENT_LIST_DIR}/src/piolib/include) +set_property(TARGET "_piomatter" PROPERTY + CXX_STANDARD 20) diff --git a/pyproject.toml b/pyproject.toml index f90c314..5e2e427 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,6 +3,7 @@ requires = [ "setuptools>=42", "pybind11>=2.10.0", "setuptools_scm[toml]>=6.2", + "cmake>=3.12" ] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index c7c9e4b..d7f6708 100644 --- a/setup.py +++ b/setup.py @@ -1,36 +1,80 @@ # Available at setup time due to pyproject.toml -from pybind11.setup_helpers import Pybind11Extension, build_ext -from setuptools import setup +import multiprocessing +import os +import subprocess +import sys +from pathlib import Path + +from setuptools import Extension, setup +from setuptools.command.build_ext import build_ext from setuptools_scm import get_version __version__ = get_version() +# A CMakeExtension needs a sourcedir instead of a file list. +# The name must be the _single_ output extension from the CMake build. +# If you need multiple extensions, see scikit-build. +class CMakeExtension(Extension): + def __init__(self, name: str, sourcedir: str = "") -> None: + super().__init__(name, sources=[]) + self.sourcedir = os.fspath(Path(sourcedir).resolve()) + + +class CMakeBuild(build_ext): + def build_extension(self, ext: CMakeExtension) -> None: + # Must be in this form due to bug in .resolve() only fixed in Python 3.10+ + ext_fullpath = Path.cwd() / self.get_ext_fullpath(ext.name) + extdir = ext_fullpath.parent.resolve() + + # Using this requires trailing slash for auto-detection & inclusion of + # auxiliary "native" libs + + debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug + cfg = "Debug" if debug else "Release" + + # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON + # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code + # from Python. + cmake_args = [ + "-GUnix Makefiles", + f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", + f"-DPYTHON_EXECUTABLE={sys.executable}", + f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm + ] + build_args = [] + # Adding CMake arguments set as environment variable + # (needed e.g. to build for ARM OSx on conda-forge) + if "CMAKE_ARGS" in os.environ: + cmake_args += [item for item in os.environ["CMAKE_ARGS"].split(" ") if item] + + build_args += [f"-j{multiprocessing.cpu_count()}"] + + build_temp = Path(self.build_temp) / ext.name + if not build_temp.exists(): + build_temp.mkdir(parents=True) + + subprocess.run( + ["cmake", ext.sourcedir, *cmake_args], cwd=build_temp, check=True + ) + subprocess.run( + ["make", *build_args], cwd=build_temp, check=True + ) + + + # The main interface is through Pybind11Extension. # * You can add cxx_std=11/14/17, and then build_ext can be removed. # * You can set include_pybind11=false to add the include directory yourself, # say from a submodule. -ext_modules = [ - Pybind11Extension("adafruit_blinka_raspberry_pi5_piomatter._piomatter", - ["src/pymain.cpp", "src/piolib/piolib.c", "src/piolib/pio_rp1.c"], - define_macros = [('VERSION_INFO', __version__)], - include_dirs = ['./src/include', './src/piolib/include'], - cxx_std=20, - # use this setting when debugging - extra_compile_args = ["-g3", "-Og"], - ), -] - setup( name="Adafruit-Blinka-Raspberry-Pi5-Piomatter", version=__version__, url="https://github.com/adafruit/Adafruit_Blinka_Raspberry_Pi5_Piomatter", description="HUB75 matrix driver for Raspberry Pi 5 using PIO", long_description="A pio-based driver", - ext_modules=ext_modules, - # Currently, build_ext only provides an optional "highest supported C++ - # level" feature, but in the future it may provide more features. - cmdclass={"build_ext": build_ext}, + ext_modules=[CMakeExtension("adafruit_blinka_raspberry_pi5_piomatter._piomatter")], + cmdclass={"build_ext": CMakeBuild}, zip_safe=False, python_requires=">=3.11", packages=['adafruit_blinka_raspberry_pi5_piomatter'],