2024-11-05 22:18:43 -05:00
use std ::collections ::{ BTreeMap , BTreeSet , Bound } ;
2023-11-08 06:57:26 -08:00
use std ::fmt ::Formatter ;
2024-04-18 21:04:59 -04:00
use std ::sync ::Arc ;
2023-11-08 06:57:26 -08:00
2024-09-18 16:16:22 +02:00
use indexmap ::IndexSet ;
2025-06-06 16:17:52 +02:00
use owo_colors ::OwoColorize ;
2024-11-05 22:18:43 -05:00
use pubgrub ::{
DefaultStringReporter , DerivationTree , Derived , External , Range , Ranges , Reporter , Term ,
} ;
2024-07-15 17:43:35 +02:00
use rustc_hash ::FxHashMap ;
2024-11-05 22:18:43 -05:00
use tracing ::trace ;
use uv_distribution_types ::{
2024-12-30 12:47:06 -05:00
DerivationChain , DistErrorKind , IndexCapabilities , IndexLocations , IndexUrl , RequestedDist ,
2024-11-05 22:18:43 -05:00
} ;
2025-02-06 14:41:33 -05:00
use uv_normalize ::{ ExtraName , InvalidNameError , PackageName } ;
2025-06-06 16:17:52 +02:00
use uv_pep440 ::{ LocalVersionSlice , LowerBound , Version , VersionSpecifier } ;
use uv_pep508 ::{ MarkerEnvironment , MarkerExpression , MarkerTree , MarkerValueVersion } ;
2025-01-13 20:03:11 -05:00
use uv_platform_tags ::Tags ;
2024-11-05 22:18:43 -05:00
use uv_static ::EnvVars ;
2023-10-09 23:29:09 -04:00
2023-12-28 22:44:35 -04:00
use crate ::candidate_selector ::CandidateSelector ;
2024-03-25 15:51:31 +01:00
use crate ::dependency_provider ::UvDependencyProvider ;
2025-01-13 20:03:11 -05:00
use crate ::fork_indexes ::ForkIndexes ;
2024-06-26 13:58:23 +02:00
use crate ::fork_urls ::ForkUrls ;
2024-12-17 11:05:15 -06:00
use crate ::prerelease ::AllowPrerelease ;
2024-10-29 20:15:18 +01:00
use crate ::pubgrub ::{ PubGrubPackage , PubGrubPackageInner , PubGrubReportFormatter } ;
2024-01-03 11:20:45 -04:00
use crate ::python_requirement ::PythonRequirement ;
2024-09-20 12:53:34 -04:00
use crate ::resolution ::ConflictingDistributionError ;
2024-10-11 15:23:38 -04:00
use crate ::resolver ::{
2024-12-10 17:46:53 +01:00
MetadataUnavailable , ResolverEnvironment , UnavailablePackage , UnavailableReason ,
2024-10-11 15:23:38 -04:00
} ;
2025-01-13 20:03:11 -05:00
use crate ::{ InMemoryIndex , Options } ;
2023-10-15 22:05:44 -04:00
2024-01-25 11:49:11 -08:00
#[ derive(Debug, thiserror::Error) ]
2023-10-09 23:29:09 -04:00
pub enum ResolveError {
#[ error(transparent) ]
2024-02-15 11:19:46 -06:00
Client ( #[ from ] uv_client ::Error ) ,
2023-10-09 23:29:09 -04:00
2024-11-13 15:49:08 -05:00
#[ error(transparent) ]
Distribution ( #[ from ] uv_distribution ::Error ) ,
2024-03-12 07:13:57 -07:00
#[ error( " The channel closed unexpectedly " ) ]
2024-02-02 18:18:24 +01:00
ChannelClosed ,
2023-10-15 22:05:44 -04:00
2023-10-16 13:26:46 -04:00
#[ error(transparent) ]
Join ( #[ from ] tokio ::task ::JoinError ) ,
2024-06-24 16:42:55 +02:00
#[ error( " Attempted to wait on an unregistered task: `{_0}` " ) ]
UnregisteredTask ( String ) ,
2024-01-22 09:00:21 -05:00
2024-10-11 15:23:38 -04:00
#[ error(
" Requirements contain conflicting URLs for package `{package_name}`{}: \n - {} " ,
if env.marker_environment().is_some() {
String::new()
} else {
format!( " in {env} " )
},
urls.join( " \n - " ),
) ]
ConflictingUrls {
2024-06-26 13:58:23 +02:00
package_name : PackageName ,
urls : Vec < String > ,
2024-10-11 15:23:38 -04:00
env : ResolverEnvironment ,
2024-06-26 13:58:23 +02:00
} ,
2023-11-05 09:09:58 -08:00
2024-10-11 15:23:38 -04:00
#[ error(
" Requirements contain conflicting indexes for package `{package_name}`{}: \n - {} " ,
if env.marker_environment().is_some() {
String::new()
} else {
format!( " in {env} " )
},
indexes.join( " \n - " ),
) ]
ConflictingIndexesForEnvironment {
2024-10-15 15:58:15 -07:00
package_name : PackageName ,
indexes : Vec < String > ,
2024-10-11 15:23:38 -04:00
env : ResolverEnvironment ,
2024-10-15 15:58:15 -07:00
} ,
2024-10-15 15:24:23 -07:00
#[ error( " Requirements contain conflicting indexes for package `{0}`: `{1}` vs. `{2}` " ) ]
ConflictingIndexes ( PackageName , String , String ) ,
2025-05-17 02:19:02 +02:00
#[ error(
" Package `{0}` attempted to resolve via URL: {1}. URL dependencies must be expressed as direct requirements or constraints. Consider adding `{0} @ {1}` to your dependencies or constraints file. "
) ]
2024-02-21 21:27:58 -05:00
DisallowedUrl ( PackageName , String ) ,
2023-11-20 23:08:54 +00:00
#[ error(transparent) ]
2024-10-01 20:15:32 -04:00
DistributionType ( #[ from ] uv_distribution_types ::Error ) ,
2023-11-20 23:08:54 +00:00
2024-08-08 21:39:47 -04:00
#[ error(transparent) ]
2024-10-01 20:15:32 -04:00
ParsedUrl ( #[ from ] uv_pypi_types ::ParsedUrlError ) ,
2024-08-08 21:39:47 -04:00
2024-12-06 02:54:14 +01:00
#[ error( " {0} `{1}` " ) ]
Dist (
DistErrorKind ,
2024-12-30 12:47:06 -05:00
Box < RequestedDist > ,
2024-11-14 15:48:26 -05:00
DerivationChain ,
#[ source ] Arc < uv_distribution ::Error > ,
) ,
2023-12-04 17:01:51 -05:00
2023-12-12 17:25:16 -06:00
#[ error(transparent) ]
2025-05-18 19:38:43 -04:00
NoSolution ( #[ from ] Box < NoSolutionError > ) ,
2023-12-12 17:25:16 -06:00
2024-03-16 07:24:50 -07:00
#[ error( " Attempted to construct an invalid version specifier " ) ]
2024-10-01 20:15:32 -04:00
InvalidVersion ( #[ from ] uv_pep440 ::VersionSpecifierBuildError ) ,
2024-03-16 07:24:50 -07:00
2025-05-17 02:19:02 +02:00
#[ error(
" In `--require-hashes` mode, all requirements must be pinned upfront with `==`, but found: `{0}` "
) ]
2024-04-10 15:09:03 -04:00
UnhashedPackage ( PackageName ) ,
2024-09-20 12:53:34 -04:00
#[ error( " found conflicting distribution in resolution: {0} " ) ]
ConflictingDistribution ( ConflictingDistributionError ) ,
2024-09-26 12:36:52 -04:00
#[ error( " Package `{0}` is unavailable " ) ]
PackageUnavailable ( PackageName ) ,
2025-02-06 14:41:33 -05:00
#[ error( " Invalid extra value in conflict marker: {reason}: {raw_extra} " ) ]
InvalidExtraInConflictMarker {
reason : String ,
raw_extra : ExtraName ,
} ,
#[ error( " Invalid {kind} value in conflict marker: {name_error} " ) ]
InvalidValueInConflictMarker {
kind : & 'static str ,
#[ source ]
name_error : InvalidNameError ,
} ,
2025-05-17 02:19:02 +02:00
#[ error(
" The index returned metadata for the wrong package: expected {request} for {expected}, got {request} for {actual} "
) ]
2025-04-22 17:36:27 +02:00
MismatchedPackageName {
request : & 'static str ,
expected : PackageName ,
actual : PackageName ,
} ,
2023-10-09 23:29:09 -04:00
}
2024-02-02 18:18:24 +01:00
impl < T > From < tokio ::sync ::mpsc ::error ::SendError < T > > for ResolveError {
/// Drop the value we want to send to not leak the private type we're sending.
/// The tokio error only says "channel closed", so we don't lose information.
fn from ( _value : tokio ::sync ::mpsc ::error ::SendError < T > ) -> Self {
Self ::ChannelClosed
2023-10-09 23:29:09 -04:00
}
}
2023-11-08 06:57:26 -08:00
2024-08-02 17:27:07 -04:00
pub ( crate ) type ErrorTree = DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ;
2024-07-15 17:43:35 +02:00
/// A wrapper around [`pubgrub::error::NoSolutionError`] that displays a resolution failure report.
pub struct NoSolutionError {
2024-07-31 14:54:11 +02:00
error : pubgrub ::NoSolutionError < UvDependencyProvider > ,
2025-01-13 20:03:11 -05:00
index : InMemoryIndex ,
2024-08-16 15:21:49 -05:00
available_versions : FxHashMap < PackageName , BTreeSet < Version > > ,
2024-09-23 15:02:19 -04:00
available_indexes : FxHashMap < PackageName , BTreeSet < IndexUrl > > ,
2024-07-15 17:43:35 +02:00
selector : CandidateSelector ,
python_requirement : PythonRequirement ,
index_locations : IndexLocations ,
2024-10-16 10:34:29 -07:00
index_capabilities : IndexCapabilities ,
2024-07-15 17:43:35 +02:00
unavailable_packages : FxHashMap < PackageName , UnavailablePackage > ,
2024-12-10 17:46:53 +01:00
incomplete_packages : FxHashMap < PackageName , BTreeMap < Version , MetadataUnavailable > > ,
2024-07-15 17:43:35 +02:00
fork_urls : ForkUrls ,
2025-01-13 20:03:11 -05:00
fork_indexes : ForkIndexes ,
2024-10-11 15:23:38 -04:00
env : ResolverEnvironment ,
2025-06-06 16:17:52 +02:00
current_environment : MarkerEnvironment ,
2025-01-13 20:03:11 -05:00
tags : Option < Tags > ,
2024-08-14 21:41:31 -05:00
workspace_members : BTreeSet < PackageName > ,
2024-10-14 21:04:30 -04:00
options : Options ,
2024-04-18 21:04:59 -04:00
}
2024-07-15 17:43:35 +02:00
impl NoSolutionError {
2024-08-09 23:21:56 -04:00
/// Create a new [`NoSolutionError`] from a [`pubgrub::NoSolutionError`].
2024-07-15 17:43:35 +02:00
pub ( crate ) fn new (
2024-07-31 14:54:11 +02:00
error : pubgrub ::NoSolutionError < UvDependencyProvider > ,
2025-01-13 20:03:11 -05:00
index : InMemoryIndex ,
2024-08-16 15:21:49 -05:00
available_versions : FxHashMap < PackageName , BTreeSet < Version > > ,
2024-09-23 15:02:19 -04:00
available_indexes : FxHashMap < PackageName , BTreeSet < IndexUrl > > ,
2024-07-15 17:43:35 +02:00
selector : CandidateSelector ,
python_requirement : PythonRequirement ,
index_locations : IndexLocations ,
2024-10-16 10:34:29 -07:00
index_capabilities : IndexCapabilities ,
2024-07-15 17:43:35 +02:00
unavailable_packages : FxHashMap < PackageName , UnavailablePackage > ,
2024-12-10 17:46:53 +01:00
incomplete_packages : FxHashMap < PackageName , BTreeMap < Version , MetadataUnavailable > > ,
2024-06-26 13:58:23 +02:00
fork_urls : ForkUrls ,
2025-01-13 20:03:11 -05:00
fork_indexes : ForkIndexes ,
2024-10-11 15:23:38 -04:00
env : ResolverEnvironment ,
2025-06-06 16:17:52 +02:00
current_environment : MarkerEnvironment ,
2025-01-13 20:03:11 -05:00
tags : Option < Tags > ,
2024-08-14 21:41:31 -05:00
workspace_members : BTreeSet < PackageName > ,
2024-10-14 21:04:30 -04:00
options : Options ,
2024-06-26 13:58:23 +02:00
) -> Self {
2024-07-15 17:43:35 +02:00
Self {
error ,
2025-01-13 20:03:11 -05:00
index ,
2024-07-15 17:43:35 +02:00
available_versions ,
2024-09-23 15:02:19 -04:00
available_indexes ,
2024-07-15 17:43:35 +02:00
selector ,
python_requirement ,
index_locations ,
2024-10-16 10:34:29 -07:00
index_capabilities ,
2024-07-15 17:43:35 +02:00
unavailable_packages ,
incomplete_packages ,
fork_urls ,
2025-01-13 20:03:11 -05:00
fork_indexes ,
2024-10-11 15:23:38 -04:00
env ,
2025-06-06 16:17:52 +02:00
current_environment ,
2025-01-13 20:03:11 -05:00
tags ,
2024-08-14 21:41:31 -05:00
workspace_members ,
2024-10-14 21:04:30 -04:00
options ,
2024-07-15 17:43:35 +02:00
}
}
/// Given a [`DerivationTree`], collapse any [`External::FromDependencyOf`] incompatibilities
/// wrap an [`PubGrubPackageInner::Extra`] package.
2024-08-02 17:27:07 -04:00
pub ( crate ) fn collapse_proxies ( derivation_tree : ErrorTree ) -> ErrorTree {
fn collapse ( derivation_tree : ErrorTree ) -> Option < ErrorTree > {
match derivation_tree {
DerivationTree ::Derived ( derived ) = > {
match ( & * derived . cause1 , & * derived . cause2 ) {
(
DerivationTree ::External ( External ::FromDependencyOf ( package1 , .. ) ) ,
DerivationTree ::External ( External ::FromDependencyOf ( package2 , .. ) ) ,
) if package1 . is_proxy ( ) & & package2 . is_proxy ( ) = > None ,
(
DerivationTree ::External ( External ::FromDependencyOf ( package , .. ) ) ,
cause ,
) if package . is_proxy ( ) = > collapse ( cause . clone ( ) ) ,
(
cause ,
DerivationTree ::External ( External ::FromDependencyOf ( package , .. ) ) ,
) if package . is_proxy ( ) = > collapse ( cause . clone ( ) ) ,
( cause1 , cause2 ) = > {
let cause1 = collapse ( cause1 . clone ( ) ) ;
let cause2 = collapse ( cause2 . clone ( ) ) ;
match ( cause1 , cause2 ) {
( Some ( cause1 ) , Some ( cause2 ) ) = > {
Some ( DerivationTree ::Derived ( Derived {
cause1 : Arc ::new ( cause1 ) ,
cause2 : Arc ::new ( cause2 ) ,
.. derived
} ) )
}
( Some ( cause ) , None ) | ( None , Some ( cause ) ) = > Some ( cause ) ,
_ = > None ,
}
}
2024-07-15 17:43:35 +02:00
}
2023-12-12 17:25:16 -06:00
}
2024-08-02 17:27:07 -04:00
DerivationTree ::External ( _ ) = > Some ( derivation_tree ) ,
2023-12-12 17:25:16 -06:00
}
}
2024-08-02 17:27:07 -04:00
collapse ( derivation_tree )
. expect ( " derivation tree should contain at least one external term " )
2023-12-12 17:25:16 -06:00
}
2024-08-09 23:21:56 -04:00
2024-11-05 22:18:43 -05:00
/// Simplifies the version ranges on any incompatibilities to remove the `[max]` sentinel.
///
/// The `[max]` sentinel is used to represent the maximum local version of a package, to
/// implement PEP 440 semantics for local version equality. For example, `1.0.0+foo` needs to
/// satisfy `==1.0.0`.
pub ( crate ) fn collapse_local_version_segments ( derivation_tree : ErrorTree ) -> ErrorTree {
fn strip ( derivation_tree : ErrorTree ) -> Option < ErrorTree > {
match derivation_tree {
DerivationTree ::External ( External ::NotRoot ( _ , _ ) ) = > Some ( derivation_tree ) ,
DerivationTree ::External ( External ::NoVersions ( package , versions ) ) = > {
2025-01-09 16:19:49 -05:00
if SentinelRange ::from ( & versions ) . is_complement ( ) {
2024-11-05 22:18:43 -05:00
return None ;
}
2024-11-15 15:06:24 -05:00
let versions = SentinelRange ::from ( & versions ) . strip ( ) ;
2024-11-05 22:18:43 -05:00
Some ( DerivationTree ::External ( External ::NoVersions (
package , versions ,
) ) )
}
DerivationTree ::External ( External ::FromDependencyOf (
package1 ,
versions1 ,
package2 ,
versions2 ,
) ) = > {
2024-11-15 15:06:24 -05:00
let versions1 = SentinelRange ::from ( & versions1 ) . strip ( ) ;
let versions2 = SentinelRange ::from ( & versions2 ) . strip ( ) ;
2024-11-05 22:18:43 -05:00
Some ( DerivationTree ::External ( External ::FromDependencyOf (
package1 , versions1 , package2 , versions2 ,
) ) )
}
DerivationTree ::External ( External ::Custom ( package , versions , reason ) ) = > {
2024-11-15 15:06:24 -05:00
let versions = SentinelRange ::from ( & versions ) . strip ( ) ;
2024-11-05 22:18:43 -05:00
Some ( DerivationTree ::External ( External ::Custom (
package , versions , reason ,
) ) )
}
DerivationTree ::Derived ( mut derived ) = > {
let cause1 = strip ( ( * derived . cause1 ) . clone ( ) ) ;
let cause2 = strip ( ( * derived . cause2 ) . clone ( ) ) ;
match ( cause1 , cause2 ) {
( Some ( cause1 ) , Some ( cause2 ) ) = > Some ( DerivationTree ::Derived ( Derived {
cause1 : Arc ::new ( cause1 ) ,
cause2 : Arc ::new ( cause2 ) ,
terms : std ::mem ::take ( & mut derived . terms )
. into_iter ( )
. map ( | ( pkg , term ) | {
let term = match term {
Term ::Positive ( versions ) = > {
2024-11-15 15:06:24 -05:00
Term ::Positive ( SentinelRange ::from ( & versions ) . strip ( ) )
2024-11-05 22:18:43 -05:00
}
Term ::Negative ( versions ) = > {
2024-11-15 15:06:24 -05:00
Term ::Negative ( SentinelRange ::from ( & versions ) . strip ( ) )
2024-11-05 22:18:43 -05:00
}
} ;
( pkg , term )
} )
. collect ( ) ,
shared_id : derived . shared_id ,
} ) ) ,
( Some ( cause ) , None ) | ( None , Some ( cause ) ) = > Some ( cause ) ,
_ = > None ,
}
}
}
}
strip ( derivation_tree ) . expect ( " derivation tree should contain at least one term " )
}
2025-01-08 12:38:17 -05:00
/// Given a [`DerivationTree`], identify the largest required Python version that is missing.
pub fn find_requires_python ( & self ) -> LowerBound {
fn find ( derivation_tree : & ErrorTree , minimum : & mut LowerBound ) {
match derivation_tree {
DerivationTree ::Derived ( derived ) = > {
find ( derived . cause1 . as_ref ( ) , minimum ) ;
find ( derived . cause2 . as_ref ( ) , minimum ) ;
}
DerivationTree ::External ( External ::FromDependencyOf ( .. , package , version ) ) = > {
if let PubGrubPackageInner ::Python ( _ ) = & * * package {
if let Some ( ( lower , .. ) ) = version . bounding_range ( ) {
let lower = LowerBound ::new ( lower . cloned ( ) ) ;
if lower > * minimum {
* minimum = lower ;
}
}
}
}
DerivationTree ::External ( _ ) = > { }
}
}
let mut minimum = LowerBound ::default ( ) ;
find ( & self . error , & mut minimum ) ;
minimum
}
2024-08-09 23:21:56 -04:00
/// Initialize a [`NoSolutionHeader`] for this error.
pub fn header ( & self ) -> NoSolutionHeader {
2024-10-11 15:23:38 -04:00
NoSolutionHeader ::new ( self . env . clone ( ) )
2024-08-09 23:21:56 -04:00
}
2025-06-06 16:17:52 +02:00
/// Hint at limiting the resolver environment if universal resolution failed for a target
/// that is not the current platform or not the current Python version.
fn hint_disjoint_targets ( & self , f : & mut Formatter ) -> std ::fmt ::Result {
// Only applicable to universal resolution.
let Some ( markers ) = self . env . fork_markers ( ) else {
return Ok ( ( ) ) ;
} ;
// TODO(konsti): This is a crude approximation to telling the user the difference
// between their Python version and the relevant Python version range from the marker.
let current_python_version = self . current_environment . python_version ( ) . version . clone ( ) ;
let current_python_marker = MarkerTree ::expression ( MarkerExpression ::Version {
key : MarkerValueVersion ::PythonVersion ,
specifier : VersionSpecifier ::equals_version ( current_python_version . clone ( ) ) ,
} ) ;
if markers . is_disjoint ( current_python_marker ) {
write! (
f ,
" \n \n {}{} While the active Python version is {}, \
the resolution failed for other Python versions supported by your \
project. Consider limiting your project's supported Python versions \
using `requires-python`. " ,
" hint " . bold ( ) . cyan ( ) ,
" : " . bold ( ) ,
current_python_version ,
) ? ;
} else if ! markers . evaluate ( & self . current_environment , & [ ] ) {
write! (
f ,
" \n \n {}{} The resolution failed for an environment that is not the current one, \
consider limiting the environments with `tool.uv.environments`. " ,
" hint " . bold ( ) . cyan ( ) ,
" : " . bold ( ) ,
) ? ;
}
Ok ( ( ) )
}
2023-12-12 17:25:16 -06:00
}
2025-01-13 20:03:11 -05:00
impl std ::fmt ::Debug for NoSolutionError {
fn fmt ( & self , f : & mut Formatter < '_ > ) -> std ::fmt ::Result {
// Include every field except `index`, which doesn't implement `Debug`.
let Self {
error ,
index : _ ,
available_versions ,
available_indexes ,
selector ,
python_requirement ,
index_locations ,
index_capabilities ,
unavailable_packages ,
incomplete_packages ,
fork_urls ,
fork_indexes ,
env ,
2025-06-06 16:17:52 +02:00
current_environment ,
2025-01-13 20:03:11 -05:00
tags ,
workspace_members ,
options ,
} = self ;
f . debug_struct ( " NoSolutionError " )
. field ( " error " , error )
. field ( " available_versions " , available_versions )
. field ( " available_indexes " , available_indexes )
. field ( " selector " , selector )
. field ( " python_requirement " , python_requirement )
. field ( " index_locations " , index_locations )
. field ( " index_capabilities " , index_capabilities )
. field ( " unavailable_packages " , unavailable_packages )
. field ( " incomplete_packages " , incomplete_packages )
. field ( " fork_urls " , fork_urls )
. field ( " fork_indexes " , fork_indexes )
. field ( " env " , env )
2025-06-06 16:17:52 +02:00
. field ( " current_environment " , current_environment )
2025-01-13 20:03:11 -05:00
. field ( " tags " , tags )
. field ( " workspace_members " , workspace_members )
. field ( " options " , options )
. finish ( )
}
}
2023-12-12 17:25:16 -06:00
impl std ::error ::Error for NoSolutionError { }
2023-11-08 06:57:26 -08:00
2023-12-12 17:25:16 -06:00
impl std ::fmt ::Display for NoSolutionError {
2023-11-08 06:57:26 -08:00
fn fmt ( & self , f : & mut Formatter < '_ > ) -> std ::fmt ::Result {
2023-12-28 22:44:35 -04:00
// Write the derivation report.
2023-12-12 17:25:16 -06:00
let formatter = PubGrubReportFormatter {
available_versions : & self . available_versions ,
2024-07-15 17:43:35 +02:00
python_requirement : & self . python_requirement ,
2024-08-14 21:41:31 -05:00
workspace_members : & self . workspace_members ,
2025-01-13 20:03:11 -05:00
tags : self . tags . as_ref ( ) ,
2023-12-12 17:25:16 -06:00
} ;
2024-08-14 21:41:31 -05:00
// Transform the error tree for reporting
let mut tree = self . error . clone ( ) ;
2024-08-19 18:05:01 -04:00
simplify_derivation_tree_markers ( & self . python_requirement , & mut tree ) ;
2024-10-14 21:48:13 +00:00
let should_display_tree = std ::env ::var_os ( EnvVars ::UV_INTERNAL__SHOW_DERIVATION_TREE )
. is_some ( )
2024-08-16 15:19:59 -05:00
| | tracing ::enabled! ( tracing ::Level ::TRACE ) ;
if should_display_tree {
display_tree ( & tree , " Resolver derivation tree before reduction " ) ;
}
2024-08-15 20:50:43 -05:00
collapse_no_versions_of_workspace_members ( & mut tree , & self . workspace_members ) ;
2024-08-14 21:41:31 -05:00
2024-08-14 22:08:56 -05:00
if self . workspace_members . len ( ) = = 1 {
let project = self . workspace_members . iter ( ) . next ( ) . unwrap ( ) ;
drop_root_dependency_on_project ( & mut tree , project ) ;
}
2024-08-16 15:19:59 -05:00
collapse_unavailable_versions ( & mut tree ) ;
2024-08-19 13:02:02 -05:00
collapse_redundant_depends_on_no_versions ( & mut tree ) ;
2024-08-16 15:19:59 -05:00
2024-12-17 11:05:15 -06:00
simplify_derivation_tree_ranges (
& mut tree ,
& self . available_versions ,
& self . selector ,
& self . env ,
) ;
// This needs to be applied _after_ simplification of the ranges
collapse_redundant_no_versions ( & mut tree ) ;
2024-12-16 19:42:05 -06:00
2024-12-17 08:27:07 -06:00
while collapse_redundant_no_versions_tree ( & mut tree ) {
// Continue collapsing until no more redundant nodes are found
}
2024-08-16 15:19:59 -05:00
if should_display_tree {
display_tree ( & tree , " Resolver derivation tree after reduction " ) ;
2024-08-16 09:25:26 -05:00
}
2024-08-14 21:41:31 -05:00
let report = DefaultStringReporter ::report_with_formatter ( & tree , & formatter ) ;
2023-12-28 22:44:35 -04:00
write! ( f , " {report} " ) ? ;
// Include any additional hints.
2024-09-18 16:16:22 +02:00
let mut additional_hints = IndexSet ::default ( ) ;
formatter . generate_hints (
2024-09-19 16:06:55 +02:00
& tree ,
2025-01-13 20:03:11 -05:00
& self . index ,
2024-02-06 11:04:14 -06:00
& self . selector ,
& self . index_locations ,
2024-10-16 10:34:29 -07:00
& self . index_capabilities ,
2024-09-23 15:02:19 -04:00
& self . available_indexes ,
2024-02-06 11:04:14 -06:00
& self . unavailable_packages ,
2024-04-09 23:12:10 -04:00
& self . incomplete_packages ,
2024-06-26 13:58:23 +02:00
& self . fork_urls ,
2025-01-13 20:03:11 -05:00
& self . fork_indexes ,
2024-10-11 15:23:38 -04:00
& self . env ,
2025-01-15 15:08:39 -05:00
self . tags . as_ref ( ) ,
2024-09-20 20:07:34 +02:00
& self . workspace_members ,
2024-12-17 14:08:22 -06:00
& self . options ,
2024-09-18 16:16:22 +02:00
& mut additional_hints ,
) ;
for hint in additional_hints {
2024-02-06 11:04:14 -06:00
write! ( f , " \n \n {hint} " ) ? ;
2023-12-28 22:44:35 -04:00
}
2025-06-06 16:17:52 +02:00
self . hint_disjoint_targets ( f ) ? ;
2023-12-28 22:44:35 -04:00
Ok ( ( ) )
2023-11-08 06:57:26 -08:00
}
}
2024-08-09 23:21:56 -04:00
2024-08-16 09:25:26 -05:00
#[ allow(clippy::print_stderr) ]
2024-08-16 15:19:59 -05:00
fn display_tree (
error : & DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
name : & str ,
) {
2024-08-16 09:25:26 -05:00
let mut lines = Vec ::new ( ) ;
display_tree_inner ( error , & mut lines , 0 ) ;
lines . reverse ( ) ;
2024-10-14 21:48:13 +00:00
if std ::env ::var_os ( EnvVars ::UV_INTERNAL__SHOW_DERIVATION_TREE ) . is_some ( ) {
2024-08-16 15:19:59 -05:00
eprintln! ( " {name} \n {} " , lines . join ( " \n " ) ) ;
2024-08-16 09:25:26 -05:00
} else {
2024-08-16 15:19:59 -05:00
trace! ( " {name} \n {} " , lines . join ( " \n " ) ) ;
2024-08-16 09:25:26 -05:00
}
}
fn display_tree_inner (
error : & DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
lines : & mut Vec < String > ,
depth : usize ,
) {
2024-12-13 22:53:33 -06:00
let prefix = " " . repeat ( depth ) . to_string ( ) ;
2024-08-16 09:25:26 -05:00
match error {
DerivationTree ::Derived ( derived ) = > {
display_tree_inner ( & derived . cause1 , lines , depth + 1 ) ;
display_tree_inner ( & derived . cause2 , lines , depth + 1 ) ;
2024-12-13 22:53:33 -06:00
for ( package , term ) in & derived . terms {
match term {
Term ::Positive ( versions ) = > {
lines . push ( format! ( " {prefix} term {package} {versions} " ) ) ;
2024-08-16 09:25:26 -05:00
}
2024-12-13 22:53:33 -06:00
Term ::Negative ( versions ) = > {
lines . push ( format! ( " {prefix} term not {package} {versions} " ) ) ;
2024-08-16 09:25:26 -05:00
}
}
}
}
2024-12-13 22:53:33 -06:00
DerivationTree ::External ( external ) = > match external {
External ::FromDependencyOf ( package , version , dependency , dependency_version ) = > {
lines . push ( format! (
" {prefix} {package} {version} depends on {dependency} {dependency_version} "
) ) ;
}
External ::Custom ( package , versions , reason ) = > match reason {
UnavailableReason ::Package ( _ ) = > {
lines . push ( format! ( " {prefix} {package} {reason} " ) ) ;
}
UnavailableReason ::Version ( _ ) = > {
lines . push ( format! ( " {prefix} {package} {versions} {reason} " ) ) ;
}
} ,
External ::NoVersions ( package , versions ) = > {
lines . push ( format! ( " {prefix} no versions of {package} {versions} " ) ) ;
}
External ::NotRoot ( package , versions ) = > {
lines . push ( format! ( " {prefix} not root {package} {versions} " ) ) ;
}
} ,
2024-08-16 09:25:26 -05:00
}
}
2024-12-16 15:31:35 -06:00
fn collapse_redundant_no_versions (
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
) {
match tree {
DerivationTree ::External ( _ ) = > { }
DerivationTree ::Derived ( derived ) = > {
match (
Arc ::make_mut ( & mut derived . cause1 ) ,
Arc ::make_mut ( & mut derived . cause2 ) ,
) {
// If we have a node for a package with no versions...
(
DerivationTree ::External ( External ::NoVersions ( package , versions ) ) ,
ref mut other ,
)
| (
ref mut other ,
DerivationTree ::External ( External ::NoVersions ( package , versions ) ) ,
) = > {
// First, always recursively visit the other side of the tree
collapse_redundant_no_versions ( other ) ;
2024-12-17 11:05:15 -06:00
// Retrieve the nearest terms, either alongside this node or from the parent.
let package_terms = if let DerivationTree ::Derived ( derived ) = other {
derived . terms . get ( package )
} else {
derived . terms . get ( package )
2024-12-16 15:31:35 -06:00
} ;
2024-12-17 11:05:15 -06:00
let Some ( Term ::Positive ( term ) ) = package_terms else {
2024-12-16 15:31:35 -06:00
return ;
} ;
2024-12-17 11:05:15 -06:00
2024-12-16 15:31:35 -06:00
let versions = versions . complement ( ) ;
// If we're disqualifying a single version, this is important to retain, e.g,
// for `only foo==1.0.0 is available`
if versions . as_singleton ( ) . is_some ( ) {
return ;
}
2024-12-17 11:05:15 -06:00
// If the range in the conclusion (terms) matches the range of no versions,
// then we'll drop this node. If the range is "all versions", then there's no
// also no need to enumerate the available versions.
if * term ! = Range ::full ( ) & & * term ! = versions {
2024-12-16 15:31:35 -06:00
return ;
}
* tree = other . clone ( ) ;
}
// If not, just recurse
_ = > {
collapse_redundant_no_versions ( Arc ::make_mut ( & mut derived . cause1 ) ) ;
collapse_redundant_no_versions ( Arc ::make_mut ( & mut derived . cause2 ) ) ;
}
}
}
}
}
2024-12-17 08:27:07 -06:00
/// Given a [`DerivationTree`], collapse any derived trees with two `NoVersions` nodes for the same
/// package. For example, if we have a tree like:
///
/// ```text
/// term Python>=3.7.9
/// no versions of Python>=3.7.9, <3.8
/// no versions of Python>=3.8
/// ```
///
/// We can simplify this to:
///
/// ```text
/// no versions of Python>=3.7.9
/// ```
///
/// This function returns a `bool` indicating if a change was made. This allows for repeated calls,
/// e.g., the following tree contains nested redundant trees:
///
/// ```text
/// term Python>=3.10
/// no versions of Python>=3.11, <3.12
/// term Python>=3.10, <3.11 | >=3.12
/// no versions of Python>=3.12
/// no versions of Python>=3.10, <3.11
/// ```
///
/// We can simplify this to:
///
/// ```text
/// no versions of Python>=3.10
/// ```
///
/// This appears to be common with the way the resolver currently models Python version
/// incompatibilities.
fn collapse_redundant_no_versions_tree (
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
) -> bool {
match tree {
DerivationTree ::External ( _ ) = > false ,
DerivationTree ::Derived ( derived ) = > {
match (
Arc ::make_mut ( & mut derived . cause1 ) ,
Arc ::make_mut ( & mut derived . cause2 ) ,
) {
// If we have a tree with two `NoVersions` nodes for the same package...
(
DerivationTree ::External ( External ::NoVersions ( package , versions ) ) ,
DerivationTree ::External ( External ::NoVersions ( other_package , other_versions ) ) ,
) if package = = other_package = > {
// Retrieve the terms from the parent.
let Some ( Term ::Positive ( term ) ) = derived . terms . get ( package ) else {
return false ;
} ;
// If they're both subsets of the term, then drop this node in favor of the term
if versions . subset_of ( term ) & & other_versions . subset_of ( term ) {
* tree = DerivationTree ::External ( External ::NoVersions (
package . clone ( ) ,
term . clone ( ) ,
) ) ;
return true ;
}
false
}
// If not, just recurse
_ = > {
collapse_redundant_no_versions_tree ( Arc ::make_mut ( & mut derived . cause1 ) )
| | collapse_redundant_no_versions_tree ( Arc ::make_mut ( & mut derived . cause2 ) )
}
}
}
}
}
2024-08-15 20:50:43 -05:00
/// Given a [`DerivationTree`], collapse any `NoVersion` incompatibilities for workspace members
2024-08-14 21:41:31 -05:00
/// to avoid saying things like "only <workspace-member>==0.1.0 is available".
2024-08-15 20:50:43 -05:00
fn collapse_no_versions_of_workspace_members (
2024-08-14 21:41:31 -05:00
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
2024-08-15 20:50:43 -05:00
workspace_members : & BTreeSet < PackageName > ,
2024-08-14 21:41:31 -05:00
) {
match tree {
DerivationTree ::External ( _ ) = > { }
DerivationTree ::Derived ( derived ) = > {
match (
Arc ::make_mut ( & mut derived . cause1 ) ,
Arc ::make_mut ( & mut derived . cause2 ) ,
) {
2024-08-15 20:50:43 -05:00
// If we have a node for a package with no versions...
( DerivationTree ::External ( External ::NoVersions ( package , _ ) ) , ref mut other )
| ( ref mut other , DerivationTree ::External ( External ::NoVersions ( package , _ ) ) ) = > {
// First, always recursively visit the other side of the tree
collapse_no_versions_of_workspace_members ( other , workspace_members ) ;
// Then, if the package is a workspace member...
let ( PubGrubPackageInner ::Package { name , .. }
| PubGrubPackageInner ::Extra { name , .. }
| PubGrubPackageInner ::Dev { name , .. } ) = & * * package
else {
return ;
} ;
if ! workspace_members . contains ( name ) {
return ;
}
// Replace this node with the other tree
2024-08-14 21:41:31 -05:00
* tree = other . clone ( ) ;
}
// If not, just recurse
_ = > {
2024-08-15 20:50:43 -05:00
collapse_no_versions_of_workspace_members (
Arc ::make_mut ( & mut derived . cause1 ) ,
workspace_members ,
) ;
collapse_no_versions_of_workspace_members (
Arc ::make_mut ( & mut derived . cause2 ) ,
workspace_members ,
) ;
2024-08-14 21:41:31 -05:00
}
}
}
}
}
2024-08-19 13:02:02 -05:00
/// Given a [`DerivationTree`], collapse `NoVersions` incompatibilities that are redundant children
/// of a dependency. For example, if we have a tree like:
2024-08-19 17:24:16 -04:00
///
/// ```text
/// A>=1,<2 depends on B
/// A has no versions >1,<2
/// C depends on A>=1,<2
/// ```
2024-08-19 13:02:02 -05:00
///
/// We can simplify this to `C depends A>=1 and A>=1 depends on B so C depends on B` without
/// explaining that there are no other versions of A. This is dependent on range of A in "A depends
/// on" being a subset of range of A in "depends on A". For example, in a tree like:
///
2024-08-19 17:24:16 -04:00
/// ```text
/// A>=1,<3 depends on B
/// A has no versions >2,<3
/// C depends on A>=2,<3
/// ```
2024-08-19 13:02:02 -05:00
///
/// We cannot say `C depends on A>=2 and A>=1 depends on B so C depends on B` because there is a
/// hole in the range — `A>=1,<3` is not a subset of `A>=2,<3`.
fn collapse_redundant_depends_on_no_versions (
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
) {
match tree {
DerivationTree ::External ( _ ) = > { }
DerivationTree ::Derived ( derived ) = > {
// If one node is a dependency incompatibility...
match (
Arc ::make_mut ( & mut derived . cause1 ) ,
Arc ::make_mut ( & mut derived . cause2 ) ,
) {
(
DerivationTree ::External ( External ::FromDependencyOf ( package , versions , _ , _ ) ) ,
ref mut other ,
)
| (
ref mut other ,
DerivationTree ::External ( External ::FromDependencyOf ( package , versions , _ , _ ) ) ,
) = > {
// Check if the other node is the relevant form of subtree...
collapse_redundant_depends_on_no_versions_inner ( other , package , versions ) ;
}
// If not, just recurse
_ = > {
collapse_redundant_depends_on_no_versions ( Arc ::make_mut ( & mut derived . cause1 ) ) ;
collapse_redundant_depends_on_no_versions ( Arc ::make_mut ( & mut derived . cause2 ) ) ;
}
}
}
}
}
/// Helper for [`collapse_redundant_depends_on_no_versions`].
fn collapse_redundant_depends_on_no_versions_inner (
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
package : & PubGrubPackage ,
versions : & Range < Version > ,
) {
match tree {
DerivationTree ::External ( _ ) = > { }
DerivationTree ::Derived ( derived ) = > {
// If we're a subtree with dependency and no versions incompatibilities...
match ( & * derived . cause1 , & * derived . cause2 ) {
(
DerivationTree ::External ( External ::NoVersions ( no_versions_package , _ ) ) ,
dependency_clause @ DerivationTree ::External ( External ::FromDependencyOf (
_ ,
_ ,
dependency_package ,
dependency_versions ,
) ) ,
)
| (
dependency_clause @ DerivationTree ::External ( External ::FromDependencyOf (
_ ,
_ ,
dependency_package ,
dependency_versions ,
) ) ,
DerivationTree ::External ( External ::NoVersions ( no_versions_package , _ ) ) ,
)
// And these incompatibilities (and the parent incompatibility) all are referring to
2024-08-19 17:24:16 -04:00
// the same package...
2024-08-19 13:02:02 -05:00
if no_versions_package = = dependency_package
& & package = = no_versions_package
// And parent dependency versions are a subset of the versions in this tree...
& & versions . subset_of ( dependency_versions ) = >
{
// Enumerating the available versions will be redundant and we can drop the no
// versions clause entirely in favor of the dependency clause.
* tree = dependency_clause . clone ( ) ;
// Note we are at a leaf of the tree so there's no further recursion to do
}
// If not, just recurse
_ = > {
collapse_redundant_depends_on_no_versions ( Arc ::make_mut ( & mut derived . cause1 ) ) ;
collapse_redundant_depends_on_no_versions ( Arc ::make_mut ( & mut derived . cause2 ) ) ;
}
}
}
}
}
2024-08-19 18:05:01 -04:00
/// Simplifies the markers on pubgrub packages in the given derivation tree
/// according to the given Python requirement.
///
/// For example, when there's a dependency like `foo ; python_version >=
/// '3.11'` and `requires-python = '>=3.11'`, this simplification will remove
/// the `python_version >= '3.11'` marker since it's implied to be true by
/// the `requires-python` setting. This simplifies error messages by reducing
/// noise.
fn simplify_derivation_tree_markers (
python_requirement : & PythonRequirement ,
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
) {
match tree {
2025-05-16 10:34:22 +02:00
DerivationTree ::External ( External ::NotRoot ( pkg , _ ) ) = > {
2024-08-19 18:05:01 -04:00
pkg . simplify_markers ( python_requirement ) ;
}
2025-05-16 10:34:22 +02:00
DerivationTree ::External ( External ::NoVersions ( pkg , _ ) ) = > {
2024-08-19 18:05:01 -04:00
pkg . simplify_markers ( python_requirement ) ;
}
2025-05-16 10:34:22 +02:00
DerivationTree ::External ( External ::FromDependencyOf ( pkg1 , _ , pkg2 , _ ) ) = > {
2024-08-19 18:05:01 -04:00
pkg1 . simplify_markers ( python_requirement ) ;
pkg2 . simplify_markers ( python_requirement ) ;
}
2025-05-16 10:34:22 +02:00
DerivationTree ::External ( External ::Custom ( pkg , _ , _ ) ) = > {
2024-08-19 18:05:01 -04:00
pkg . simplify_markers ( python_requirement ) ;
}
DerivationTree ::Derived ( derived ) = > {
derived . terms = std ::mem ::take ( & mut derived . terms )
. into_iter ( )
. map ( | ( mut pkg , term ) | {
pkg . simplify_markers ( python_requirement ) ;
( pkg , term )
} )
. collect ( ) ;
simplify_derivation_tree_markers (
python_requirement ,
Arc ::make_mut ( & mut derived . cause1 ) ,
) ;
simplify_derivation_tree_markers (
python_requirement ,
Arc ::make_mut ( & mut derived . cause2 ) ,
) ;
}
}
}
2024-08-16 15:19:59 -05:00
/// Given a [`DerivationTree`], collapse incompatibilities for versions of a package that are
/// unavailable for the same reason to avoid repeating the same message for every unavailable
/// version.
fn collapse_unavailable_versions (
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
) {
match tree {
DerivationTree ::External ( _ ) = > { }
DerivationTree ::Derived ( derived ) = > {
match (
Arc ::make_mut ( & mut derived . cause1 ) ,
Arc ::make_mut ( & mut derived . cause2 ) ,
) {
// If we have a node for unavailable package versions
(
DerivationTree ::External ( External ::Custom ( package , versions , reason ) ) ,
ref mut other ,
)
| (
ref mut other ,
DerivationTree ::External ( External ::Custom ( package , versions , reason ) ) ,
) = > {
// First, recursively collapse the other side of the tree
collapse_unavailable_versions ( other ) ;
// If it's not a derived tree, nothing to do.
let DerivationTree ::Derived ( Derived {
terms ,
shared_id ,
cause1 ,
cause2 ,
} ) = other
else {
return ;
} ;
// If the other tree has an unavailable package...
match ( & * * cause1 , & * * cause2 ) {
// Note the following cases are the same, but we need two matches to retain
// the ordering of the causes
(
_ ,
DerivationTree ::External ( External ::Custom (
other_package ,
other_versions ,
other_reason ,
) ) ,
) = > {
// And the package and reason are the same...
if package = = other_package & & reason = = other_reason {
// Collapse both into a new node, with a union of their ranges
2024-12-13 15:06:39 -06:00
let versions = other_versions . union ( versions ) ;
let mut terms = terms . clone ( ) ;
if let Some ( Term ::Positive ( range ) ) = terms . get_mut ( package ) {
* range = versions . clone ( ) ;
}
2024-08-16 15:19:59 -05:00
* tree = DerivationTree ::Derived ( Derived {
2024-12-13 15:06:39 -06:00
terms ,
2024-08-16 15:19:59 -05:00
shared_id : * shared_id ,
cause1 : cause1 . clone ( ) ,
cause2 : Arc ::new ( DerivationTree ::External ( External ::Custom (
package . clone ( ) ,
2024-12-13 15:06:39 -06:00
versions ,
2024-08-16 15:19:59 -05:00
reason . clone ( ) ,
) ) ) ,
} ) ;
}
}
(
DerivationTree ::External ( External ::Custom (
other_package ,
other_versions ,
other_reason ,
) ) ,
_ ,
) = > {
// And the package and reason are the same...
if package = = other_package & & reason = = other_reason {
// Collapse both into a new node, with a union of their ranges
2024-12-13 15:06:39 -06:00
let versions = other_versions . union ( versions ) ;
let mut terms = terms . clone ( ) ;
if let Some ( Term ::Positive ( range ) ) = terms . get_mut ( package ) {
* range = versions . clone ( ) ;
}
2024-08-16 15:19:59 -05:00
* tree = DerivationTree ::Derived ( Derived {
2024-12-13 15:06:39 -06:00
terms ,
2024-08-16 15:19:59 -05:00
shared_id : * shared_id ,
cause1 : Arc ::new ( DerivationTree ::External ( External ::Custom (
package . clone ( ) ,
2024-12-13 15:06:39 -06:00
versions ,
2024-08-16 15:19:59 -05:00
reason . clone ( ) ,
) ) ) ,
cause2 : cause2 . clone ( ) ,
} ) ;
}
}
_ = > { }
}
}
// If not, just recurse
_ = > {
collapse_unavailable_versions ( Arc ::make_mut ( & mut derived . cause1 ) ) ;
collapse_unavailable_versions ( Arc ::make_mut ( & mut derived . cause2 ) ) ;
}
}
}
}
}
2024-08-14 22:08:56 -05:00
/// Given a [`DerivationTree`], drop dependency incompatibilities from the root
/// to the project.
///
/// Intended to effectively change the root to a workspace member in single project
/// workspaces, avoiding a level of indirection like "And because your project
2024-12-07 14:03:16 -05:00
/// requires your project, we can conclude that your project's requirements are
2024-08-14 22:08:56 -05:00
/// unsatisfiable."
fn drop_root_dependency_on_project (
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
project : & PackageName ,
) {
match tree {
DerivationTree ::External ( _ ) = > { }
DerivationTree ::Derived ( derived ) = > {
match (
Arc ::make_mut ( & mut derived . cause1 ) ,
Arc ::make_mut ( & mut derived . cause2 ) ,
) {
// If one node is a dependency incompatibility...
(
DerivationTree ::External ( External ::FromDependencyOf ( package , _ , dependency , _ ) ) ,
ref mut other ,
)
| (
ref mut other ,
DerivationTree ::External ( External ::FromDependencyOf ( package , _ , dependency , _ ) ) ,
) = > {
// And the parent is the root package...
if ! matches! ( & * * package , PubGrubPackageInner ::Root ( _ ) ) {
return ;
}
// And the dependency is the project...
let PubGrubPackageInner ::Package { name , .. } = & * * dependency else {
return ;
} ;
if name ! = project {
return ;
}
// Recursively collapse the other side of the tree
drop_root_dependency_on_project ( other , project ) ;
// Then, replace this node with the other tree
* tree = other . clone ( ) ;
}
// If not, just recurse
_ = > {
drop_root_dependency_on_project ( Arc ::make_mut ( & mut derived . cause1 ) , project ) ;
drop_root_dependency_on_project ( Arc ::make_mut ( & mut derived . cause2 ) , project ) ;
}
}
}
}
}
2024-11-15 15:06:24 -05:00
/// A version range that may include local version sentinels (`+[max]`).
#[ derive(Debug) ]
pub struct SentinelRange < ' range > ( & ' range Range < Version > ) ;
impl < ' range > From < & ' range Range < Version > > for SentinelRange < ' range > {
fn from ( range : & ' range Range < Version > ) -> Self {
Self ( range )
}
}
2024-12-11 10:06:19 -06:00
impl SentinelRange < '_ > {
2025-01-09 16:19:49 -05:00
/// Returns `true` if the range appears to be, e.g., `>=1.0.0, <1.0.0+[max]`.
2024-11-15 15:06:24 -05:00
pub fn is_sentinel ( & self ) -> bool {
2025-01-09 16:19:49 -05:00
self . 0. iter ( ) . all ( | ( lower , upper ) | {
let ( Bound ::Included ( lower ) , Bound ::Excluded ( upper ) ) = ( lower , upper ) else {
return false ;
} ;
if ! lower . local ( ) . is_empty ( ) {
return false ;
}
if upper . local ( ) ! = LocalVersionSlice ::Max {
return false ;
}
* lower = = upper . clone ( ) . without_local ( )
} )
}
/// Returns `true` if the range appears to be, e.g., `>1.0.0, <1.0.0+[max]` (i.e., a sentinel
/// range with the non-local version removed).
pub fn is_complement ( & self ) -> bool {
2024-11-15 15:06:24 -05:00
self . 0. iter ( ) . all ( | ( lower , upper ) | {
let ( Bound ::Excluded ( lower ) , Bound ::Excluded ( upper ) ) = ( lower , upper ) else {
return false ;
} ;
2025-01-09 16:19:49 -05:00
if ! lower . local ( ) . is_empty ( ) {
2024-11-15 15:06:24 -05:00
return false ;
}
if upper . local ( ) ! = LocalVersionSlice ::Max {
return false ;
}
* lower = = upper . clone ( ) . without_local ( )
} )
}
/// Remove local versions sentinels (`+[max]`) from the version ranges.
pub fn strip ( & self ) -> Ranges < Version > {
2024-11-18 13:28:17 +01:00
self . 0
. iter ( )
. map ( | ( lower , upper ) | Self ::strip_sentinel ( lower . clone ( ) , upper . clone ( ) ) )
. collect ( )
2024-11-15 15:06:24 -05:00
}
/// Remove local versions sentinels (`+[max]`) from the interval.
fn strip_sentinel (
mut lower : Bound < Version > ,
mut upper : Bound < Version > ,
) -> ( Bound < Version > , Bound < Version > ) {
match ( & lower , & upper ) {
( Bound ::Unbounded , Bound ::Unbounded ) = > { }
( Bound ::Unbounded , Bound ::Included ( v ) ) = > {
// `<=1.0.0+[max]` is equivalent to `<=1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
upper = Bound ::Included ( v . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Unbounded , Bound ::Excluded ( v ) ) = > {
// `<1.0.0+[max]` is equivalent to `<1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
upper = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Included ( v ) , Bound ::Unbounded ) = > {
// `>=1.0.0+[max]` is equivalent to `>1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
lower = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Included ( v ) , Bound ::Included ( b ) ) = > {
// `>=1.0.0+[max]` is equivalent to `>1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
lower = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
// `<=1.0.0+[max]` is equivalent to `<=1.0.0`
if b . local ( ) = = LocalVersionSlice ::Max {
upper = Bound ::Included ( b . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Included ( v ) , Bound ::Excluded ( b ) ) = > {
// `>=1.0.0+[max]` is equivalent to `>1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
lower = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
// `<1.0.0+[max]` is equivalent to `<1.0.0`
if b . local ( ) = = LocalVersionSlice ::Max {
upper = Bound ::Included ( b . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Excluded ( v ) , Bound ::Unbounded ) = > {
// `>1.0.0+[max]` is equivalent to `>1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
lower = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Excluded ( v ) , Bound ::Included ( b ) ) = > {
// `>1.0.0+[max]` is equivalent to `>1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
lower = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
// `<=1.0.0+[max]` is equivalent to `<=1.0.0`
if b . local ( ) = = LocalVersionSlice ::Max {
upper = Bound ::Included ( b . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Excluded ( v ) , Bound ::Excluded ( b ) ) = > {
// `>1.0.0+[max]` is equivalent to `>1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
lower = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
// `<1.0.0+[max]` is equivalent to `<1.0.0`
if b . local ( ) = = LocalVersionSlice ::Max {
upper = Bound ::Excluded ( b . clone ( ) . without_local ( ) ) ;
}
}
}
( lower , upper )
}
}
2024-08-09 23:21:56 -04:00
#[ derive(Debug) ]
pub struct NoSolutionHeader {
2024-10-11 15:23:38 -04:00
/// The [`ResolverEnvironment`] that caused the failure.
env : ResolverEnvironment ,
2024-08-09 23:21:56 -04:00
/// The additional context for the resolution failure.
context : Option < & 'static str > ,
}
impl NoSolutionHeader {
2024-10-11 15:23:38 -04:00
/// Create a new [`NoSolutionHeader`] with the given [`ResolverEnvironment`].
pub fn new ( env : ResolverEnvironment ) -> Self {
Self { env , context : None }
2024-08-09 23:21:56 -04:00
}
/// Set the context for the resolution failure.
#[ must_use ]
pub fn with_context ( mut self , context : & 'static str ) -> Self {
self . context = Some ( context ) ;
self
}
}
impl std ::fmt ::Display for NoSolutionHeader {
fn fmt ( & self , f : & mut Formatter < '_ > ) -> std ::fmt ::Result {
2024-10-11 15:23:38 -04:00
match ( self . context , self . env . end_user_fork_display ( ) ) {
( None , None ) = > write! ( f , " No solution found when resolving dependencies: " ) ,
( Some ( context ) , None ) = > write! (
f ,
" No solution found when resolving {context} dependencies: "
) ,
( None , Some ( split ) ) = > write! (
f ,
" No solution found when resolving dependencies for {split}: "
) ,
( Some ( context ) , Some ( split ) ) = > write! (
f ,
" No solution found when resolving {context} dependencies for {split}: "
) ,
2024-08-09 23:21:56 -04:00
}
}
}
2024-12-16 19:42:05 -06:00
/// Given a [`DerivationTree`], simplify version ranges using the available versions for each
/// package.
fn simplify_derivation_tree_ranges (
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
available_versions : & FxHashMap < PackageName , BTreeSet < Version > > ,
2024-12-17 11:05:15 -06:00
candidate_selector : & CandidateSelector ,
resolver_environment : & ResolverEnvironment ,
2024-12-16 19:42:05 -06:00
) {
match tree {
DerivationTree ::External ( external ) = > match external {
External ::FromDependencyOf ( package1 , versions1 , package2 , versions2 ) = > {
2024-12-17 11:05:15 -06:00
if let Some ( simplified ) = simplify_range (
versions1 ,
package1 ,
available_versions ,
candidate_selector ,
resolver_environment ,
) {
2024-12-16 19:42:05 -06:00
* versions1 = simplified ;
}
2024-12-17 11:05:15 -06:00
if let Some ( simplified ) = simplify_range (
versions2 ,
package2 ,
available_versions ,
candidate_selector ,
resolver_environment ,
) {
2024-12-16 19:42:05 -06:00
* versions2 = simplified ;
}
}
External ::NoVersions ( package , versions ) = > {
2024-12-17 11:05:15 -06:00
if let Some ( simplified ) = simplify_range (
versions ,
package ,
available_versions ,
candidate_selector ,
resolver_environment ,
) {
2024-12-16 19:42:05 -06:00
* versions = simplified ;
}
}
External ::Custom ( package , versions , _ ) = > {
2024-12-17 11:05:15 -06:00
if let Some ( simplified ) = simplify_range (
versions ,
package ,
available_versions ,
candidate_selector ,
resolver_environment ,
) {
2024-12-16 19:42:05 -06:00
* versions = simplified ;
}
}
External ::NotRoot ( .. ) = > ( ) ,
} ,
DerivationTree ::Derived ( derived ) = > {
// Recursively simplify both sides of the tree
2024-12-17 11:05:15 -06:00
simplify_derivation_tree_ranges (
Arc ::make_mut ( & mut derived . cause1 ) ,
available_versions ,
candidate_selector ,
resolver_environment ,
) ;
simplify_derivation_tree_ranges (
Arc ::make_mut ( & mut derived . cause2 ) ,
available_versions ,
candidate_selector ,
resolver_environment ,
) ;
2024-12-16 19:42:05 -06:00
// Simplify the terms
derived . terms = std ::mem ::take ( & mut derived . terms )
. into_iter ( )
. map ( | ( pkg , term ) | {
let term = match term {
Term ::Positive ( versions ) = > Term ::Positive (
2024-12-17 11:05:15 -06:00
simplify_range (
& versions ,
& pkg ,
available_versions ,
candidate_selector ,
resolver_environment ,
)
. unwrap_or ( versions ) ,
2024-12-16 19:42:05 -06:00
) ,
Term ::Negative ( versions ) = > Term ::Negative (
2024-12-17 11:05:15 -06:00
simplify_range (
& versions ,
& pkg ,
available_versions ,
candidate_selector ,
resolver_environment ,
)
. unwrap_or ( versions ) ,
2024-12-16 19:42:05 -06:00
) ,
} ;
( pkg , term )
} )
. collect ( ) ;
}
}
}
/// Helper function to simplify a version range using available versions for a package.
///
/// If the range cannot be simplified, `None` is returned.
fn simplify_range (
range : & Range < Version > ,
package : & PubGrubPackage ,
available_versions : & FxHashMap < PackageName , BTreeSet < Version > > ,
2024-12-17 11:05:15 -06:00
candidate_selector : & CandidateSelector ,
resolver_environment : & ResolverEnvironment ,
2024-12-16 19:42:05 -06:00
) -> Option < Range < Version > > {
// If there's not a package name or available versions, we can't simplify anything
let name = package . name ( ) ? ;
let versions = available_versions . get ( name ) ? ;
// If this is a full range, there's nothing to simplify
if range = = & Range ::full ( ) {
return None ;
}
// If there's only one version available and it's in the range, return just that version
if let Some ( version ) = versions . iter ( ) . next ( ) {
if versions . len ( ) = = 1 & & range . contains ( version ) {
return Some ( Range ::singleton ( version . clone ( ) ) ) ;
}
}
2024-12-17 11:05:15 -06:00
// Check if pre-releases are allowed
let prereleases_not_allowed = candidate_selector
. prerelease_strategy ( )
. allows ( name , resolver_environment )
! = AllowPrerelease ::Yes ;
let any_prerelease = range . iter ( ) . any ( | ( start , end ) | {
let is_pre1 = match start {
Bound ::Included ( version ) = > version . any_prerelease ( ) ,
Bound ::Excluded ( version ) = > version . any_prerelease ( ) ,
Bound ::Unbounded = > false ,
} ;
let is_pre2 = match end {
Bound ::Included ( version ) = > version . any_prerelease ( ) ,
Bound ::Excluded ( version ) = > version . any_prerelease ( ) ,
Bound ::Unbounded = > false ,
} ;
is_pre1 | | is_pre2
} ) ;
2024-12-16 19:42:05 -06:00
// Simplify the range, as implemented in PubGrub
2024-12-17 11:05:15 -06:00
Some ( range . simplify ( versions . iter ( ) . filter ( | version | {
// If there are pre-releases in the range segments, we need to include pre-releases
if any_prerelease {
return true ;
}
// If pre-releases are not allowed, filter out pre-releases
if prereleases_not_allowed & & version . any_prerelease ( ) {
return false ;
}
// Otherwise, include the version
true
} ) ) )
2024-12-16 19:42:05 -06:00
}