Uh oh!
There was an error while loading. Please reload this page.
fix(build): register run-export-introduced source deps on the assembled record - #6519
Conversation
…ed record A run dependency injected purely via the run-exports of a source-built build/host dependency (e.g. python_abi exporting itself) was converted to a plain binary matchspec, so the consuming record's `sources` map never learned about it. The parent environment's source walk then never recursed into the package and the solve failed with "no candidates found" for a package that only exists as a source build. - extract_run_exports now maps exported names back to source records in the solved env and keeps them source-typed, carrying the pinned location. - resolve_source_record registers those locations (relativized to the record's manifest so consumer-side anchoring roundtrips) leniently: an explicit source spec for the same package wins. - Fix the inverted DuplicateSourceDependency condition: it errored on two identical locations and silently ignored genuinely different ones. - The satisfiability mirror now reads run-exports off source records too, matching the solve path. - SourceAnchor::relativize_location: inverse of resolve_location for workspace-root-relative pinned locations. Fixesprefix-dev#6482 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lucascolley
commented
Jul 3, 2026
not working yet: ~/sandbox/v3-panic via 🧚 v0.72.0
❯ bat pixi.toml -p[workspace]channels = ["https://prefix.dev/conda-forge"]platforms = ["osx-arm64", "linux-64"]preview = ["pixi-build"][dependencies]
# numpy.git = "https://github.com/lucascolley/numpy"
# numpy.subdirectory = "pixi-packages/asan"
# numpy.rev = "29d042b3b3e79b0a26fd2695ee469c66f4e98266"numpy.path = "/Users/lucascolley/ghq/github.com/rgommers/pixi-dev-scipystack/numpy/numpy/pixi-packages/asan"~/sandbox/v3-panic via 🧚 v0.72.0
❯ tixi lockError: × failed to solve requirements of environment 'default' for platform 'osx-arm64' ├─▶ × failed to solve the environment │ ╰─▶ Cannot solve the request because of: numpy * cannot be installed because there are no viable options: └─ numpy 2.6.0.dev0 would require └─ python_abi 3.16.* *_asan_cp316, for which no candidates were found.~/sandbox/v3-panic via 🧚 v0.72.0 took 2s
❯ tixi lockError: × failed to solve requirements of environment 'default' for platform 'osx-arm64' ├─▶ × failed to solve the environment │ ╰─▶ Cannot solve the request because of: numpy * cannot be installed because there are no viable options: └─ numpy 2.6.0.dev0 would require └─ python_abi 3.16.* *_tsan_cp316t, for which no candidates were found. |
Extends the previous commit to the reporter's newer setup, verified against the real reproduction (lucascolley/numpy@29d042b, lock now succeeds and is stable across re-runs): - extract_run_exports consults the exporting record's own `sources` map first (anchored to that record's manifest). This covers recipe sibling outputs — `python` weak-exports `python_abi`, which never appears in the consumer's host env, so the env-wide lookup alone cannot see it. - A binary-shaped run dep on a package that was built from source in the record's build/host envs (numpy's `python >=3.12` while the host env holds source-built python) now registers that source too — the package was linked against it, so the install-time env must build the same source rather than fetch a channel package that conflicts with the source python_abi's run constraint. - The passthrough backend emits run-exports naming model-declared source dependencies as source specs (mirroring real backends' local_source_packages) so the regression test can model the sibling case: package_b run-exports package_c, which is absent from package_a's host env. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
wolfv
commented
Jul 3, 2026
Pushed a second commit after verifying against the real reproduction (the newer manifest at
With both, Remaining, filed mentally as a separate issue: |
lucascolley
commented
Jul 3, 2026
then we should fix this too! |
lucascolley
commented
Jul 3, 2026
confirmed that it can still pick the wrong variant: |
…end recipe round-trip
A source dependency's selectors (version, build, build-number, extras,
flags, subdir, license, condition) were silently discarded twice on the
way through a generated recipe:
- source_package_spec_to_package_dependency put an empty MatchSpec next
to the encoded location URL,
- and the conversions back to a SourcePackageSpec
(from_source_matchspec_into_package_spec and convert_dependency's
source-url branch) rebuilt the spec from the URL alone. The
local-source-packages merge branch also skipped extras/flags/condition.
The visible symptom was `python = { git = ..., flags = ["asan"] }"
degrading to "any python variant from that source": the host env solve
picked a variant nondeterministically (asan on one run, tsan or
freethreading on the next), producing unstable and wrong locks
(prefix-dev#6482's flip-flopping error messages).
The selectors now ride on the SourceMatchSpec's inner MatchSpec — the
existing v3 detection (required_repodata_revision) already switches the
recipe parse to the V3 syntax surface when they are present, so
flags=[...] survives the YAML round-trip. All conversion sites use
exhaustive destructuring/construction so the compiler flags the next
field that gets added to SourcePackageSpec or MatchSpec instead of
silently widening the dependency.
Verified against the issue's reproduction with
PIXI_BUILD_BACKEND_OVERRIDE: both platforms now deterministically lock
python/python_abi 0_asan_cp316 (previously osx-arm64 locked the
freethreading variant and consecutive runs flipped variants).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>wolfv
commented
Jul 3, 2026
Third commit: tracked down the variant nondeterminism ( Root cause: source dependencies lose all matchspec selectors (version, build, flags, extras, ...) on the round-trip through a generated recipe in
So The selectors now travel on the Verified against the reproduction with |
lucascolley
commented
Jul 3, 2026
this fixes the linked issue for me now 👍 |
Hofer-Julian
left a comment
There was a problem hiding this comment.
It feels like we are working around our current architecture here rather than fixing it. However, I don't have a better solution and this fixes the problem, so let's bring it in!
Fixes#6482.
Problem
When a source package's build/host environment contains another source-built package that declares run-exports (e.g.
python_abiexportingpython_abi 3.16.* *_asan_cp316), the exported spec is injected into the consumer's run dependencies — butextract_run_exportsparsed run-export strings intoBinarySpecunconditionally. The assembled record'ssourcesmap therefore never learned about the dependency, the parent environment's source walk (walk_and_resolve) never recursed into it (resolved_records=1in the issue's logs), and the solve failed with:The host env of
numpysolved fine only becausepython_abiwas an explicit host dependency there.Changes
extract_run_exportsnow maps each exported package name against the solved environment's records; if the name resolved to a source record, the run-export stays a source spec carrying that record's pinned location (matchspec selectors are preserved, so the stringifieddependsin the lock file are unchanged). This also covers the same hole one layer down, where build-env strong exports feed the nested host solve.resolve_source_recordregisters run-export-introduced source deps into the record'ssourcesmap leniently — an explicit source spec for the same package wins — since the pinned location can spell the same source differently than a manifest spec (pinned commit vs. branch). Pinned path locations are workspace-root-relative whilesourcesentries are read relative to the record's manifest, so they are relativized first via a newSourceAnchor::relativize_location(the inverse ofresolve_location; git/URL locations anchor as identity and pass through).DuplicateSourceDependencycheck: it errored when the same package was registered twice from the same location and silently kept the first when the locations genuinely differed — the opposite of the error message's intent.collect_direct_run_exportsonly read run-exports off binary records while the solve path reads them off source records too; a locked mutable source with a source host dep that has run-exports would look perpetually unsatisfied. It now reads the package record for both shapes.Tests
test_run_export_on_source_host_dependency+tests/data/workspaces/run-exports-sourceworkspace modelling the issue:package_ahost-depends on sourcepackage_b, which run-exports itself. Without the fix it fails with exactly the issue's error shape; with the fixpackage_bis part of the solution as a source record and registered inpackage_a'ssources. The passthrough backend learned to serve an output's own run-exports from the instantiator'srun_exportsmap (keyed by the package's own name).SourceAnchor::relativize_location.Not addressed here: the issue also shows the error nondeterministically naming
*_cp316vs*_asan_cp316, which points at unstable variant selection in the host solve when several variants satisfy a bare spec — worth a separate look.🤖 Generated with Claude Code