Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

suggest unlocking locked pkgs that cause dep resolution failures #3970

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 36 additions & 4 deletions compiler-cli/src/dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,18 +691,50 @@ fn resolve_versions<Telem: Telemetry>(
}

// Convert provided packages into hex packages for pub-grub resolve
let provided_hex_packages = provided_packages
let provided_hex_packages: HashMap<EcoString, hexpm::Package> = provided_packages
.iter()
.map(|(name, package)| (name.clone(), package.to_hex_package(name)))
.collect();

let resolved = dependency::resolve_versions(
let root_requirements_clone = root_requirements.clone();
let resolved: HashMap<String, Version> = match dependency::resolve_versions(
PackageFetcher::boxed(runtime.clone()),
provided_hex_packages,
provided_hex_packages.clone(),
config.name.clone(),
root_requirements.into_iter(),
&locked,
)?;
) {
Ok(it) => it,
Err(
ref e @ Error::DependencyResolutionFailedWithLocked {
error: _,
ref locked_conflicts,
},
) => {
// TODO: provide more error context
let should_try_unlock = cli::confirm(
"\nSome of these dependencies are locked to specific versions. It may
be possible to find a solution if they are unlocked, would you like
to unlock and try again? [y/n]",
)?;

if should_try_unlock {
unlock_packages(&mut locked, &locked_conflicts, manifest)?;

dependency::resolve_versions(
PackageFetcher::boxed(runtime.clone()),
provided_hex_packages,
config.name.clone(),
root_requirements_clone.into_iter(),
&locked,
)?
} else {
return Err(e.clone());
}
}

Err(err) => return Err(err),
};

// Convert the hex packages and local packages into manifest packages
let manifest_packages = runtime.block_on(future::try_join_all(
Expand Down
7 changes: 4 additions & 3 deletions compiler-core/src/dependency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ where
{
tracing::info!("resolving_versions");
let root_version = Version::new(0, 0, 0);
let requirements =
root_dependencies(dependencies, locked).map_err(Error::dependency_resolution_failed)?;

let requirements = root_dependencies(dependencies, locked)
.map_err(|err| Error::dependency_resolution_failed(err, locked))?;

// Creating a map of all the required packages that have exact versions specified
let exact_deps = &requirements
Expand All @@ -55,7 +56,7 @@ where
root_name.as_str().into(),
root_version,
)
.map_err(Error::dependency_resolution_failed)?
.map_err(|err| Error::dependency_resolution_failed(err, locked))?
.into_iter()
.filter(|(name, _)| name.as_str() != root_name.as_str())
.collect();
Expand Down
111 changes: 88 additions & 23 deletions compiler-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use pubgrub::package::Package;
use pubgrub::report::DerivationTree;
use pubgrub::version::Version;
use std::borrow::Cow;
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::env;
use std::fmt::{Debug, Display};
use std::io::Write;
Expand Down Expand Up @@ -238,6 +238,13 @@ file_names.iter().map(|x| x.as_str()).join(", "))]
#[error("Dependency tree resolution failed: {0}")]
DependencyResolutionFailed(String),

#[error("Dependency tree resolution failed due to locked packages: {error}")]
DependencyResolutionFailedWithLocked {
error: String,
// a vec of the names of locked dependencies responsible for the failure
locked_conflicts: Vec<EcoString>,
},

#[error("The package {0} is listed in dependencies and dev-dependencies")]
DuplicateDependency(EcoString),

Expand Down Expand Up @@ -378,7 +385,10 @@ impl Error {
Self::TarFinish(error.to_string())
}

pub fn dependency_resolution_failed(error: ResolutionError) -> Error {
pub fn dependency_resolution_failed(
error: ResolutionError,
locked: &HashMap<EcoString, hexpm::version::Version>,
) -> Error {
fn collect_conflicting_packages<'dt, P: Package, V: Version>(
derivation_tree: &'dt DerivationTree<P, V>,
conflicting_packages: &mut HashSet<&'dt P>,
Expand Down Expand Up @@ -406,54 +416,87 @@ impl Error {
}
}

Self::DependencyResolutionFailed(match error {
match error {
ResolutionError::NoSolution(mut derivation_tree) => {
derivation_tree.collapse_no_versions();

let mut conflicting_packages = HashSet::new();
collect_conflicting_packages(&derivation_tree, &mut conflicting_packages);

wrap_format!("Unable to find compatible versions for \
the version constraints in your gleam.toml. \
The conflicting packages are:
let conflict_names: Vec<EcoString> = conflicting_packages
.iter()
.map(|pkg| (*pkg).to_string().into())
.collect();

{}
",
conflicting_packages.into_iter().map(|s| format!("- {s}")).join("\n"))
}
let locked_conflicts: Vec<EcoString> = conflict_names
.iter()
.filter(|name| locked.contains_key(*name))
.cloned()
.collect();

if !locked_conflicts.is_empty() {
Error::DependencyResolutionFailedWithLocked {
error: wrap_format!(
"Unable to find compatible versions due to package versions locked by manifest.toml.\n\
Consider unlocking the responsible locked package(s) :\n{}",
locked_conflicts.iter().map(|s| format!("- {s}")).join("\n")
),
locked_conflicts,
}
} else {
Error::DependencyResolutionFailed(
wrap_format!(
"Unable to find compatible versions for the version constraints in your gleam.toml.\n\
The conflicting packages are:\n{}",
conflicting_packages.into_iter().map(|s| format!("- {s}")).join("\n")
)
)
}
} // end [`ResolutionError::NoSolution`] arm

ResolutionError::ErrorRetrievingDependencies {
package,
version,
source,
} => format!(
} => {
let msg = format!(
"An error occurred while trying to retrieve dependencies of {package}@{version}: {source}",
),
);
Error::DependencyResolutionFailed(msg)
}

ResolutionError::DependencyOnTheEmptySet {
package,
version,
dependent,
} => format!(
"{package}@{version} has an impossible dependency on {dependent}",
),
} => {
let msg =
format!("{package}@{version} has an impossible dependency on {dependent}",);

Error::DependencyResolutionFailed(msg)
}

ResolutionError::SelfDependency { package, version } => {
format!("{package}@{version} somehow depends on itself.")
let msg = format!("{package}@{version} somehow depends on itself.");
Error::DependencyResolutionFailed(msg)
}

ResolutionError::ErrorChoosingPackageVersion(err) => {
format!("Unable to determine package versions: {err}")
let msg = format!("Unable to determine package versions: {err}");
Error::DependencyResolutionFailed(msg)
}

ResolutionError::ErrorInShouldCancel(err) => {
format!("Dependency resolution was cancelled. {err}")
let msg = format!("Dependency resolution was cancelled. {err}");
Error::DependencyResolutionFailed(msg)
}

ResolutionError::Failure(err) => format!(
"An unrecoverable error happened while solving dependencies: {err}"
),
})
ResolutionError::Failure(err) => {
let msg =
format!("An unrecoverable error happened while solving dependencies: {err}");
Error::DependencyResolutionFailed(msg)
}
}
}

pub fn expand_tar<E>(error: E) -> Error
Expand Down Expand Up @@ -3570,7 +3613,29 @@ The error from the version resolver library was:
}]
}

Error::GitDependencyUnsupported => vec![Diagnostic {
// locked_conflicts ignored as the version resolver lib builds the message
// enumerating them
Error::DependencyResolutionFailedWithLocked{error, locked_conflicts: _} => {
let text = format!(
"An error occurred while determining what dependency packages and
versions should be downloaded.
The error from the version resolver library was:

{}

",
wrap(error)
);
vec![Diagnostic {
title: "Dependency resolution with a locked package".into(),
text,
hint: Some("Try removing locked version(s) in your manifest.toml and re-run the command.".into()),
location: None,
level: Level::Error,
}]
},

Error::GitDependencyUnsupported => vec![Diagnostic {
title: "Git dependencies are not currently supported".into(),
text: "Please remove all git dependencies from the gleam.toml file".into(),
hint: None,
Expand Down
Loading