Skip to content

Commit 6b79efd

Browse files
committed
Use difference
1 parent 0a8ca2d commit 6b79efd

File tree

4 files changed

+739
-139
lines changed

4 files changed

+739
-139
lines changed

crates/uv-resolver/src/resolver/mod.rs

Lines changed: 91 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use uv_distribution_types::{
3131
use uv_git::GitResolver;
3232
use uv_normalize::{ExtraName, GroupName, PackageName};
3333
use uv_pep440::{release_specifiers_to_ranges, Version, VersionSpecifiers, MIN_VERSION};
34-
use uv_pep508::{MarkerExpression, MarkerOperator, MarkerTree, MarkerValueString};
34+
use uv_pep508::MarkerTree;
3535
use uv_platform_tags::Tags;
3636
use uv_pypi_types::{
3737
ConflictItem, ConflictItemRef, Conflicts, Requirement, ResolutionMetadata, VerbatimParsedUrl,
@@ -1339,88 +1339,108 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
13391339
return Ok(None);
13401340
};
13411341

1342-
for platform in [
1343-
ResolverPlatform::Linux,
1344-
ResolverPlatform::Windows,
1345-
ResolverPlatform::MacOS,
1346-
] {
1347-
let marker = platform.marker();
1348-
1349-
// If the platform is relevant to the current environment...
1350-
if !env.included_by_marker(marker) {
1351-
continue;
1352-
}
1342+
debug!(
1343+
"Looking at local version: {}=={}",
1344+
name,
1345+
candidate.version()
1346+
);
13531347

1354-
// But isn't supported by the distribution...
1355-
if !dist.implied_markers().is_disjoint(marker) {
1356-
continue;
1357-
}
1348+
// If there's a non-local version...
1349+
let range = range.clone().intersection(&Range::singleton(
1350+
candidate.version().clone().without_local(),
1351+
));
13581352

1359-
// And there's a non-local version that _does_ support the platform.
1360-
let range = range.clone().intersection(&Range::singleton(
1361-
candidate.version().clone().without_local(),
1362-
));
1353+
let Some(base_candidate) = self.selector.select(
1354+
name,
1355+
&range,
1356+
version_maps,
1357+
preferences,
1358+
&self.installed_packages,
1359+
&self.exclusions,
1360+
index,
1361+
env,
1362+
) else {
1363+
return Ok(None);
1364+
};
1365+
let CandidateDist::Compatible(base_dist) = base_candidate.dist() else {
1366+
return Ok(None);
1367+
};
13631368

1364-
let Some(platform_candidate) = self.selector.select(
1365-
name,
1366-
&range,
1367-
version_maps,
1368-
preferences,
1369-
&self.installed_packages,
1370-
&self.exclusions,
1371-
index,
1372-
env,
1373-
) else {
1374-
continue;
1375-
};
1376-
let CandidateDist::Compatible(platform_dist) = platform_candidate.dist() else {
1377-
continue;
1378-
};
1369+
// ...and the non-local version has greater platform support...
1370+
let remainder = {
1371+
let mut remainder = base_dist.implied_markers();
1372+
remainder.and(dist.implied_markers().negate());
1373+
remainder
1374+
};
1375+
if remainder.is_false() {
1376+
return Ok(None);
1377+
}
13791378

1380-
if platform_dist.implied_markers().is_disjoint(marker) {
1381-
continue;
1382-
};
1379+
// If the remainder isn't relevant to the current environment, there's no need to fork.
1380+
// For example, if we're solving for `sys_platform == 'darwin'` but the remainder is
1381+
// `sys_platform == 'linux'`, we don't need to fork.
1382+
if !env.included_by_marker(remainder) {
1383+
return Ok(None);
1384+
}
13831385

1384-
// Then we need to fork.
1385-
let Some((platform_env, env)) = fork_version_by_marker(env, marker) else {
1386-
continue;
1386+
// Similarly, if the local distribution is incompatible with the current environment, then
1387+
// use the base distribution instead (but don't fork).
1388+
if !env.included_by_marker(dist.implied_markers()) {
1389+
let filename = match dist.for_installation() {
1390+
ResolvedDistRef::InstallableRegistrySourceDist { sdist, .. } => sdist
1391+
.filename()
1392+
.unwrap_or(Cow::Borrowed("unknown filename")),
1393+
ResolvedDistRef::InstallableRegistryBuiltDist { wheel, .. } => wheel
1394+
.filename()
1395+
.unwrap_or(Cow::Borrowed("unknown filename")),
1396+
ResolvedDistRef::Installed { .. } => Cow::Borrowed("installed"),
13871397
};
13881398

13891399
debug!(
1390-
"Forking platform for {}=={} ({})",
1400+
"Preferring non-local candidate: {}=={} [{}] ({})",
13911401
name,
1392-
candidate.version(),
1393-
[&platform_env, &env]
1394-
.iter()
1395-
.map(ToString::to_string)
1396-
.collect::<Vec<_>>()
1397-
.join(", ")
1402+
base_candidate.version(),
1403+
base_candidate.choice_kind(),
1404+
filename,
13981405
);
1399-
self.visit_candidate(candidate, dist, package, pins, request_sink)?;
1400-
self.visit_candidate(
1401-
&platform_candidate,
1402-
platform_dist,
1403-
package,
1404-
pins,
1405-
request_sink,
1406-
)?;
1407-
1408-
let forks = vec![
1409-
VersionFork {
1410-
env: env.clone(),
1411-
id,
1412-
version: Some(candidate.version().clone()),
1413-
},
1414-
VersionFork {
1415-
env: platform_env.clone(),
1416-
id,
1417-
version: Some(platform_candidate.version().clone()),
1418-
},
1419-
];
1420-
return Ok(Some(ResolverVersion::Forked(forks)));
1406+
self.visit_candidate(&base_candidate, base_dist, package, pins, request_sink)?;
1407+
1408+
return Ok(Some(ResolverVersion::Unforked(
1409+
base_candidate.version().clone(),
1410+
)));
14211411
}
14221412

1423-
Ok(None)
1413+
// Otherwise, we need to fork.
1414+
let Some((base_env, local_env)) = fork_version_by_marker(env, remainder) else {
1415+
return Ok(None);
1416+
};
1417+
1418+
debug!(
1419+
"Forking platform for {}=={} ({})",
1420+
name,
1421+
candidate.version(),
1422+
[&base_env, &local_env]
1423+
.iter()
1424+
.map(ToString::to_string)
1425+
.collect::<Vec<_>>()
1426+
.join(", ")
1427+
);
1428+
self.visit_candidate(candidate, dist, package, pins, request_sink)?;
1429+
self.visit_candidate(&base_candidate, base_dist, package, pins, request_sink)?;
1430+
1431+
let forks = vec![
1432+
VersionFork {
1433+
env: base_env.clone(),
1434+
id,
1435+
version: Some(base_candidate.version().clone()),
1436+
},
1437+
VersionFork {
1438+
env: local_env.clone(),
1439+
id,
1440+
version: Some(candidate.version().clone()),
1441+
},
1442+
];
1443+
Ok(Some(ResolverVersion::Forked(forks)))
14241444
}
14251445

14261446
/// Visit a selected candidate.
@@ -3536,41 +3556,3 @@ struct ConflictTracker {
35363556
/// Distilled from `culprit` for fast checking in the hot loop.
35373557
deprioritize: Vec<Id<PubGrubPackage>>,
35383558
}
3539-
3540-
/// A platform for which the resolver is solving.
3541-
#[derive(Debug, Clone, Copy)]
3542-
enum ResolverPlatform {
3543-
Linux,
3544-
Windows,
3545-
MacOS,
3546-
}
3547-
3548-
impl ResolverPlatform {
3549-
/// Return the platform's `sys.platform` value.
3550-
fn sys_platform(self) -> &'static str {
3551-
match self {
3552-
ResolverPlatform::Linux => "linux",
3553-
ResolverPlatform::Windows => "win32",
3554-
ResolverPlatform::MacOS => "darwin",
3555-
}
3556-
}
3557-
3558-
/// Return a [`MarkerTree`] for the platform.
3559-
fn marker(self) -> MarkerTree {
3560-
MarkerTree::expression(MarkerExpression::String {
3561-
key: MarkerValueString::SysPlatform,
3562-
operator: MarkerOperator::Equal,
3563-
value: self.sys_platform().to_string(),
3564-
})
3565-
}
3566-
}
3567-
3568-
impl Display for ResolverPlatform {
3569-
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3570-
match self {
3571-
ResolverPlatform::Linux => write!(f, "Linux"),
3572-
ResolverPlatform::Windows => write!(f, "Windows"),
3573-
ResolverPlatform::MacOS => write!(f, "macOS"),
3574-
}
3575-
}
3576-
}

0 commit comments

Comments
 (0)