Skip to content

build(python): 🎨 dynamically add mpi4py to build-system.requires - #314

Merged
Panadestein merged 1 commit into
mainfrom
chore-mpi4py-build-req-dynamically
Aug 31, 2026
Merged

build(python): 🎨 dynamically add mpi4py to build-system.requires#314
Panadestein merged 1 commit into
mainfrom
chore-mpi4py-build-req-dynamically

Conversation

@robertodr

@robertodr robertodr commented Aug 31, 2026

Copy link
Copy Markdown
Member

🤖 AI text below 🤖

Summary

mpi4py sat in build-system.requires, so every build installed it — including the non-MPI builds that are the default everywhere, and the cibuildwheel jobs that explicitly pass -Dmonoprop_ENABLE_MPI=OFF. It is only needed for MPI builds, where src/monoprop/bindings/CMakeLists.txt reads its headers to hand a communicator across the nanobind boundary. PEP 508 markers cannot express "only when MPI is on", so the dependency has to become dynamic.

It is now injected per-build through PEP 517's get_requires_for_build_wheel, via scikit-build-core's build.requires setting. monoprop_ENABLE_MPI=ON in the environment is the single switch: a [[tool.scikit-build.overrides]] block matches it and both adds mpi4py and sets the CMake option, so the two cannot drift apart. Enabling MPI therefore moves from a config-settings flag to an environment variable in the justfile, CI, README and docs.

The config-settings route still works but cannot chain: an override's if. clause matches environment variables only, so --config-settings=cmake.define.monoprop_ENABLE_MPI=ON must carry --config-settings=build.requires=mpi4py>=4.1.0 alongside it. Getting that wrong used to surface as an opaque Could NOT find mpi4py (missing: PY_mpi4py); the bindings CMakeLists now fails with a message naming both routes.

Changes

  • pyproject.toml: drop mpi4py from build-system.requires; add a [[tool.scikit-build.overrides]] block keyed on if.env.monoprop_ENABLE_MPI setting build.requires and cmake.define.monoprop_ENABLE_MPI. It must stay last in the [tool.scikit-build] section — an array-of-tables swallows any bare key that follows it.
  • src/monoprop/bindings/CMakeLists.txt: find_python_module(mpi4py QUIET) plus an explicit FATAL_ERROR spelling out both ways to enable MPI.
  • justfile (test-mpi, bench-build-mpi) and .github/workflows/qa-analysis.yml (sonarqube-analysis, clang-tidy): switch to the environment switch. MPI moves out of SKBUILD_CMAKE_DEFINE, which keeps its other defines.
  • docs/content/docs/building.mdx: documents the environment route, the uv build-cache caveat, and a "Driving it with config-settings instead" subsection covering the --config-settings-package form and its repeated-pin cost.
  • docs/content/docs/testing.mdx, README.md, cpp/tests/README.md, AGENTS.md: updated to the environment switch.

Checklist

  • Tests added or updated to cover the changes
  • Documentation updated (docstrings, docs/, CONTRIBUTING.md) if needed
  • CHANGELOG / release notes updated if applicable

AI/LLM disclosure

  • I did not use LLM tooling, or used it only privately for ideation
  • I used the following tool to help write this PR description: ClaudeCode: claude-opus-5
  • I used the following tool to generate or modify code: ClaudeCode: claude-opus-5

Important

By opening this PR I confirm that I have read CONTRIBUTING.md and I agree to the terms of the Contributor License Agreement.

Warning

If you're contributing on behalf of your employer, contact cla@algorithmiq.fi to arrange a Corporate CLA.

@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file python cpp ci labels Aug 31, 2026
@github-actions

Copy link
Copy Markdown

Docs preview: https://pr-314.monoprop-docs.pages.dev

@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.70%. Comparing base (999caf1) to head (8cb62bc).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #314   +/-   ##
=======================================
  Coverage   97.70%   97.70%           
=======================================
  Files          14       14           
  Lines         742      742           
  Branches       98       98           
=======================================
  Hits          725      725           
  Misses         12       12           
  Partials        5        5           
Flag Coverage Δ
cpp 97.70% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

@sonarqubecloud

Copy link
Copy Markdown

@Panadestein Panadestein left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@Panadestein
Panadestein merged commit f12b3f7 into main Aug 31, 2026
33 checks passed
@Panadestein
Panadestein deleted the chore-mpi4py-build-req-dynamically branch August 31, 2026 11:56
robertodr added a commit that referenced this pull request Sep 1, 2026
🤖 _AI text below_ 🤖

Adds a Nix flake so Nix/NixOS users can build and hack on monoprop
without installing the C++ toolchain, hwloc, Boost or MPI by hand.

Stacked on #314 — this branch is rebased onto
`chore-mpi4py-build-req-dynamically` and should merge after it.

## Outputs

| Command | What it gives you |
| --- | --- |
| `nix develop` | dev shell: GCC 15, CMake, Ninja, Boost, hwloc, Open
MPI, `uv`, `just`, Node.js, clang-tools, gdb, lcov, doxygen |
| `nix build .#monoprop` | the Python package, without MPI |
| `nix build .#monoprop-mpi` | the same package with
`monoprop_ENABLE_MPI=ON` |
| `nix run` | a Python interpreter with `monoprop` importable |

Inside `nix develop`, the existing `uv sync` and `just` workflows apply
unchanged. The shell sets `UV_PYTHON_PREFERENCE=only-system` (uv's
managed interpreters expect a loader NixOS does not provide, and
`[tool.uv]` pins `only-managed`) and `LD_LIBRARY_PATH` so manylinux
wheels resolve `libstdc++`.

## Blocker: the packages do not currently build

`nix build .#monoprop` and `.#monoprop-mpi` both fail in
`pypaBuildPhase` with `Unmet dependencies`: #243 pinned
`nanobind==3.0.0` and added `nanobind-backend>=1.0.0` to `[build-system]
requires`, but nixpkgs ships nanobind 2.13.0 and has no
`nanobind-backend` at all. This predates the rebase — it arrived in this
branch through its last `Merge branch 'main'` and the validation below
was never re-run against it. The flake needs the same kind of override
it already applies to scikit-build-core before this can merge. The dev
shell is unaffected.

## Deviations from the `uv` build

All in `nix/monoprop.nix` and documented in the building guide:

- C++ unit tests are off — they resolve msgpack-cxx through a CPM git
fetch that the build sandbox denies. Build them from the dev shell
instead.
- `monoprop_ENABLE_ARCH_FLAGS` is off, since a store path may be
substituted onto a machine other than the one that built it. `.override
{ enableArchFlags = true; }` restores `-march=native`.
- The version is pinned, because setuptools-scm cannot read git metadata
inside the sandbox. **It needs bumping alongside the release tag.**
- MPI is enabled by setting the CMake option directly rather than
through #314's `monoprop_ENABLE_MPI` environment switch, because that
switch also appends mpi4py to `build.requires` — a PyPI resolution the
sandbox denies. The store copy of mpi4py is supplied as a build input of
`monoprop-mpi` only.

## Notes for reviewers

- nixpkgs ships scikit-build-core 1.0.2 while `[build-system] requires`
asks for `>=1.0.3`, so the flake overrides it with the 1.0.3 PyPI sdist.
That override should be dropped once nixpkgs catches up.
- `tools/generate-dispatch.py` reads `.github/license-header.txt` during
`cmake --install`, but `.github` is in `sdist.exclude`. The flake works
around it by including that one file in the source set; an sdist-based
install would presumably hit the same failure.

## Validation

On `x86_64-linux`, **before #243 landed**: `nix build .#monoprop` and
`.#monoprop-mpi` both compiled and passed the import check, `nix flake
check` passed, `nix run` evaluated a `MajoranaOperator`, and `uv sync`
inside `nix develop` built the bindings against the Nix toolchain. All
of that needs re-running once the nanobind override is in. Darwin and
`aarch64-linux` are exposed via `flake-utils.lib.eachDefaultSystem` but
untested.

---------

Signed-off-by: Roberto Di Remigio Eikås <robertodr@users.noreply.github.com>
Co-authored-by: Roberto Di Remigio Eikås <robertodr@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci cpp dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants