Skip to content

fix(stack): refuse two runtime versions in one toolchain - #12

Merged
HaoZeke merged 4 commits into
mainfrom
fix/stack-order-one-version
Nov 3, 2026
Merged

HaoZeke merged 4 commits into
mainfrom
fix/stack-order-one-version

Conversation

@HaoZeke

@HaoZeke HaoZeke commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Reproducer

The fixture fixtures/stack_order_version_conflict has LAMMPS 2025.07 pinning Eigen 3.4.1 and SciPy-bundle 2026.06 pinning Eigen 5.0.0, both at foss/2026.1.

eb-stack stack order \
  --easyconfigs fixtures/stack_order_version_conflict/easyconfigs \
  --root LAMMPS --root SciPy-bundle \
  --out /tmp/build-order.txt

Before, the command exits 0, writes the order file, and prints:

multiple builds of Eigen: 3.4.1-foss-2026.1, 5.0.0-foss-2026.1
order=/tmp/build-order.txt builds=4

After, the command exits 1, writes no order file, and prints:

error: Eigen at foss-2026.1 is required at more than one version, and no version satisfies every runtime requirement. stack order does not choose one: stack solve rejects this closure. LAMMPS-2025.07-foss-2026.1 requires Eigen ==3.4.1 (resolved 3.4.1); SciPy-bundle-2026.06-foss-2026.1 requires Eigen ==5.0.0 (resolved 5.0.0)

The stack solve command on the same roots already rejects this closure, and this change leaves it as it was:

eb-stack stack solve \
  --easyconfigs fixtures/stack_order_version_conflict/easyconfigs \
  --toolchain foss/2026.1 \
  --root LAMMPS --root SciPy-bundle \
  --lock-out /tmp/solve-conflict.lock.json
error: unsatisfiable stack: unsatisfiable stack (resolvo SAT): The following packages are incompatible
├─ SciPy-bundle {2026.06} can be installed with any of the following options:
│  └─ SciPy-bundle 2026.06 would require
│     └─ Eigen {5.0.0}, which can be installed with any of the following options:
│        └─ Eigen 5.0.0
└─ LAMMPS {2025.07} cannot be installed because there are no viable options:
   └─ LAMMPS 2025.07 would require
      └─ Eigen {3.4.1}, which cannot be installed because there are no viable options:
         └─ Eigen 3.4.1, which conflicts with the versions reported above.

What and why

The old stack order wrote both Eigen builds into the order.
That order describes a stack stack solve can't install.
A runtime dependency keeps one version per name at one toolchain, so no order exists for these two exact pins.

The build_order function now checks the selected closure before it sorts.
It groups modules with InstallSlots, the package key classification stack solve already used inline.
A runtime slot with two selected modules fails when no candidate satisfies every runtime requirement that resolved into it.
The error names each requester, its requirement, and the version it resolved.

That error comes without a repin.
Picking which exact pin to drop would describe a build the easyconfig doesn't ask for, and that choice belongs to the solver.
When one requirement is unpinned and the other pins an admitted version, the order still lists both resolutions, since stack solve can still pick the shared version.

Two builds of one name still stay side by side where EasyBuild installs them that way:

  • another toolchain, such as Perl at GCCcore and at system;
  • a system bootstrap pair, such as binutils 2.40 and 2.42;
  • a build-only tool, such as CMake 3.31.11 and 4.2.1.

Docs record the refusal and the CMake and binutils builds that stay.
The edits touch the CHANGELOG, the solve-lock how-to, the CLI and formats references, and llms-full.txt.
The test workflow now runs --test stack_order_version.

Tests

Check Result
cargo fmt --all --check pass
cargo clippy --locked --all-targets -- -D warnings pass
cargo test --locked --all-targets pass, 787 library tests
python3 scripts/gen-llms-txt.py --check pass
sphinx-build -W --keep-going after the Org export and scripts/gen-rust-api.sh pass

New tests:

Test Covers
two_runtime_versions_of_one_dependency_are_refused error names both requesters and both versions
two_build_only_versions_of_one_tool_both_appear both CMake builds stay
a_system_bootstrap_pair_still_orders_both_versions both binutils builds stay
a_shared_version_is_not_repinned an unpinned requirement keeps both Eigen resolutions
tests/stack_order_version.rs the binary on the fixture exits 1 and writes no order file

Repro

git checkout fix/stack-order-one-version
cargo test --locked --lib two_runtime_versions_of_one_dependency_are_refused
cargo test --locked --test stack_order_version

Note

Medium Risk
Changes behavior of stack order from success to failure for previously accepted impossible runtime closures; core build-order logic but aligned with existing solver semantics.

Overview
stack order now fails (exit 1, no order file) when two recipes at the same toolchain pin incompatible runtime versions of one package—matching closures stack solve already rejects. The error lists each requester, its requirement, and the resolved version (e.g. LAMMPS → Eigen 3.4.1 vs SciPy-bundle → Eigen 5.0.0 at foss-2026.1).

Before ordering, build_order runs refuse_disjoint_runtime_versions, grouping modules with shared InstallSlots package keys from resolvo_provider (same co-install rules as the solver). It does not repin recipes; side-by-side duplicates (multi-toolchain, system bootstrap pairs, build-only tools like two CMake versions) still order successfully.

Adds fixture fixtures/stack_order_version_conflict, unit tests, integration test stack_order_version, CI wiring, and doc/changelog updates.

Reviewed by Cursor Bugbot for commit f65ea77. Bugbot is set up for automated code reviews on this repo. Configure here.

A foss-2026.1 fixture pins Eigen 3.4.1 from LAMMPS and Eigen 5.0.0 from SciPy-bundle.
The `stack order` command still writes both builds, which `stack solve` rejects.
The `stack order` command kept Eigen 3.4.1 for LAMMPS and Eigen 5.0.0 for SciPy-bundle at foss-2026.1.
The `stack solve` command rejects that closure because a runtime dependency has one version per toolchain.
The order now fails and names both requesters.
Side-by-side builds stay: another toolchain, a system bootstrap pair, or a build-only tool.
A recipe is not repinned.
The `stack order` command refuses two runtime versions of one name at one toolchain and names both requesters.
The docs say that side-by-side builds stay.
They also say that a recipe is not repinned.
Both points are in the how-to, the CLI reference, the format reference, and the changelog.
The default test workflow names every integration test.
The new `stack order` fixture was not in that list.
@HaoZeke
HaoZeke merged commit 8050836 into main Nov 3, 2026
19 checks passed
@HaoZeke
HaoZeke deleted the fix/stack-order-one-version branch November 4, 2026 09:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant