diff --git a/Cargo.lock b/Cargo.lock index d8eccf1c6..3b5cc7f4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1826,6 +1826,7 @@ dependencies = [ "pep508_rs", "platform-host", "platform-tags", + "pubgrub", "puffin-client", "puffin-installer", "puffin-interpreter", diff --git a/crates/puffin-cli/Cargo.toml b/crates/puffin-cli/Cargo.toml index 627311b97..163083a23 100644 --- a/crates/puffin-cli/Cargo.toml +++ b/crates/puffin-cli/Cargo.toml @@ -14,6 +14,7 @@ pep440_rs = { path = "../pep440-rs" } pep508_rs = { path = "../pep508-rs" } platform-host = { path = "../platform-host" } platform-tags = { path = "../platform-tags" } +pubgrub = { path = "../../vendor/pubgrub" } puffin-client = { path = "../puffin-client" } puffin-installer = { path = "../puffin-installer" } puffin-interpreter = { path = "../puffin-interpreter" } diff --git a/crates/puffin-cli/src/commands/compile.rs b/crates/puffin-cli/src/commands/compile.rs index 90c5ac9a6..b9c458017 100644 --- a/crates/puffin-cli/src/commands/compile.rs +++ b/crates/puffin-cli/src/commands/compile.rs @@ -5,6 +5,7 @@ use std::path::Path; use anyhow::Result; use colored::Colorize; +use pubgrub::report::Reporter; use tracing::debug; use platform_host::Platform; @@ -62,7 +63,23 @@ pub(crate) async fn compile( // Resolve the dependencies. let resolver = puffin_resolver::Resolver::new(requirements, markers, &tags, &client); - let resolution = resolver.resolve().await?; + let resolution = match resolver.resolve().await { + Err(puffin_resolver::ResolveError::PubGrub(pubgrub::error::PubGrubError::NoSolution( + mut derivation_tree, + ))) => { + derivation_tree.collapse_no_versions(); + #[allow(clippy::print_stderr)] + { + eprintln!("{}: {}", "error".red().bold(), "no solution found".bold()); + eprintln!( + "{}", + pubgrub::report::DefaultStringReporter::report(&derivation_tree) + ); + } + return Ok(ExitStatus::Failure); + } + result => result, + }?; let s = if resolution.len() == 1 { "" } else { "s" }; writeln!( diff --git a/crates/puffin-cli/src/main.rs b/crates/puffin-cli/src/main.rs index 47c22e830..dd15bd542 100644 --- a/crates/puffin-cli/src/main.rs +++ b/crates/puffin-cli/src/main.rs @@ -1,11 +1,12 @@ use std::path::PathBuf; use std::process::ExitCode; -use crate::commands::ExitStatus; use clap::{Args, Parser, Subcommand}; use colored::Colorize; use directories::ProjectDirs; +use crate::commands::ExitStatus; + mod commands; mod logging; mod printer; diff --git a/crates/puffin-resolver/src/lib.rs b/crates/puffin-resolver/src/lib.rs index b03e74147..2bf55e9ed 100644 --- a/crates/puffin-resolver/src/lib.rs +++ b/crates/puffin-resolver/src/lib.rs @@ -1,3 +1,4 @@ +pub use error::ResolveError; pub use resolution::{PinnedPackage, Resolution}; pub use resolver::Resolver; pub use wheel_finder::{Reporter, WheelFinder}; diff --git a/crates/puffin-resolver/src/pubgrub/package.rs b/crates/puffin-resolver/src/pubgrub/package.rs index efe7c3af3..b928967d9 100644 --- a/crates/puffin-resolver/src/pubgrub/package.rs +++ b/crates/puffin-resolver/src/pubgrub/package.rs @@ -17,7 +17,7 @@ pub enum PubGrubPackage { impl std::fmt::Display for PubGrubPackage { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - PubGrubPackage::Root => write!(f, ""), + PubGrubPackage::Root => write!(f, "root"), PubGrubPackage::Package(name, None) => write!(f, "{name}"), PubGrubPackage::Package(name, Some(extra)) => { write!(f, "{name}[{extra}]") diff --git a/crates/puffin-resolver/src/resolver.rs b/crates/puffin-resolver/src/resolver.rs index c0dd8c706..fbb2f8e59 100644 --- a/crates/puffin-resolver/src/resolver.rs +++ b/crates/puffin-resolver/src/resolver.rs @@ -1,6 +1,7 @@ //! Given a set of requirements, find a set of compatible packages. use std::borrow::Borrow; +use std::collections::hash_map::Entry; use std::collections::{BTreeMap, HashMap, HashSet}; use std::str::FromStr; use std::sync::Arc; @@ -158,16 +159,13 @@ impl<'a> Resolver<'a> { // Fetch the list of candidates. let Some(potential_packages) = state.partial_solution.potential_packages() else { - let Some(selected_dependencies) = state.partial_solution.extract_solution() else { + let Some(selection) = state.partial_solution.extract_solution() else { return Err(PubGrubError::Failure( "How did we end up with no package to choose but no solution?".into(), ) .into()); }; - return Ok(PubGrubResolution { - selected_dependencies, - pins, - }); + return Ok(PubGrubResolution { selection, pins }); }; // Choose a package version. @@ -415,12 +413,23 @@ impl<'a> Resolver<'a> { for (package, version) in iter_requirements(self.requirements.iter(), None, self.markers) { - constraints.insert(package, version); + match constraints.entry(package) { + Entry::Occupied(mut entry) => { + entry.insert(entry.get().intersection(&version)); + } + Entry::Vacant(entry) => { + entry.insert(version); + } + } } Ok(Dependencies::Known(constraints)) } PubGrubPackage::Package(package_name, extra) => { - debug!("Fetching dependencies for {}[{:?}]", package_name, extra); + if let Some(extra) = extra.as_ref() { + debug!("Fetching dependencies for {}[{:?}]", package_name, extra); + } else { + debug!("Fetching dependencies for {}", package_name); + } // Wait for the metadata to be available. let versions = pins.get(package_name).unwrap(); @@ -429,7 +438,6 @@ impl<'a> Resolver<'a> { let metadata = entry.value(); let mut constraints = DependencyConstraints::default(); - for (package, version) in iter_requirements(metadata.requires_dist.iter(), extra.as_ref(), self.markers) { @@ -443,7 +451,14 @@ impl<'a> Resolver<'a> { } // Add it to the constraints. - constraints.insert(package, version); + match constraints.entry(package) { + Entry::Occupied(mut entry) => { + entry.insert(entry.get().intersection(&version)); + } + Entry::Vacant(entry) => { + entry.insert(version); + } + } } if let Some(extra) = extra { @@ -512,7 +527,7 @@ enum Dependencies { #[derive(Debug)] struct PubGrubResolution { /// The selected dependencies. - selected_dependencies: SelectedDependencies, + selection: SelectedDependencies, /// The selected file (source or built distribution) for each package. pins: HashMap>, } @@ -520,7 +535,7 @@ struct PubGrubResolution { impl From for Resolution { fn from(value: PubGrubResolution) -> Self { let mut packages = BTreeMap::new(); - for (package, version) in value.selected_dependencies { + for (package, version) in value.selection { let PubGrubPackage::Package(package_name, None) = package else { continue; };