diff --git a/.github/workflows/buildpublish.yml b/.github/workflows/buildpublish.yml new file mode 100644 index 0000000..c193a21 --- /dev/null +++ b/.github/workflows/buildpublish.yml @@ -0,0 +1,64 @@ +name: Build wheels/sdist and publish to PyPI + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + workflow_dispatch: + +jobs: + make_sdist: + name: Make SDist + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build SDist + run: pipx run build --sdist + - uses: actions/upload-artifact@v4 + with: + name: cibw-sdist + path: dist/*.tar.gz + build_wheels: + name: Wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-13, macos-14] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + - uses: pypa/cibuildwheel@v2.23 + env: + CIBW_ARCHS: auto64 + CIBW_SKIP: "*musllinux* pp*" + CIBW_MANYLINUX_X86_64_IMAGE: quay.io/pypa/manylinux_2_34 + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-${{ matrix.os }} + path: wheelhouse/*.whl + publish_all: + name: Publish on PyPI + needs: [build_wheels, make_sdist] + runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write + attestations: write + contents: read + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + pattern: cibw-* + path: dist + merge-multiple: true + - name: Generate artifact attestations + uses: actions/attest-build-provenance@v2.2.3 + with: + subject-path: "dist/*" + - name: Upload to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index 4ef79c3..c6ef758 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .vscode/* .idea/* build/* +dist/* cmake-build-*/* python/.venv/* .venv/* diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 498897e..0000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "CDT"] - path = CDT - url = https://github.com/artem-ogre/CDT.git -[submodule "pybind11"] - path = pybind11 - url = https://github.com/pybind/pybind11.git diff --git a/CDT b/CDT deleted file mode 160000 index 58f34da..0000000 --- a/CDT +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 58f34da24b438bd17629450fcec189ccb181dc9f diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a51f53..87c9e01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,23 +1,31 @@ -cmake_minimum_required(VERSION 3.13.0) +cmake_minimum_required(VERSION 3.18.0) -set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() project( - PythonCDT - VERSION 0.1.0 - DESCRIPTION "Software surface data rasterizer library" + ${SKBUILD_PROJECT_NAME} + VERSION ${SKBUILD_PROJECT_VERSION} LANGUAGES CXX ) set(CMAKE_CXX_STANDARD 17) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -add_subdirectory(CDT/CDT CDT) -add_subdirectory(pybind11) -pybind11_add_module(PythonCDT cdt_bindings.cpp) -target_include_directories( - PythonCDT PRIVATE $ +set(PYBIND11_NEWPYTHON ON) +find_package(pybind11 CONFIG REQUIRED) + +include(FetchContent) +FetchContent_Declare( + CDT + GIT_REPOSITORY https://github.com/artem-ogre/CDT.git + GIT_TAG 4b4181713c73cf0a49a5b88fb3df4acca7436235 + SOURCE_SUBDIR CDT ) -target_link_libraries(PythonCDT PRIVATE CDT::CDT) -# Use rasterizer as pre-compiled header for faster test-only re-compiles -#target_precompile_headers(PythonCDT PRIVATE include/rasterizer.h) +FetchContent_MakeAvailable(CDT) + +pybind11_add_module(${SKBUILD_PROJECT_NAME} MODULE cdt_bindings.cpp) +target_link_libraries(${SKBUILD_PROJECT_NAME} PRIVATE pybind11::module) +target_link_libraries(${SKBUILD_PROJECT_NAME} PRIVATE CDT::CDT) +install(TARGETS ${SKBUILD_PROJECT_NAME} LIBRARY DESTINATION .) diff --git a/pybind11 b/pybind11 deleted file mode 160000 index 8b48ff8..0000000 --- a/pybind11 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8b48ff878c168b51fe5ef7b8c728815b9e1a9857 diff --git a/pyproject.toml b/pyproject.toml index 7b3b581..e6328b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,25 +1,23 @@ +[project] +name = "PythonCDT" +version = "0.0.1" +authors = [{name = "Leica Geosystems"}] +description = "Constrained Delaunay Triangulation" +readme = {file = "README.md", content-type = "text/markdown"} +requires-python=">=3.10" + +[project.license] +text = "MPL-2.0" + +[project.urls] +Repository = "https://github.com/artem-ogre/PythonCDT" + [build-system] -requires = [ - "setuptools>=42", - "wheel", - "ninja", - "cmake>=3.12", -] -build-backend = "setuptools.build_meta" +requires = ["scikit-build-core", "pybind11"] +build-backend = "scikit_build_core.build" + +[tool.scikit-build] +wheel.exclude = ["include/*", "cmake/*"] [tool.isort] profile = "black" - -[tool.pytest.ini_options] -minversion = "6.0" -addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"] -xfail_strict = true -filterwarnings = ["error"] -testpaths = ["tests"] - -[tool.cibuildwheel] -test-command = "pytest {project}/tests" -test-extras = ["test"] -test-skip = ["*universal2:arm64"] -# Setuptools bug causes collision between pypy and cpython artifacts -before-build = "rm -rf {project}/build" diff --git a/setup.py b/setup.py deleted file mode 100644 index cbe1c3b..0000000 --- a/setup.py +++ /dev/null @@ -1,140 +0,0 @@ -import os -import re -import subprocess -import sys - -from setuptools import Extension, setup -from setuptools.command.build_ext import build_ext - -# Convert distutils Windows platform specifiers to CMake -A arguments -PLAT_TO_CMAKE = { - "win32": "Win32", - "win-amd64": "x64", - "win-arm32": "ARM", - "win-arm64": "ARM64", -} - - -# 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, sourcedir=""): - Extension.__init__(self, name, sources=[]) - self.sourcedir = os.path.abspath(sourcedir) - - -class CMakeBuild(build_ext): - def build_extension(self, ext): - extdir = os.path.abspath(os.path.dirname( - self.get_ext_fullpath(ext.name))) - - # required for auto-detection & inclusion of auxiliary "native" libs - if not extdir.endswith(os.path.sep): - extdir += os.path.sep - - debug = int(os.environ.get("DEBUG", 0) - ) if self.debug is None else self.debug - cfg = "Debug" if debug else "Release" - - # CMake lets you override the generator - we need to check this. - # Can be set with Conda-Build, for example. - cmake_generator = os.environ.get("CMAKE_GENERATOR", "") - - # 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 = [ - f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}", - 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] - - # In this example, we pass in the version to C++. You might not need to. - cmake_args += [ - f"-DEXAMPLE_VERSION_INFO={self.distribution.get_version()}"] - - if self.compiler.compiler_type != "msvc": - # Using Ninja-build since it a) is available as a wheel and b) - # multithreads automatically. MSVC would require all variables be - # exported for Ninja to pick it up, which is a little tricky to do. - # Users can override the generator with CMAKE_GENERATOR in CMake - # 3.15+. - if not cmake_generator: - try: - import ninja # noqa: F401 - - cmake_args += ["-GNinja"] - except ImportError: - pass - - else: - - # Single config generators are handled "normally" - single_config = any( - x in cmake_generator for x in {"NMake", "Ninja"}) - - # CMake allows an arch-in-generator style for backward compatibility - contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"}) - - # Specify the arch if using MSVC generator, but only if it doesn't - # contain a backward-compatibility arch spec already in the - # generator name. - if not single_config and not contains_arch: - cmake_args += ["-A", PLAT_TO_CMAKE[self.plat_name]] - - # Multi-config generators have a different way to specify configs - if not single_config: - cmake_args += [ - f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}" - ] - build_args += ["--config", cfg] - - if sys.platform.startswith("darwin"): - # Cross-compile support for macOS - respect ARCHFLAGS if set - archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) - if archs: - cmake_args += [ - "-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] - - # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level - # across all generators. - if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ: - # self.parallel is a Python 3 only way to set parallel jobs by hand - # using -j in the build_ext call, not supported by pip or PyPA-build. - if hasattr(self, "parallel") and self.parallel: - # CMake 3.12+ only. - build_args += [f"-j{self.parallel}"] - - 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 - ) - subprocess.check_call( - ["cmake", "--build", "."] + build_args, cwd=self.build_temp - ) - - -# The information here can also be placed in setup.cfg - better separation of -# logic and declaration, and simpler if you include description/version in a file. -setup( - name="PythonCDT", - version="0.0.1", - author="Leica Geosystems", - author_email="", - description="Test", - long_description="", - ext_modules=[CMakeExtension("PythonCDT")], - cmdclass={"build_ext": CMakeBuild}, - zip_safe=False, - extras_require={"test": ["pytest>=6.0"]}, - python_requires=">=3.6", -)