From 689bbf1fa9e7d99f091c9586a7a9eea33a474778 Mon Sep 17 00:00:00 2001 From: Markus Worchel <9449513+mworchel@users.noreply.github.com> Date: Wed, 2 Apr 2025 10:55:47 +0200 Subject: [PATCH 1/5] Remove `numpy` dependency (only required for tests) --- pyproject.toml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index de0751b..4243f3f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,24 +15,22 @@ authors = [{name = "Markus Worchel", email = "m.worchel@campus.tu-berlin.de"}] license = {file = "LICENSE"} description = "Python bindings for xatlas" urls = {Homepage = "https://github.com/mworchel/xatlas-python"} -dependencies = ["numpy"] [project.readme] file = "README.md" content-type = "text/markdown" [project.optional-dependencies] -test = ["trimesh", +test = ["numpy", "pytest", - "scipy"] + "scipy", + "trimesh"] [tool.cibuildwheel] # Run the package tests on every wheel using `pytest` test-command = "pytest {package}/tests" # will install pytest and other packages in the `test` extra test-extras = ["test"] -# Skip PyPy on Windows as it doesn't appear to have numpy wheels -skip = ["pp*-win*", "*musllinux_i686"] [tool.setuptools] license-files = [] From e0580880a728c8e5c2f28b1a664a93d4defa6c45 Mon Sep 17 00:00:00 2001 From: Markus Worchel <9449513+mworchel@users.noreply.github.com> Date: Wed, 2 Apr 2025 11:02:29 +0200 Subject: [PATCH 2/5] Build and test action for Python 3.13, x64 MacOS, and latest Ubuntu --- .github/workflows/install-and-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/install-and-test.yml b/.github/workflows/install-and-test.yml index 514164a..797d83a 100644 --- a/.github/workflows/install-and-test.yml +++ b/.github/workflows/install-and-test.yml @@ -10,8 +10,8 @@ jobs: build: strategy: matrix: - python-version: [3.8, 3.9, '3.10', '3.11', '3.12'] - platform: [windows-latest, macos-latest, ubuntu-22.04] + python-version: [3.8, 3.9, '3.10', '3.11', '3.12', '3.13'] + platform: [ubuntu-latest, windows-latest, macos-latest, macos-13] runs-on: ${{ matrix.platform }} From 4a1f75bc975e03809dba19634b4ac9ffaeed7d13 Mon Sep 17 00:00:00 2001 From: Markus Worchel <9449513+mworchel@users.noreply.github.com> Date: Wed, 2 Apr 2025 11:23:35 +0200 Subject: [PATCH 3/5] Build using `scikit-build-core` --- CMakeLists.txt | 5 +- pyproject.toml | 21 ++++---- setup.py | 132 --------------------------------------------- src/CMakeLists.txt | 5 +- src/module.cpp | 9 ++++ 5 files changed, 28 insertions(+), 144 deletions(-) delete mode 100644 setup.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 17736f1..b5b32ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.15...3.27) -project(xatlas-python LANGUAGES CXX) +project(xatlas-python LANGUAGES CXX + VERSION ${SKBUILD_PROJECT_VERSION}) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED true) diff --git a/pyproject.toml b/pyproject.toml index 4243f3f..f24ee55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,6 @@ [build-system] -requires = [ - "setuptools>=42", - "wheel", - "ninja; sys_platform != 'win32'", - "cmake>=3.12", -] -build-backend = "setuptools.build_meta" +requires = ["scikit-build-core>=0.10"] +build-backend = "scikit_build_core.build" [project] name = "xatlas" @@ -26,11 +21,19 @@ test = ["numpy", "scipy", "trimesh"] +[tool.scikit-build] +wheel.expand-macos-universal-tags = true +# Protect the configuration against future changes in scikit-build-core +minimum-version = "build-system.requires" +# Setuptools-style build caching in a local directory +build-dir = "build/{wheel_tag}" + [tool.cibuildwheel] # Run the package tests on every wheel using `pytest` test-command = "pytest {package}/tests" # will install pytest and other packages in the `test` extra test-extras = ["test"] -[tool.setuptools] -license-files = [] +# Needed for full C++17 support +[tool.cibuildwheel.macos.environment] +MACOSX_DEPLOYMENT_TARGET = "10.14" \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 1ab62bb..0000000 --- a/setup.py +++ /dev/null @@ -1,132 +0,0 @@ -# Code adapted from https://github.com/pybind/cmake_example - -# -*- coding: utf-8 -*- -import os -import re -from pathlib import Path -import sys -import subprocess - -from setuptools import setup, Extension -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: 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" - - # 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 - # VERSION_INFO shows you how to pass a value into the C++ code - # from Python. - cmake_args = [ - f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", - f"-DPYTHON_EXECUTABLE={sys.executable}", - f"-DVERSION_INFO={self.distribution.get_version()}", - 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] - - 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 or cmake_generator == "Ninja": - try: - import ninja - - ninja_executable_path = Path(ninja.BIN_DIR) / "ninja" - cmake_args += [ - "-GNinja", - f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}", - ] - 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}"] - - 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( - ["cmake", "--build", ".", *build_args], cwd=build_temp, check=True - ) - - -# The information here can also be placed in pyproject.toml - better separation of -# logic and declaration, and simpler if you include description/version in a file. -setup( - ext_modules=[CMakeExtension("xatlas")], - cmdclass={"build_ext": CMakeBuild}, -) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f354445..559202b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,4 +5,7 @@ pybind11_add_module(xatlas module.cpp target_link_libraries(xatlas PRIVATE xatlas-cpp) -target_compile_definitions(xatlas PRIVATE VERSION_INFO=${VERSION_INFO}) \ No newline at end of file +target_compile_definitions(xatlas PRIVATE VERSION_INFO=${PROJECT_VERSION}) + +# The install directory is the output (wheel) directory +install(TARGETS xatlas DESTINATION .) \ No newline at end of file diff --git a/src/module.cpp b/src/module.cpp index b49d34f..9a59636 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -41,6 +41,9 @@ namespace py = pybind11; +#define STRINGIFY(x) #x +#define MACRO_STRINGIFY(x) STRINGIFY(x) + auto parametrize(ContiguousArray const& positions, ContiguousArray const& indices, std::optional> normals = std::nullopt, @@ -146,4 +149,10 @@ PYBIND11_MODULE(xatlas, m) // I/O functions m.def("export", &exportObj, py::arg("path"), py::arg("positions"), py::arg("indices") = std::nullopt, py::arg("uvs") = std::nullopt, py::arg("normals") = std::nullopt); + +#ifdef VERSION_INFO + m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); +#else + m.attr("__version__") = "dev"; +#endif } \ No newline at end of file From c4d0bd865d6bfe1cfbe72ac0ff5c85cb508f9e36 Mon Sep 17 00:00:00 2001 From: Markus Worchel <9449513+mworchel@users.noreply.github.com> Date: Wed, 2 Apr 2025 12:31:06 +0200 Subject: [PATCH 4/5] Downgrade pybind11 to `v2.13.6` --- extern/pybind11 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/pybind11 b/extern/pybind11 index e7e5d6e..a2e59f0 160000 --- a/extern/pybind11 +++ b/extern/pybind11 @@ -1 +1 @@ -Subproject commit e7e5d6e5bb0af543a2ded6d34163176c3e6ab745 +Subproject commit a2e59f0e7065404b44dfe92a28aca47ba1378dc4 From 1d36f6b2fe986b85445013b28f2a5ac59ec12880 Mon Sep 17 00:00:00 2001 From: Markus Worchel <9449513+mworchel@users.noreply.github.com> Date: Wed, 2 Apr 2025 12:45:49 +0200 Subject: [PATCH 5/5] Fix CI --- .github/workflows/wheels.yml | 33 +++++++++++++++++++-------------- pyproject.toml | 13 ++++++++++++- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 47b6714..a61c6eb 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -12,11 +12,12 @@ jobs: name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest, macos-13] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true @@ -25,31 +26,32 @@ jobs: with: output-dir: wheelhouse + - name: Verify clean directory + run: git diff --exit-code + shell: bash + - uses: actions/upload-artifact@v4 with: - path: ./wheelhouse/*.whl + name: cibw-wheels-${{ matrix.os }} + path: wheelhouse/*.whl build_sdist: name: Build SDist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true - - uses: actions/setup-python@v4 - - - name: Install deps - run: python -m pip install twine build - - name: Build SDist - run: python -m build -s + run: pipx run build --sdist - name: Check metadata - run: twine check dist/* + run: pipx run twine check dist/* - uses: actions/upload-artifact@v4 with: + name: cibw-sdist path: dist/*.tar.gz upload_all: @@ -59,14 +61,17 @@ jobs: if: github.event_name == 'release' && github.event.action == 'published' steps: - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.x" - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v4 with: - name: artifact + pattern: cibw-* + merge-multiple: true path: dist - - uses: pypa/gh-action-pypi-publish@v1.4.2 + - uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_TOKEN }} diff --git a/pyproject.toml b/pyproject.toml index f24ee55..01a8163 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,18 @@ build-dir = "build/{wheel_tag}" test-command = "pytest {package}/tests" # will install pytest and other packages in the `test` extra test-extras = ["test"] +# Skip testing of PyPy and 32-bit wheels +test-skip = ["pp*", "*win32", "*i686", "*-musllinux*"] # Needed for full C++17 support [tool.cibuildwheel.macos.environment] -MACOSX_DEPLOYMENT_TARGET = "10.14" \ No newline at end of file +MACOSX_DEPLOYMENT_TARGET = "10.14" + +# Install OpenBLAS on linux (required for scipy in tests) +[tool.cibuildwheel.linux] +before-all = "yum install -y openblas-devel" + +# Skip OpenBLAS installation on untested variants +[[tool.cibuildwheel.overrides]] +select = ["*i686", "*-musllinux*"] +before-all = "" \ No newline at end of file