Skip to content

Commit

Permalink
PubGrub resolver
Browse files Browse the repository at this point in the history
commit-id:d55b5ecb
  • Loading branch information
maciektr committed Oct 22, 2024
1 parent 28db9a7 commit 93173f0
Show file tree
Hide file tree
Showing 18 changed files with 930 additions and 87 deletions.
47 changes: 47 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,15 @@ pathdiff = { version = "0.2", features = ["camino"] }
petgraph = "0.6"
predicates = "3"
proc-macro2 = "1"
pubgrub = { git = "https://github.com/pubgrub-rs/pubgrub.git", branch = "dev" }
quote = "1"
ra_ap_toolchain = "0.0.218"
rayon = "1.10"
redb = "2.1.4"
reqwest = { version = "0.11", features = ["gzip", "brotli", "deflate", "json", "stream", "multipart"], default-features = false }
salsa = { package = "rust-analyzer-salsa", version = "0.17.0-pre.6" }
semver = { version = "1", features = ["serde"] }
semver-pubgrub = { git = "https://github.com/maciektr/semver-pubgrub.git" }
serde = { version = "1", features = ["serde_derive"] }
serde-untagged = "0.1"
serde-value = "0.7"
Expand All @@ -125,6 +127,7 @@ test-case = "3"
thiserror = "1"
time = "0.3"
tokio = { version = "1", features = ["macros", "io-util", "process", "rt", "rt-multi-thread", "sync"] }
tokio-stream = "0.1"
toml = "0.8"
toml_edit = { version = "0.22", features = ["serde"] }
tower-http = { version = "0.4", features = ["fs"] }
Expand All @@ -142,6 +145,9 @@ xxhash-rust = { version = "0.8", features = ["xxh3"] }
zip = { version = "0.6", default-features = false, features = ["deflate"] }
zstd = "0.13"

[patch.'https://github.com/pubgrub-rs/pubgrub.git']
pubgrub = { git = 'https://github.com/maciektr/pubgrub.git', branch = 'dev' }

[profile.release]
lto = true

Expand Down
4 changes: 4 additions & 0 deletions scarb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,18 @@ indoc.workspace = true
itertools.workspace = true
libloading.workspace = true
once_cell.workspace = true
once_map = { path = "../utils/once-map" }
pathdiff.workspace = true
petgraph.workspace = true
pubgrub.workspace = true
ra_ap_toolchain.workspace = true
redb.workspace = true
reqwest.workspace = true
scarb-build-metadata = { path = "../utils/scarb-build-metadata" }
scarb-metadata = { path = "../scarb-metadata", default-features = false, features = ["builder"] }
scarb-stable-hash = { path = "../utils/scarb-stable-hash" }
scarb-ui = { path = "../utils/scarb-ui" }
semver-pubgrub.workspace = true
semver.workspace = true
serde-untagged.workspace = true
serde-value.workspace = true
Expand All @@ -75,6 +78,7 @@ smol_str.workspace = true
tar.workspace = true
thiserror.workspace = true
tokio.workspace = true
tokio-stream.workspace = true
toml.workspace = true
toml_edit.workspace = true
tracing-subscriber.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion scarb/src/core/lockfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub enum LockVersion {
V1 = 1,
}

#[derive(Debug, Eq, PartialEq, Default, Serialize, Deserialize)]
#[derive(Debug, Clone, Eq, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Lockfile {
pub version: LockVersion,
Expand Down
2 changes: 1 addition & 1 deletion scarb/src/core/registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ pub(crate) mod mock {
let summary = Summary::builder()
.package_id(package_id)
.dependencies(dependencies)
.no_core(package_id.is_core())
.no_core(package_id.name == PackageName::CORE)
.build();

let manifest = Box::new(
Expand Down
46 changes: 46 additions & 0 deletions scarb/src/resolver/algorithm/in_memory_index.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use crate::core::{Package, PackageId, Summary};
use crate::resolver::algorithm::provider::PubGrubPackage;
use once_map::OnceMap;
use std::sync::Arc;

/// In-memory index of package metadata.
#[derive(Default, Clone)]
pub struct InMemoryIndex(Arc<SharedInMemoryIndex>);

#[derive(Default)]
struct SharedInMemoryIndex {
/// A map from package name to the metadata for that package and the index where the metadata
/// came from.
packages: FxOnceMap<PubGrubPackage, Arc<VersionsResponse>>,

/// A map from package ID to metadata for that distribution.
#[allow(dead_code)]
distributions: FxOnceMap<PackageId, Arc<MetadataResponse>>,
}

pub(crate) type FxOnceMap<K, V> = OnceMap<K, V>;

impl InMemoryIndex {
/// Returns a reference to the package metadata map.
pub fn packages(&self) -> &FxOnceMap<PubGrubPackage, Arc<VersionsResponse>> {
&self.0.packages
}

/// Returns a reference to the distribution metadata map.
#[allow(dead_code)]
pub fn distributions(&self) -> &FxOnceMap<PackageId, Arc<MetadataResponse>> {
&self.0.distributions
}
}

// pub struct VersionsResponse;
#[derive(Debug)]
pub enum VersionsResponse {
Found(Vec<Summary>),
}

// pub struct MetadataResponse;
pub enum MetadataResponse {
#[allow(dead_code)]
Found(Package),
}
167 changes: 167 additions & 0 deletions scarb/src/resolver/algorithm/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,168 @@
use crate::core::lockfile::Lockfile;
use crate::core::registry::Registry;
use crate::core::{PackageId, Resolve, Summary};
use crate::resolver::algorithm::provider::{
rewrite_dependency_source_id, rewrite_locked_dependency, DependencyProviderError,
PubGrubDependencyProvider, PubGrubPackage,
};
use crate::resolver::algorithm::solution::{build_resolve, validate_solution};
use crate::resolver::algorithm::state::{Request, ResolverState};
use anyhow::bail;
use futures::{FutureExt, TryFutureExt};
use itertools::Itertools;
use pubgrub::error::PubGrubError;
use pubgrub::report::{DefaultStringReporter, Reporter};
use pubgrub::{Incompatibility, State};
use std::collections::HashSet;
use std::sync::Arc;
use std::thread;
use tokio::sync::{mpsc, oneshot};

mod in_memory_index;
mod provider;
mod solution;
mod state;

#[allow(clippy::dbg_macro)]
#[allow(dead_code)]
pub async fn resolve<'c>(
summaries: &[Summary],
registry: &dyn Registry,
lockfile: Lockfile,
) -> anyhow::Result<Resolve> {
let state = Arc::new(ResolverState::default());

let (request_sink, request_stream): (mpsc::Sender<Request>, mpsc::Receiver<Request>) =
mpsc::channel(300);

let requests_fut = state
.clone()
.fetch(registry, request_stream)
.map_err(|err| anyhow::format_err!(err))
.fuse();

for summary in summaries {
let package: PubGrubPackage = summary.package_id.into();
if state.index.packages().register(package.clone()) {
request_sink.send(Request::Package(package)).await?;
}
for dep in summary.full_dependencies() {
let dep = rewrite_dependency_source_id(summary.package_id, dep)?;
let locked_package_id = lockfile.packages_matching(dep.clone());
let dep = if let Some(locked_package_id) = locked_package_id {
rewrite_locked_dependency(dep.clone(), locked_package_id?)
} else {
dep.clone()
};

let package: PubGrubPackage = (&dep).into();
if state.index.packages().register(package.clone()) {
request_sink.send(Request::Package(package)).await?;
}
}
}

let main_package_ids: HashSet<PackageId> =
HashSet::from_iter(summaries.iter().map(|sum| sum.package_id));

let (tx, rx) = oneshot::channel();

let cloned_lockfile = lockfile.clone();
thread::Builder::new()
.name("scarb-resolver".into())
.spawn(move || {
let result = || {
let provider = PubGrubDependencyProvider::new(
main_package_ids,
state,
request_sink,
cloned_lockfile,
);

// Init state
let main_package_ids = provider
.main_package_ids()
.clone()
.into_iter()
.collect_vec();

let Some((first, rest)) = main_package_ids.split_first() else {
bail!("empty summaries");
};
let package: PubGrubPackage = (*first).into();
let version = first.version.clone();
let mut state = State::init(package.clone(), version);
state
.unit_propagation(package.clone())
.map_err(|err| anyhow::format_err!("unit propagation failed: {:?}", err))?;
for package_id in rest {
let package: PubGrubPackage = (*package_id).into();
let version = package_id.version.clone();
state.add_incompatibility(Incompatibility::not_root(
package.clone(),
version.clone(),
));
state
.unit_propagation(package)
.map_err(|err| anyhow::format_err!("unit propagation failed: {:?}", err))?
}

// Resolve requirements
let solution = pubgrub::solver::resolve_state(&provider, &mut state, package)
.map_err(format_error)?;

validate_solution(&solution)?;
build_resolve(&provider, solution)
};
let result = result();
tx.send(result).unwrap();
})?;

let resolve_fut = async move {
rx.await
// .map_err(|_| (ResolveError::ChannelClosed, FxHashSet::default()))
// .map_err(|_| DependencyProviderError::ChannelClosed)
.map_err(|_err| anyhow::format_err!("channel closed"))
.and_then(|result| result)
};

let (_, resolve) = tokio::try_join!(requests_fut, resolve_fut)?;
resolve.check_checksums(&lockfile)?;
Ok(resolve)
}

fn format_error(err: PubGrubError<PubGrubDependencyProvider>) -> anyhow::Error {
match err {
PubGrubError::NoSolution(derivation_tree) => {
anyhow::format_err!(
"version solving failed:\n{}\n",
DefaultStringReporter::report(&derivation_tree)
)
}
PubGrubError::ErrorChoosingPackageVersion(DependencyProviderError::PackageNotFound {
name,
version,
}) => {
anyhow::format_err!("cannot find package `{name} {version}`")
}
PubGrubError::ErrorChoosingPackageVersion(DependencyProviderError::PackageQueryFailed(
err,
)) => anyhow::format_err!("{}", err).context("dependency query failed"),
PubGrubError::ErrorRetrievingDependencies {
package,
version,
source,
} => anyhow::Error::from(source)
.context(format!("cannot get dependencies of `{package}@{version}`")),
PubGrubError::SelfDependency { package, version } => {
anyhow::format_err!("self dependency found: `{}@{}`", package, version)
}
PubGrubError::ErrorInShouldCancel(err) => {
anyhow::format_err!("{}", err).context("should cancel failed")
}
PubGrubError::Failure(msg) => anyhow::format_err!("{}", msg).context("resolver failure"),
// PubGrubError::ErrorChoosingPackageVersion(DependencyProviderError::ChannelClosed) => {
// anyhow::format_err!("channel closed")
// }
}
}
Loading

0 comments on commit 93173f0

Please sign in to comment.