Releasing and the native wheels#
solprop is released as two distributions from one repository, at one version taken from the git tag (SEP 29):
solprop, pure Python: a source distribution and one universal wheel.solprop-native, the compiled backend built fromnative/: wheels only, one per supported platform. No source distribution is published, so that where no wheel matches,pip install "solprop[native]"fails to resolve instead of starting a C++ build; source builds are made from the repository (Building the native backend).
The wheels are built, repaired, qualified and published by
.github/workflows/wheels.yml. cibuildwheel’s configuration is the
[tool.cibuildwheel] table of native/pyproject.toml; the libraries each wheel bundles
are built from pinned, checksummed sources by the scripts in native/ci/, each of which
also runs by hand.
The wheels#
Linux x86-64 |
Linux aarch64 |
macOS arm64 |
Windows x64 |
|
|---|---|---|---|---|
CI runner |
|
|
|
|
build environment |
|
|
Homebrew’s Clang, deployment target 14.0 (macOS Sonoma) |
Clang on the MSVC ABI (MSVC STL and CRT, |
CPU baseline |
x86-64-v3 |
Neoverse N1 |
Apple M1 |
x86-64-v3 |
host execution space |
Kokkos OpenMP |
Kokkos OpenMP |
Kokkos OpenMP |
Kokkos OpenMP |
OpenMP runtime |
GCC’s |
GCC’s |
LLVM |
LLVM |
BLAS |
OpenBLAS 0.3.34, OpenMP, |
the same |
Accelerate |
OpenBLAS 0.3.34, OpenMP, one fixed Haswell target (no |
FFTW 3.3.11 |
shared, SSE2 and AVX codelets |
shared, cycle counter |
shared, cycle counter |
static, SSE2 and AVX codelets |
repair |
|
|
|
|
Every bundled library runs on the same OpenMP runtime as Kokkos, so a process holds one
thread pool for the backend. The wheel carries the licence text of every library compiled
into it or copied in by the repair (LICENSE-*.txt beside the extension). CPython 3.14
only; free-threaded CPython is not a target.
Building a wheel locally#
Every wheel is the distribution profile (SOLPROP_NATIVE_PROFILE=DISTRIBUTION), never
a build for the build machine’s own architecture.
macOS arm64. cibuildwheel needs python.org’s framework Python, which it installs only
on CI. native/ci/build-wheel-macos.sh runs the same steps with uv: it builds libomp
and FFTW for the deployment target (native/ci/build-deps.sh), builds the wheel and
repairs it with delocate.
brew install llvm cmake ninja
native/ci/build-wheel-macos.sh /tmp/wheels # the repaired wheel lands in /tmp/wheels
Keep the script and [tool.cibuildwheel.macos] in step: they set the same deployment
target, CMake arguments and delocate check.
Linux (x86-64 or aarch64, on a machine of that architecture with Docker):
uvx cibuildwheel --platform linux --output-dir wheelhouse native
Windows x64, from a Visual Studio developer prompt (the x64 MSVC tools and SDK in the environment), with LLVM, CMake and Ninja installed:
uvx cibuildwheel --platform windows --output-dir wheelhouse native
To build one platform’s dependencies by hand: native/ci/build-deps.sh PREFIX (Linux,
macOS) or native/ci/build-deps-windows.ps1 -Prefix PREFIX.
Qualifying a wheel#
The workflow’s test jobs install each repaired wheel beside solprop from the same checkout and run, in order:
python -I native/ci/smoke.py --version VERSION: the package imports and passescheck_build(), is the distribution build at the release’s version, loads the FFT, BLAS and OpenMP libraries from inside the wheel, and carries their licences;python -I -m solprop_native: the build record;python -I native/ci/coexistence.py: NumPy, SciPy, pyFFTW and Numba imported before and aftersolprop_native, each order in a fresh process, every package’s thread pool started, and a short propagation run on the native backend, onnumba_cpuand on the native backend again in the same process;uv run --no-sync poe testwithSOLPROP_TEST_BACKEND=native: solprop’s default lane on the native backend.
On Linux x86-64, the cpu-floor job runs the wheel under Intel’s Software Development
Emulator: the smoke test on a Haswell (x86-64-v3); a refusal before any native code runs
on a Nehalem (x86-64-v2, no AVX) and a Sandy Bridge (AVX without AVX2, FMA or BMI), naming
the missing features; and, on the Sandy Bridge, backend="auto" resolving to
numba_cpu with its one warning. It runs when the repository variables SDE_URL and
SDE_SHA256 name an SDE release. The other platforms’ checks (Windows through
IsProcessorFeaturePresent, Linux aarch64 through /proc/cpuinfo’s Features) are
unit-tested with stand-in processors in native/tests/test_cpu_baseline.py; no CI job
runs them on a processor below the baseline.
macOS and pyFFTW. pyFFTW’s macOS wheel carries its own libomp, which it initialises
when it is imported. LLVM’s OpenMP runtime refuses, or crashes on, a second initialised
copy, so the native package raises at import when pyFFTW is already loaded
(OpenMP runtimes and pyFFTW).
solprop imports both backends lazily, so a script that only calls solprop is unaffected.
coexistence.py expects that refusal on macOS; --bypass-guard adds a report-only
scenario that hides pyFFTW from the guard, to test whether it is still needed.
Making a release#
On
main, with CI green, check that the changelog’sUnreleasedsection describes the release, then move it under the new version.Tag the release,
git tag -a vX.Y.Z, and push the tag. The wheels workflow builds and tests every wheel and builds solprop’s distributions.Publishing runs on the tag once the repository variable
PUBLISHistrue, through PyPI’s trusted publishing, one job and GitHub environment per project: solprop’s sdist and wheel from the environmentpypi, and solprop-native’s wheels frompypi-native. Each PyPI project names this repository, the workflowwheels.ymland its environment as its trusted publisher (solprop:pypi;solprop-native:pypi-native). PyPI ties a pending publisher to one project name per workflow and environment, hence the two.Install the published release in a fresh environment,
pip install "solprop[native]"on each platform, and check thatbackend="auto"resolves tonative.
The actions in the workflows are pinned by commit; when updating one, take the commit of its release tag and keep the version in the trailing comment.
The release recipe on this page was set up and qualified by SEP 29.