feat: add condition dependency and extras support for libsolv#1836
Merged
wolfv merged 16 commits intoconda:mainfrom Nov 27, 2025
Merged
Conversation
Collaborator
Author
|
Id like to merge #1816 before continuing on this. |
extras support for libsolv
2 tasks
Implemented conditional dependency support for the libsolv backend,
achieving feature parity with the resolvo backend. Conditional
dependencies enable important conda features like extras and
platform-specific dependencies.
Implementation:
1. **Pool wrapper methods** (wrapper/pool.rs):
- Added rel_and(), rel_or(), rel_cond() to create libsolv relations
- These map to libsolv's REL_AND, REL_OR, REL_COND operators
2. **Condition parsing** (input.rs):
- Added parse_condition() to recursively convert MatchSpecCondition
to libsolv IDs
- Handles And, Or, and MatchSpec variants
- Modified add_repodata_records() to detect and process conditional
dependencies
- Extracts condition from match spec, creates REL_COND relation
3. **Build configuration** (build.rs):
- Enabled ENABLE_COMPLEX_DEPS flag for libsolv
- This flag is REQUIRED for libsolv to evaluate conditional
dependencies correctly
- Without it, REL_COND relations are created but not properly
evaluated
The key insight was discovering that libsolv requires
ENABLE_COMPLEX_DEPS to be enabled at compile-time for conditional
dependency evaluation to work correctly.
Added comprehensive test suite for conditional dependencies that runs
across all solver backends (libsolv_c and resolvo).
Test structure:
1. **Conditional test module** (conditional_tests.rs):
- test_solve_conditional_dependencies: Basic conditions with virtual
packages
- test_solve_complex_conditional_dependencies: AND, OR, and nested
conditions
2. **Test helpers** (helpers/):
- PackageBuilder: Fluent API for building test packages
- SolverCase: Helper for running solver tests with different backends
- Improved test readability and maintainability
Test coverage:
- Simple conditions: "numpy; if python>=3.9"
- Virtual package conditions: "scipy; if __unix"
- AND conditions: "pkg; if python>=3.8 and python<3.9.5"
- OR conditions: "pkg; if python>=3.9 or python<3.7"
- Nested conditions: "pkg; if (python>=3.9 or python<3.7) and __unix"
All tests pass for both libsolv_c and resolvo backends, confirming
feature parity for conditional dependency support.
…kage names Add extensive test coverage for conditional dependencies using an unusual package name pattern that won't conflict with real conda packages. The tests cover: - Multiple conditional dependencies with complex version constraints - Chained conditional dependencies (conditions depending on other conditions) - Negation-like behavior using OR conditions - Virtual package conditionals (__unix, __cuda) - Complex AND/OR combinations across different Python versions The tests are shared between both libsolv_c and resolvo backends through the solver_backend_tests! macro, ensuring consistent behavior across both implementations. Package name used: __test_conditional_never_use_pkg_xyz123 - Uses double underscores and specific pattern to avoid conflicts - Contains only valid conda package name characters - Clearly indicates this is for testing and should never be used
Add complete support for optional dependencies (extras) in the libsolv solver backend, mirroring the functionality already available in resolvo. ## Implementation **Synthetic Solvables:** - Create synthetic solvables with names using bracket notation: `package[extra]` - Brackets are safe to use as they're forbidden in real conda package names - Each unique (package, extra) pair gets exactly one synthetic solvable **Conditional Requirements:** - Convert `experimental_extra_depends` into conditional requirements - Dependencies are conditionally activated: `dep; if package[extra]` - When the synthetic `package[extra]` solvable is selected, the extra dependencies are pulled in through the conditional mechanism **User-facing Changes:** - Specs like `foo[extras=[test,dev]]` now work with libsolv - The base spec is stripped of extras syntax before passing to libsolv - Separate dependencies on synthetic `foo[test]` and `foo[dev]` are added - When selected, these synthetic solvables trigger the conditional deps **Technical Details:** - Synthetic solvables have version "0" to distinguish them from real packages - Use `intern_str` + direct Id conversion instead of `conda_matchspec` to avoid bracket parsing issues - Made `MatchSpecId` field `pub(crate)` to allow internal construction - Tests added to verify extras work for both libsolv and resolvo backends ## Testing Added shared tests in the `solver_backend_tests!` macro: - `test_extras_basic`: Verifies extras pull in correct dependencies - `test_extras_version_restriction`: Verifies extras influence version selection Both backends now pass the same extras tests, ensuring consistent behavior.
- Remove `solve_conditional_unusual_package` test from conditional_tests.rs - Create new `extras_tests.rs` module with dedicated extras tests - Add `extra_depends()` method to `PackageBuilder` for setting optional dependencies - Update feature gates to allow helpers module for both conditionals and extras - Fix `PackageBuilder` to generate unique filenames including version - Add comprehensive extras tests: - `solve_extras_basic`: Test basic extras functionality - `solve_extras_version_restriction`: Test that extras influence version selection - `solve_multiple_extras`: Test multiple extras on same package - `solve_extras_complex_constraints`: Test extras with complex dependency constraints All tests pass for both libsolv and resolvo backends.
Integrate changes from main where feature flags for experimental_extras and experimental_conditionals have been removed in favor of runtime configuration through ParseMatchSpecOptions. **Changes:** - Remove all `#[cfg(feature = "experimental_conditionals")]` and `#[cfg(feature = "experimental_extras")]` feature gates - Update MatchSpec parsing to use `ParseMatchSpecOptions::lenient()` with `.with_experimental_conditionals(true)` and `.with_experimental_extras(true)` - Update libsolv_c to work with new `PackageNameMatcher` type instead of `PackageName` (use `as_exact()` to get exact package names for extras) - Remove conditional compilation from test helpers and test modules - All extras and conditionals support is now always compiled and enabled at runtime through parse options **Testing:** - All extras tests pass for both libsolv_c and resolvo backends - All conditional tests pass for both backends - Tests updated to enable experimental features through parse options This aligns the codebase with the refactoring done in PR conda#1816 which made these features runtime-configurable instead of compile-time gated.
ab2f2ae to
6eb2ed1
Compare
11 tasks
- Add constraints, locked_packages, pinned_packages, exclude_newer, strategy methods to SolverCase - Add timestamp method to PackageBuilder - Create solver_case_tests.rs with 8 self-contained tests - Tests use manually constructed packages instead of JSON files
- Resolved merge conflicts with upstream/main - Added test_solve_with_unparsable_dependency test from upstream - Migrated test_conditional_root_requirement_* tests to use SolverCase helper for improved readability and consistency - Removed duplicate test_solve_conditional_dependencies (already covered by conditional_tests.rs module)
- Add backticks around code identifiers in doc comments - Add allow attribute for test variable names
- Convert test_solve_with_unparsable_dependency to use PackageBuilder - Create strategy_tests.rs module with SolverCase-based tests: - solve_lowest_version_strategy - solve_lowest_version_strategy_transitive - solve_lowest_version_direct_strategy - Fix PackageBuilder.build_string() to update filename - Remove #[allow(dead_code)] from SolverCase.strategy()
- Add archive_type field to PackageBuilder (defaults to .conda) - Use update_filename() helper for consistent filename handling - Archive type can be changed with archive_type() method
The LowestVersion and LowestVersionDirect strategies are only implemented for the resolvo backend, so move these tests from the shared macro to the resolvo module.
d4df322 to
4df4494
Compare
wolfv
approved these changes
Nov 27, 2025
Contributor
wolfv
left a comment
There was a problem hiding this comment.
Looks good to me! Nice new testing framework should make it easy to add more tests if we uncover bugs 🚀
...lve/tests/snapshots/backends__resolvo__solve_dummy_repo_extra_depends_recursive_feature.snap
Show resolved
Hide resolved
Use the already-parsed MatchSpec by cloning it with condition=None and calling to_string(), instead of manually splitting the original string on ';'. This is more robust and reuses existing parsing logic.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds conditional dependency support for libsolv alongside resolvo.
libsolv already has support for this, this PR just ties it together.
I also extended the test suite to run the same conditional-dependency tests for libsolv and resolvo.