Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
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@v6
with:
fetch-depth: 0

- uses: astral-sh/setup-uv@v7

- name: Build sdist
run: uvx --from build pyproject-build --sdist

- uses: actions/upload-artifact@v5
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@v6
with:
fetch-depth: 0

- name: Build wheels
uses: pypa/cibuildwheel@v3.3
env:
CIBW_ARCHS: ${{ matrix.cibw_archs }}

- uses: actions/upload-artifact@v5
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@v6
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@v6
with:
pattern: cibw-*
path: dist
merge-multiple: true

- name: Display artifacts
run: ls -la dist/

- uses: pypa/gh-action-pypi-publish@release/v1
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
78 changes: 78 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,82 @@ 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 (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}"

# -----------------------------------------------------------------------------
# Windows configuration
# -----------------------------------------------------------------------------

[tool.cibuildwheel.windows]
# Install dependencies via vcpkg
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 - CMake installs all needed DLLs directly into the wheel
repair-wheel-command = ""
Loading