Skip to content

feat: add condition dependency and extras support for libsolv#1836

Merged
wolfv merged 16 commits intoconda:mainfrom
baszalmstra:claude/rattler-solve-support-011CUtZj1JA3GpgTrqyxrn4t
Nov 27, 2025
Merged

feat: add condition dependency and extras support for libsolv#1836
wolfv merged 16 commits intoconda:mainfrom
baszalmstra:claude/rattler-solve-support-011CUtZj1JA3GpgTrqyxrn4t

Conversation

@baszalmstra
Copy link
Collaborator

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.

@baszalmstra baszalmstra requested a review from wolfv November 11, 2025 14:51
@baszalmstra
Copy link
Collaborator Author

Id like to merge #1816 before continuing on this.

@baszalmstra baszalmstra changed the title feat: add condition dependency support for libsolv feat: add condition dependency and extras support for libsolv Nov 11, 2025
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.
@baszalmstra baszalmstra force-pushed the claude/rattler-solve-support-011CUtZj1JA3GpgTrqyxrn4t branch from ab2f2ae to 6eb2ed1 Compare November 20, 2025 15:51
- 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.
@baszalmstra baszalmstra force-pushed the claude/rattler-solve-support-011CUtZj1JA3GpgTrqyxrn4t branch from d4df322 to 4df4494 Compare November 26, 2025 15:15
@baszalmstra baszalmstra marked this pull request as ready for review November 26, 2025 15:48
Copy link
Contributor

@wolfv wolfv left a comment

Choose a reason for hiding this comment

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

Looks good to me! Nice new testing framework should make it easy to add more tests if we uncover bugs 🚀

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.
@wolfv wolfv merged commit 6bbc23b into conda:main Nov 27, 2025
17 checks passed
@github-actions github-actions bot mentioned this pull request Nov 27, 2025
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.

3 participants