From 8c22b47864c663622024471a1a8e9d3537631d43 Mon Sep 17 00:00:00 2001 From: Kevin Marchais Date: Tue, 27 Jan 2026 18:01:01 +0100 Subject: [PATCH 1/6] Add cibuildwheel configuration for PyPI wheel builds - Add GitHub workflow to build wheels on Linux (x86_64, aarch64), macOS (arm64), and Windows (x64) - Add cibuildwheel configuration in pyproject.toml - Use Development.Module instead of Development for manylinux compatibility - Configure OIDC trusted publishing for PyPI/TestPyPI --- .github/workflows/wheels.yml | 135 +++++++++++++++++++++++++++++++++++ CMakeLists.txt | 2 +- pyproject.toml | 79 ++++++++++++++++++++ 3 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/wheels.yml diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml new file mode 100644 index 00000000..d75d6dd9 --- /dev/null +++ b/.github/workflows/wheels.yml @@ -0,0 +1,135 @@ +name: Build Wheels + +on: + workflow_dispatch: + inputs: + publish_testpypi: + description: 'Publish to TestPyPI' + type: boolean + default: false + push: + tags: + - "v*" + pull_request: + branches: [master] + paths: + - ".github/workflows/wheels.yml" + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + # ========================================================================= + # Build source distribution + # ========================================================================= + build_sdist: + name: Build source distribution + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Build sdist + run: pipx run build --sdist + + - uses: actions/upload-artifact@v4 + with: + name: cibw-sdist + path: dist/*.tar.gz + + # ========================================================================= + # Build wheels on all platforms + # ========================================================================= + build_wheels: + name: Wheels on ${{ matrix.os }} (${{ matrix.cibw_archs }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + # Linux x86_64 + - os: ubuntu-latest + cibw_archs: "x86_64" + + # Linux aarch64 (native ARM runner) + - os: ubuntu-24.04-arm + cibw_archs: "aarch64" + + # macOS Apple Silicon only + - os: macos-latest + cibw_archs: "arm64" + + # Windows x64 + - os: windows-latest + cibw_archs: "AMD64" + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Build wheels + uses: pypa/cibuildwheel@v2.22 + env: + CIBW_ARCHS: ${{ matrix.cibw_archs }} + + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-${{ matrix.os }}-${{ matrix.cibw_archs }} + path: ./wheelhouse/*.whl + + # ========================================================================= + # Publish to TestPyPI (manual trigger) + # ========================================================================= + publish_testpypi: + name: Publish to TestPyPI + needs: [build_sdist, build_wheels] + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' && github.event.inputs.publish_testpypi == 'true' + environment: + name: testpypi + url: https://test.pypi.org/p/simcoon + permissions: + id-token: write + + steps: + - uses: actions/download-artifact@v4 + with: + pattern: cibw-* + path: dist + merge-multiple: true + + - uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + + # ========================================================================= + # Publish to PyPI (release tags only) + # ========================================================================= + publish_pypi: + name: Publish to PyPI + needs: [build_sdist, build_wheels] + runs-on: ubuntu-latest + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + environment: + name: pypi + url: https://pypi.org/p/simcoon + permissions: + id-token: write + + steps: + - uses: actions/download-artifact@v4 + with: + pattern: cibw-* + path: dist + merge-multiple: true + + - name: Display artifacts + run: ls -la dist/ + + - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/CMakeLists.txt b/CMakeLists.txt index d229db02..f1664ea8 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,7 +173,7 @@ set_target_properties(dylib PROPERTIES UNITY_BUILD OFF) # Python dependencies (only if building Python bindings) if(BUILD_PYTHON_BINDINGS) - find_package(Python3 COMPONENTS Interpreter Development NumPy REQUIRED) + find_package(Python3 COMPONENTS Interpreter Development.Module NumPy REQUIRED) find_package(pybind11 REQUIRED) # Try to find carma from conda/system first diff --git a/pyproject.toml b/pyproject.toml index fc127051..b7a1f6ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,4 +110,83 @@ addopts = "-v --tb=short" # Requires build dependencies to be installed first: scikit-build-core, pybind11, numpy no-build-isolation-package = ["simcoon"] +# ============================================================================= +# cibuildwheel configuration +# ============================================================================= + +[tool.cibuildwheel] +# Build for CPython 3.10-3.14 +build = "cp310-* cp311-* cp312-* cp313-* cp314-*" + +# Skip unsupported configurations +skip = [ + "*-musllinux*", # musl libc: complex C++ deps don't support musl + "pp*", # PyPy: pybind11/numpy compatibility issues + "*-win32", # 32-bit Windows: not supported + "*_i686", # 32-bit Linux: not supported + "*-macosx_x86_64", # macOS Intel: not supported +] + +# Build verbosity +build-verbosity = 1 + +# Test configuration +test-requires = ["pytest>=7.0", "numpy>=2.0"] +test-command = "pytest {project}/simcoon-python-builder/test -v --tb=short" + +# ----------------------------------------------------------------------------- +# Linux configuration +# ----------------------------------------------------------------------------- + +[tool.cibuildwheel.linux] +# Use manylinux_2_28 for C++20 compiler support (GCC 12+) and newer tools +manylinux-x86_64-image = "manylinux_2_28" +manylinux-aarch64-image = "manylinux_2_28" + +# Install dependencies before build +before-all = [ + "dnf install -y epel-release", + "dnf install -y cmake ninja-build openblas-devel lapack-devel wget", + # Build Armadillo from source (manylinux packages are too old) + "wget -q https://sourceforge.net/projects/arma/files/armadillo-14.0.2.tar.xz", + "tar -xf armadillo-14.0.2.tar.xz", + "cd armadillo-14.0.2 && cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local -DDETECT_HDF5=OFF -DBUILD_SHARED_LIBS=ON && cmake --build build && cmake --install build", +] + +# Environment for finding installed dependencies +environment = { CMAKE_PREFIX_PATH = "/usr/local", LD_LIBRARY_PATH = "/usr/local/lib:/usr/local/lib64" } + +# auditwheel bundles shared libraries +repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel}" + +# ----------------------------------------------------------------------------- +# macOS configuration (arm64 only) +# ----------------------------------------------------------------------------- + +[tool.cibuildwheel.macos] +# Install dependencies via Homebrew +before-all = "brew install armadillo openblas ninja" + +# Build for arm64 only (native on Apple Silicon runners) +archs = ["arm64"] + +# Deployment target for C++20 support +environment = { MACOSX_DEPLOYMENT_TARGET = "11.0" } + +# delocate bundles shared libraries +repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}" + +# ----------------------------------------------------------------------------- +# Windows configuration +# ----------------------------------------------------------------------------- + +[tool.cibuildwheel.windows] +# Install dependencies via vcpkg +before-all = "vcpkg install armadillo:x64-windows openblas:x64-windows" + +# Point CMake to vcpkg +environment = { CMAKE_TOOLCHAIN_FILE = "C:\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake" } +# Use delvewheel to bundle DLLs +before-build = "pip install delvewheel" +repair-wheel-command = "delvewheel repair -w {dest_dir} {wheel}" From 12f33051a4069851fe2cb04287b1a2d572c7e93a Mon Sep 17 00:00:00 2001 From: Kevin Marchais Date: Tue, 27 Jan 2026 18:03:15 +0100 Subject: [PATCH 2/6] Use uv instead of pipx for sdist build --- .github/workflows/wheels.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index d75d6dd9..19a49d37 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -34,8 +34,10 @@ jobs: with: fetch-depth: 0 + - uses: astral-sh/setup-uv@v5 + - name: Build sdist - run: pipx run build --sdist + run: uv build --sdist - uses: actions/upload-artifact@v4 with: From 852ebe786f352916e822a69fed5ef8853ee9672c Mon Sep 17 00:00:00 2001 From: Kevin Marchais Date: Tue, 27 Jan 2026 18:05:33 +0100 Subject: [PATCH 3/6] Update GitHub Actions to latest versions --- .github/workflows/wheels.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 19a49d37..5963eba1 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -30,16 +30,16 @@ jobs: name: Build source distribution runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: astral-sh/setup-uv@v5 + - uses: astral-sh/setup-uv@v7 - name: Build sdist run: uv build --sdist - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v5 with: name: cibw-sdist path: dist/*.tar.gz @@ -71,16 +71,16 @@ jobs: cibw_archs: "AMD64" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Build wheels - uses: pypa/cibuildwheel@v2.22 + uses: pypa/cibuildwheel@v3.3 env: CIBW_ARCHS: ${{ matrix.cibw_archs }} - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v5 with: name: cibw-wheels-${{ matrix.os }}-${{ matrix.cibw_archs }} path: ./wheelhouse/*.whl @@ -100,7 +100,7 @@ jobs: id-token: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v6 with: pattern: cibw-* path: dist @@ -125,7 +125,7 @@ jobs: id-token: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v6 with: pattern: cibw-* path: dist From 5037bff9cf19f596c3ea2be8e1e5e6d4778af86b Mon Sep 17 00:00:00 2001 From: Kevin Marchais Date: Tue, 27 Jan 2026 18:32:20 +0100 Subject: [PATCH 4/6] Fix cibuildwheel configuration issues - macOS: Update MACOSX_DEPLOYMENT_TARGET to 15.0 (Homebrew libs require it) - Windows: Use forward slashes in vcpkg toolchain path for TOML compatibility - sdist: Use uvx with pyproject-build for proper build isolation --- .github/workflows/wheels.yml | 2 +- pyproject.toml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 5963eba1..24c65d63 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -37,7 +37,7 @@ jobs: - uses: astral-sh/setup-uv@v7 - name: Build sdist - run: uv build --sdist + run: uvx --from build pyproject-build --sdist - uses: actions/upload-artifact@v5 with: diff --git a/pyproject.toml b/pyproject.toml index b7a1f6ff..d206f301 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -170,8 +170,8 @@ before-all = "brew install armadillo openblas ninja" # Build for arm64 only (native on Apple Silicon runners) archs = ["arm64"] -# Deployment target for C++20 support -environment = { MACOSX_DEPLOYMENT_TARGET = "11.0" } +# Deployment target (must match Homebrew library targets) +environment = { MACOSX_DEPLOYMENT_TARGET = "15.0" } # delocate bundles shared libraries repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}" @@ -184,8 +184,8 @@ repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest # Install dependencies via vcpkg before-all = "vcpkg install armadillo:x64-windows openblas:x64-windows" -# Point CMake to vcpkg -environment = { CMAKE_TOOLCHAIN_FILE = "C:\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake" } +# Point CMake to vcpkg (use forward slashes for TOML compatibility) +environment = { CMAKE_TOOLCHAIN_FILE = "C:/vcpkg/scripts/buildsystems/vcpkg.cmake" } # Use delvewheel to bundle DLLs before-build = "pip install delvewheel" From 91818f2ce28e910da1bb21d0abd3c62dbcb39235 Mon Sep 17 00:00:00 2001 From: Kevin Marchais Date: Tue, 27 Jan 2026 19:10:14 +0100 Subject: [PATCH 5/6] Skip Windows wheel repair temporarily to debug --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d206f301..d3582181 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -187,6 +187,6 @@ before-all = "vcpkg install armadillo:x64-windows openblas:x64-windows" # Point CMake to vcpkg (use forward slashes for TOML compatibility) environment = { CMAKE_TOOLCHAIN_FILE = "C:/vcpkg/scripts/buildsystems/vcpkg.cmake" } -# Use delvewheel to bundle DLLs -before-build = "pip install delvewheel" -repair-wheel-command = "delvewheel repair -w {dest_dir} {wheel}" +# Skip wheel repair - the wheel should already contain all needed DLLs +# TODO: investigate delvewheel "Unable to find library: simcoon.dll" error +repair-wheel-command = "" From 883a7499fcd0de3dc797565a20ec570bef0b97da Mon Sep 17 00:00:00 2001 From: Kevin Marchais Date: Wed, 28 Jan 2026 09:38:17 +0100 Subject: [PATCH 6/6] Clean up Windows wheel repair comment --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d3582181..8b25ae76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -187,6 +187,5 @@ before-all = "vcpkg install armadillo:x64-windows openblas:x64-windows" # Point CMake to vcpkg (use forward slashes for TOML compatibility) environment = { CMAKE_TOOLCHAIN_FILE = "C:/vcpkg/scripts/buildsystems/vcpkg.cmake" } -# Skip wheel repair - the wheel should already contain all needed DLLs -# TODO: investigate delvewheel "Unable to find library: simcoon.dll" error +# Skip wheel repair - CMake installs all needed DLLs directly into the wheel repair-wheel-command = ""