Date: 2026-07-14
Status: Plan — not yet implemented
Tracking: #229 (PyPI ships pure-Python), #225 (macOS/Windows runner billing), #234
Related: docs/RUST_EVALUATION.md (why C++, not Rust, is the wheel to ship)
Make the C++ speedup the default for a plain install, with no toolchain on the user's side:
pip install chilmesh[cpp] # downloads a prebuilt binary; no compiler neededToday pip install chilmesh is pure-Python (the compiled extension must be built
from source — a C++ compiler + CMake barrier that most users, especially on
managed/HPC/Windows machines, will not clear). Prebuilding the wheel once per
platform in CI and publishing it to PyPI removes that barrier entirely: pip
downloads the already-compiled binary matching the user's OS + Python.
Non-goal: Rust wheels. The Rust backend is frozen (see
RUST_EVALUATION.md); it earns no perf niche over C++ and
is not part of the distribution story.
| Piece | State |
|---|---|
chilmesh (main pkg) |
pure-Python, setuptools, requires-python >=3.10, PyPI = no compiled ext (#229) |
chilmesh-cpp (src/chilmesh_cpp) |
separate package, scikit-build-core + pybind11, version 0.6.0.dev0, not on PyPI |
build-cpp-wheels.yml |
build-only: cibuildwheel manylinux x86_64 only, cp310–312, musllinux skipped, asserts import chilmesh_cpp — uploads artifacts only, no publish; manual dispatch |
publish-pypi.yml |
publishes the main pkg on release; twine + PYPI_API_TOKEN secret |
| Backend selection | chilmesh.backend_info() auto-detects import chilmesh_cpp and picks it — no code change needed once the wheel is installable |
So the runtime detection already works; the entire gap is packaging + distribution.
Model A (recommended): publish chilmesh-cpp as its own binary-wheel package; add a
chilmesh[cpp] extra that depends on it.
chilmeshstays pure-Python (universal default + fallback; sdist/uncovered platforms still work).chilmesh[cpp]pulls the prebuiltchilmesh-cppbinary → auto-selected at import.- Minimal churn: matches the existing two-package layout and the existing
import chilmesh_cppdetection. No change tochilmesh's build backend.
Model B (rejected for now): bundle the C++ extension into the main chilmesh wheel.
Would turn chilmesh itself into a per-platform binary package (change its build
backend to scikit-build-core, ship a pure-Python sdist fallback). More invasive, and
it couples the pure-Python reference release cadence to the compiled build matrix.
Revisit only if Model A's two-package version-sync proves painful.
- Ratify Model A.
- Define the version-compatibility contract between
chilmeshandchilmesh-cpp(they version independently today:chilmesh1.4.x vschilmesh-cpp0.6.0.dev0). Pin a compatible range in the extra, e.g.chilmesh[cpp]→chilmesh-cpp>=0.6,<0.7, and bump them together on any extension-API change. Record the contract inCONTRIBUTING.md. - Graduate
chilmesh-cppoff.dev0to a real release version (e.g.0.6.0).
- Add macOS (
x86_64+arm64/ universal2) and Windows (AMD64) alongside manylinuxx86_64. Resolve the runner-billing question in #225 first (macOS/ Windows are paid GitHub runners). - Keep
cp310 cp311 cp312; addcp313once pybind11/deps support it. (pybind11 needs a wheel per Python version — this matrix is inherentlyplatforms × versions. This is the one place Rust's maturinabi3single-wheel would be simpler — noted, but out of scope since C++ wins on perf.) - Decide musllinux (currently skipped) and linux
aarch64(emulated builds are slow) — defer both unless a consumer needs them. - Strengthen
CIBW_TEST_COMMAND: import and run one realfull_initon a tiny mesh, not justhasattr(full_init).
- New job (or new workflow
publish-cpp-wheels.yml) that, on achilmesh-cpprelease tag, builds the full matrix + sdist andtwine uploads to PyPI. - Auth: reuse the
PYPI_API_TOKENpattern frompublish-pypi.yml, or migrate to PyPI Trusted Publishing (OIDC,id-token: write) — preferred, no long-lived token. - Guard strictly on the tag/release event so nothing publishes from
development.
- In
chilmesh'spyproject.toml:[project.optional-dependencies] cpp = ["chilmesh-cpp>=0.6,<0.7"]
- No backend code change —
backend_info()already importschilmesh_cpp. - README + docs: document
pip install chilmesh[cpp]; update the "PyPI installs are pure-Python" note (#229) to "plain install = pure-Python;chilmesh[cpp]= prebuilt C++ where a wheel exists, pure-Python fallback elsewhere."
- Post-publish smoke: in a clean environment on each platform,
pip install chilmesh[cpp]then assertbackend_info()['selected'] == 'cpp'and runtests/test_backend_equivalence.pyagainst the installed wheel (notsrc/— avoid the namespace-stub shadowing from #163: run from outside the repo tree). - Update
docs/BENCHMARK.md/ README wheel-availability notes; close #229.
- Runner cost (#225): macOS/Windows runners are billed; Phase 1 is gated on that
decision. manylinux (Linux) is free on
ubuntu-latestand can ship first. - Matrix size: pybind11 → one wheel per (platform × Python version). Manageable at 3–4 Python versions × 3 platforms, but it grows.
- macOS arm64: cross-build/
universal2needs care in cibuildwheel; test on Apple silicon if possible. - glibc target: pick a manylinux baseline (e.g.
manylinux_2_28) broad enough for HPC/older distros. - Version sync: the
chilmesh↔chilmesh-cppcompatibility pin must be maintained on every extension-API change (Phase 0 contract). - Test isolation: always validate against the installed wheel, never the
src/source dir onsys.path(#163 false-positiveCPP_AVAILABLE).
- Phase 0 (design/pin) — cheap, unblocks everything.
- Phase 1 Linux-only publish first (free runners) → real prebuilt wheel on PyPI for the largest user base, fastest win.
- Resolve #225, then add macOS/Windows (Phase 1 remainder) + Phase 2 publish job.
- Phase 3 extra + docs, Phase 4 validation, close #229.
PROPOSED — not yet applied. These are copy-paste-ready but intentionally inert (they are documentation, not live workflow files). Apply them only after Model A is ratified (Phase 0) and the macOS/Windows runner-billing question (#225) is resolved. The
macos-latest/windows-latestmatrix legs and the publish job are the parts that incur runner cost or perform the irreversible, outward act of uploading to PyPI — those need operator sign-off before they go live. The Linux-only build leg is free and safe to enable first.
Replace the single build-manylinux job with a matrixed build. macOS/Windows legs
are gated on #225 — drop them from matrix.os to ship Linux-only first.
jobs:
build-wheels:
name: cibuildwheel chilmesh_cpp (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest] # macos/windows gated on #225
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install cibuildwheel
run: python -m pip install cibuildwheel==2.21.3
- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse src/chilmesh_cpp
env:
CIBW_BUILD: "cp310-* cp311-* cp312-*"
CIBW_ARCHS_LINUX: "x86_64"
CIBW_ARCHS_MACOS: "x86_64 arm64"
CIBW_ARCHS_WINDOWS: "AMD64"
CIBW_SKIP: "*-musllinux*"
# Real smoke: build a mesh, not just check the symbol exists.
CIBW_TEST_REQUIRES: "numpy"
CIBW_TEST_COMMAND: >
python -c "import numpy as np, chilmesh_cpp;
pts=np.array([[0.,0.],[1.,0.],[0.,1.],[1.,1.]]);
conn=np.array([[0,1,2],[1,3,2]],dtype=np.int32);
m=chilmesh_cpp.full_init(pts, conn);
assert m.n_elems==2, m.n_elems; print('cpp full_init OK', m.n_verts, m.n_elems)"
- uses: actions/upload-artifact@v4
with:
name: chilmesh-cpp-wheels-${{ matrix.os }}
path: wheelhouse/*.whl
if-no-files-found: errorNew workflow. Prefer Trusted Publishing (OIDC, no long-lived token); the
token variant (mirroring publish-pypi.yml) is shown as a fallback comment. The
build job reuses A1; only publish is new.
name: publish-cpp-wheels
on:
release:
types: [published] # publishes ONLY on a real GitHub release; never on push
workflow_dispatch:
permissions:
contents: read
jobs:
build:
# ... reuse the A1 matrix build; uploads per-OS wheel artifacts ...
sdist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- run: pipx run build --sdist --outdir dist src/chilmesh_cpp
- uses: actions/upload-artifact@v4
with: { name: chilmesh-cpp-sdist, path: dist/*.tar.gz }
publish:
needs: [build, sdist]
runs-on: ubuntu-latest
environment: pypi # protect with required reviewers in repo settings
permissions:
id-token: write # PyPI Trusted Publishing (OIDC) — no secret
steps:
- uses: actions/download-artifact@v4
with: { path: dist, merge-multiple: true }
- uses: pypa/gh-action-pypi-publish@release/v1
# Token fallback (if not using Trusted Publishing):
# with: { password: ${{ secrets.PYPI_API_TOKEN }} }chilmesh/pyproject.toml (add alongside the existing dev extra) — land this in
the same change that publishes chilmesh-cpp, never before, or
pip install chilmesh[cpp] resolves to a package that isn't on PyPI yet:
[project.optional-dependencies]
cpp = ["chilmesh-cpp>=0.6,<0.7"] # pin bumps with any extension-API change (Phase 0 contract)README / docs one-liner:
pip install chilmesh # pure-Python, runs everywhere
pip install chilmesh[cpp] # + prebuilt C++ acceleration where a wheel existsNo backend code changes — chilmesh.backend_info() already imports chilmesh_cpp
and auto-selects it.