diff --git a/Cargo.lock b/Cargo.lock index 63d7c67034d..b6d04b8a045 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -581,7 +581,7 @@ dependencies = [ [[package]] name = "cargo-util-schemas" -version = "0.14.4" +version = "0.15.0" dependencies = [ "jiff", "schemars", diff --git a/Cargo.toml b/Cargo.toml index 118765b0170..de068415902 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ cargo-platform = { path = "crates/cargo-platform", version = "0.3.3" } cargo-test-macro = { version = "0.4.15", path = "crates/cargo-test-macro" } cargo-test-support = { version = "0.12.0", path = "crates/cargo-test-support" } cargo-util = { version = "0.2.33", path = "crates/cargo-util" } -cargo-util-schemas = { version = "0.14.4", path = "crates/cargo-util-schemas" } +cargo-util-schemas = { version = "0.15.0", path = "crates/cargo-util-schemas" } cargo-util-terminal = { version = "0.1.3", path = "crates/cargo-util-terminal" } cargo_metadata = "0.23.1" clap = "4.6.0" diff --git a/crates/cargo-util-schemas/Cargo.toml b/crates/cargo-util-schemas/Cargo.toml index 489c2b52ce3..e17feac3275 100644 --- a/crates/cargo-util-schemas/Cargo.toml +++ b/crates/cargo-util-schemas/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-util-schemas" -version = "0.14.4" +version = "0.15.0" rust-version = "1.98" # MSRV:1 edition.workspace = true license.workspace = true diff --git a/crates/cargo-util-schemas/src/core/source_kind.rs b/crates/cargo-util-schemas/src/core/source_kind.rs index 3794791114d..1c16a251371 100644 --- a/crates/cargo-util-schemas/src/core/source_kind.rs +++ b/crates/cargo-util-schemas/src/core/source_kind.rs @@ -15,6 +15,8 @@ pub enum SourceKind { LocalRegistry, /// A directory-based registry. Directory, + /// Package sources distributed with the rust toolchain + Builtin, } // The hash here is important for what folder packages get downloaded into. @@ -40,6 +42,7 @@ impl SourceKind { SourceKind::SparseRegistry => None, SourceKind::LocalRegistry => Some("local-registry"), SourceKind::Directory => Some("directory"), + SourceKind::Builtin => Some("builtin"), } } } @@ -71,6 +74,10 @@ impl Ord for SourceKind { (_, SourceKind::Directory) => Ordering::Greater, (SourceKind::Git(a), SourceKind::Git(b)) => a.cmp(b), + (SourceKind::Git(_), _) => Ordering::Less, + (_, SourceKind::Git(_)) => Ordering::Greater, + + (SourceKind::Builtin, SourceKind::Builtin) => Ordering::Equal, } } } diff --git a/crates/resolver-tests/src/helpers.rs b/crates/resolver-tests/src/helpers.rs index dec0250eb02..604d09a1018 100644 --- a/crates/resolver-tests/src/helpers.rs +++ b/crates/resolver-tests/src/helpers.rs @@ -87,6 +87,17 @@ impl, U: AsRef> ToPkgId for (T, U) { } } +#[derive(Copy, Clone)] +pub struct BuiltinPid { + pub name: &'static str, +} + +impl ToPkgId for BuiltinPid { + fn to_pkgid(&self) -> PackageId { + PackageId::try_new(self.name, "0.0.0", builtin_loc()).unwrap() + } +} + #[macro_export] macro_rules! pkg { ($pkgid:expr => [$($deps:expr),* $(,)? ]) => ({ @@ -108,6 +119,13 @@ fn registry_loc() -> SourceId { *example_dot } +fn builtin_loc() -> SourceId { + static LOCAL_PATH: OnceLock = OnceLock::new(); + let local_path = LOCAL_PATH + .get_or_init(|| SourceId::for_builtin(&std::env::current_dir().unwrap()).unwrap()); + *local_path +} + pub fn pkg(name: T) -> Summary { pkg_dep(name, Vec::new()) } @@ -215,6 +233,10 @@ pub fn dep_loc(name: &str, location: &str) -> Dependency { Dependency::parse(name, Some("1.0.0"), source_id).unwrap() } +pub fn dep_builtin(name: &str) -> Dependency { + Dependency::parse(name, None, builtin_loc()).unwrap() +} + pub fn dep_kind(name: &str, kind: DepKind) -> Dependency { let mut dep = dep(name); dep.set_kind(kind); @@ -235,6 +257,12 @@ pub fn names(names: &[P]) -> Vec { names.iter().map(|name| name.to_pkgid()).collect() } +/// For a set of name specifiers of varying types +#[macro_export] +macro_rules! names { + ($($name:expr),* $(,)?) => {&vec![$($name.to_pkgid()),*]}; +} + pub fn loc_names(names: &[(&'static str, &'static str)]) -> Vec { names .iter() diff --git a/crates/resolver-tests/src/lib.rs b/crates/resolver-tests/src/lib.rs index cad66c896d2..e960e7d4e74 100644 --- a/crates/resolver-tests/src/lib.rs +++ b/crates/resolver-tests/src/lib.rs @@ -57,11 +57,12 @@ pub fn resolve_and_validated_raw( root_pkg_id: PackageId, sat_resolver: &mut SatResolver, ) -> CargoResult)>> { - let resolve = resolve_with_global_context_raw( + let resolve = resolve_with_gctx_implicit_deps_raw( deps.clone(), registry, root_pkg_id, &GlobalContext::default().unwrap(), + &[], ); match resolve { @@ -115,20 +116,36 @@ fn collect_features(resolve: &Resolve) -> Vec<(PackageId, Vec)> .collect() } +pub fn resolve_with_implicit_builtins( + deps: Vec, + registry: &[Summary], + implicit_builtin_deps: &[Dependency], +) -> CargoResult { + let gctx = GlobalContext::default().unwrap(); + resolve_with_gctx_implicit_deps_raw( + deps, + registry, + pkg_id("root"), + &gctx, + implicit_builtin_deps, + ) +} + pub fn resolve_with_global_context( deps: Vec, registry: &[Summary], gctx: &GlobalContext, ) -> CargoResult)>> { - let resolve = resolve_with_global_context_raw(deps, registry, pkg_id("root"), gctx)?; + let resolve = resolve_with_gctx_implicit_deps_raw(deps, registry, pkg_id("root"), gctx, &[])?; Ok(collect_features(&resolve)) } -pub fn resolve_with_global_context_raw( +fn resolve_with_gctx_implicit_deps_raw( deps: Vec, registry: &[Summary], root_pkg_id: PackageId, gctx: &GlobalContext, + implicit_builtin_deps: &[Dependency], ) -> CargoResult { struct MyRegistry<'a> { list: &'a [Summary], @@ -205,6 +222,7 @@ pub fn resolve_with_global_context_raw( &version_prefs, ResolveVersion::with_rust_version(None), gctx, + implicit_builtin_deps, ); // The largest test in our suite takes less then 30 secs. diff --git a/crates/resolver-tests/tests/resolve.rs b/crates/resolver-tests/tests/resolve.rs index 20d32fbf884..b8b843ccf63 100644 --- a/crates/resolver-tests/tests/resolve.rs +++ b/crates/resolver-tests/tests/resolve.rs @@ -1,15 +1,18 @@ use cargo::util::GlobalContext; +use cargo::util::interning::InternedString; use cargo::workspace::Dependency; use cargo::workspace::dependency::DepKind; +use resolver_tests::helpers::dep_builtin; +use resolver_tests::resolve_with_implicit_builtins; use snapbox::assert_data_eq; use snapbox::str; use resolver_tests::{ helpers::{ - ToDep, ToPkgId, assert_contains, assert_same, dep, dep_kind, dep_loc, dep_req, loc_names, - names, pkg, pkg_dep, pkg_dep_with, pkg_id, pkg_loc, registry, + BuiltinPid, ToDep, ToPkgId, assert_contains, assert_same, dep, dep_kind, dep_loc, dep_req, + loc_names, names, pkg, pkg_dep, pkg_dep_with, pkg_id, pkg_loc, registry, }, - pkg, resolve, resolve_with_global_context, + names, pkg, resolve, resolve_with_global_context, }; #[test] @@ -1036,3 +1039,71 @@ failed to select a version for `F` which could resolve this conflict "#]] ); } + +#[test] +fn test_builtin_dependency() { + let core = BuiltinPid { name: "core" }; + let reg = registry(vec![pkg!(core)]); + + let builtin_dep = dep_builtin("core"); + + let res = resolve(vec![builtin_dep], ®).unwrap(); + + assert_same(&res, &names!("root", core)); +} + +#[test] +fn normal_dependency_is_not_satisfied_by_builtin_package() { + let core = BuiltinPid { name: "core" }; + let reg = registry(vec![pkg!(core)]); + + assert!(resolve(vec![dep("core")], ®).is_err()); +} + +#[test] +fn missing_builtin_dependency_errors() { + assert!(resolve(vec![dep_builtin("core")], ®istry(vec![])).is_err()); +} + +#[test] +fn injected_builtins() { + let core = BuiltinPid { name: "core" }; + let core = pkg!(core); + let compiler_builtins = BuiltinPid { + name: "compiler_builtins", + }; + let compiler_builtins = pkg!(compiler_builtins); + + let reg = registry(vec![core.clone(), compiler_builtins.clone()]); + + let mut deps = vec![]; + deps.push( + Dependency::new_implicit_builtin( + InternedString::new("core"), + &core.source_id().local_path().unwrap(), + ) + .unwrap(), + ); + deps.push( + Dependency::new_implicit_builtin( + InternedString::new("compiler_builtins"), + &core.source_id().local_path().unwrap(), + ) + .unwrap(), + ); + + let resolve = resolve_with_implicit_builtins(Vec::new(), ®, &deps).unwrap(); + + let root_deps = resolve + .deps(pkg_id("root")) + .map(|(pkg_id, deps)| { + assert_eq!(deps.len(), 1); + assert!(deps.iter().all(Dependency::is_opaque)); + pkg_id + }) + .collect::>(); + assert_same( + &root_deps, + &[core.package_id(), compiler_builtins.package_id()], + ); +} diff --git a/src/compiler/standard_lib.rs b/src/compiler/standard_lib.rs index ba5e1bc12f9..d3d05d5dd1b 100644 --- a/src/compiler/standard_lib.rs +++ b/src/compiler/standard_lib.rs @@ -7,7 +7,7 @@ use crate::ops::{self, Packages}; use crate::resolver::HasDevUnits; use crate::resolver::Resolve; use crate::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures}; -use crate::util::errors::CargoResult; +use crate::util::CargoResult; use crate::workspace::profiles::{Profiles, UnitFor}; use crate::workspace::{PackageId, PackageSet, Workspace}; @@ -16,7 +16,11 @@ use std::path::PathBuf; use super::BuildConfig; -fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[Unit]) -> HashSet<&'a str> { +pub fn std_crates<'a>( + crates: &'a [String], + default: &'static str, + units: &[Unit], +) -> HashSet<&'a str> { let mut crates = HashSet::from_iter(crates.iter().map(|s| s.as_str())); // This is a temporary hack until there is a more principled way to // declare dependencies in Cargo.toml. @@ -217,7 +221,7 @@ fn generate_roots( Ok(()) } -fn detect_sysroot_src_path(target_data: &RustcTargetData<'_>) -> CargoResult { +pub(crate) fn detect_sysroot_src_path(target_data: &RustcTargetData<'_>) -> CargoResult { if let Some(s) = target_data.gctx.get_env_os("__CARGO_TESTS_ONLY_SRC_ROOT") { return Ok(s.into()); } diff --git a/src/ops/resolve.rs b/src/ops/resolve.rs index aa5821210dd..def98e9245d 100644 --- a/src/ops/resolve.rs +++ b/src/ops/resolve.rs @@ -528,6 +528,9 @@ pub fn resolve_with_previous<'gctx>( let replace = lock_replacements(ws, previous, &keep); + //TODO: Enable implicit builtin dependencies for `-Zbuild-std` once builtins are fully implemented + let implicit_builtin_deps = &[]; + let mut resolved = resolver::resolve( &summaries, &replace, @@ -535,6 +538,7 @@ pub fn resolve_with_previous<'gctx>( &version_prefs, ResolveVersion::with_rust_version(ws.lowest_rust_version()), ws.gctx(), + implicit_builtin_deps, )?; let patches = registry.patches().values().flat_map(|v| v.iter()); diff --git a/src/resolver/dep_cache.rs b/src/resolver/dep_cache.rs index f2a928fe231..756e3b7857d 100644 --- a/src/resolver/dep_cache.rs +++ b/src/resolver/dep_cache.rs @@ -219,6 +219,8 @@ pub struct RegistryQueryer<'a, T: Registry> { (Option, Summary, ResolveOpts), (Rc<(HashSet, Rc>)>, bool), >, + /// The set of builtin dependencies to inject when appropriate + implicit_builtin_deps: &'a [Dependency], } impl<'a, T: Registry> RegistryQueryer<'a, T> { @@ -226,6 +228,7 @@ impl<'a, T: Registry> RegistryQueryer<'a, T> { registry: &'a T, replacements: &'a [(PackageIdSpec, Dependency)], version_prefs: &'a VersionPreferences, + implicit_builtin_deps: &'a [Dependency], ) -> Self { let inner = Rc::new(RegistryQueryerAsync::new( registry, @@ -236,6 +239,7 @@ impl<'a, T: Registry> RegistryQueryer<'a, T> { inner: inner.clone(), poller: LocalPollAdapter::new(inner), summary_cache: HashMap::default(), + implicit_builtin_deps, } } @@ -308,7 +312,13 @@ impl<'a, T: Registry> RegistryQueryer<'a, T> { // First, figure out our set of dependencies based on the requested set // of features. This also calculates what features we're going to enable // for our own dependencies. - let (used_features, deps) = resolve_features(parent, candidate, opts)?; + let (used_features, mut deps) = resolve_features(parent, candidate, opts)?; + + if !candidate.source_id().is_builtin() { + for dep in self.implicit_builtin_deps { + deps.push((dep.clone(), Rc::new(BTreeSet::default()))); + } + } // Next, transform all dependencies into a list of possible candidates // which can satisfy that dependency. diff --git a/src/resolver/encode.rs b/src/resolver/encode.rs index 3bc4d0921af..6ef1752f265 100644 --- a/src/resolver/encode.rs +++ b/src/resolver/encode.rs @@ -661,7 +661,7 @@ pub fn encodable_package_id( } fn encodable_source_id(id: SourceId, version: ResolveVersion) -> Option { - if id.is_path() { + if id.is_path() || id.is_builtin() { None } else { Some( diff --git a/src/resolver/mod.rs b/src/resolver/mod.rs index ce1ca941efc..b0f993ca595 100644 --- a/src/resolver/mod.rs +++ b/src/resolver/mod.rs @@ -129,12 +129,19 @@ pub fn resolve( version_prefs: &VersionPreferences, resolve_version: ResolveVersion, gctx: &GlobalContext, + implicit_builtin_deps: &[Dependency], ) -> CargoResult { let first_version = gctx .cli_unstable() .direct_minimal_versions .then_some(VersionOrdering::MinimumVersionsFirst); - let mut registry = RegistryQueryer::new(registry, replacements, version_prefs); + + let mut registry = RegistryQueryer::new( + registry, + replacements, + version_prefs, + &implicit_builtin_deps, + ); // Global cache of the reasons for each time we backtrack. let mut past_conflicting_activations = conflict_cache::ConflictCache::new(); diff --git a/src/sources/builtin.rs b/src/sources/builtin.rs new file mode 100644 index 00000000000..665ae78e55d --- /dev/null +++ b/src/sources/builtin.rs @@ -0,0 +1,139 @@ +use std::{cell::RefCell, path::Path}; + +use crate::{ + CargoResult, GlobalContext, + sources::{ + IndexSummary, RecursivePathSource, + source::{MaybePackage, QueryKind, Source}, + }, + util::data_structures::HashMap, + workspace::{Dependency, Package, PackageId, SourceId, Summary}, +}; + +/// A builtin source represents standard library packages used in build-std, which are "built into" +/// the toolchain. Returns opaque `Summary`s - see [`Summary::new_opaque()`] +/// +/// It wraps a [`RecursivePathSource`] and uses that to discover packages +pub struct BuiltinSource<'gctx> { + /// The unique identifier for this source + source_id: SourceId, + /// The underlying path source which discovers packages + path_source: RecursivePathSource<'gctx>, + /// Opaque summaries cached by the real package ID returned by the path source. + opaque_summaries: RefCell>, +} + +impl<'gctx> BuiltinSource<'gctx> { + pub fn new(path: &Path, source_id: SourceId, gctx: &'gctx GlobalContext) -> Self { + assert!( + source_id.is_builtin(), + "source `{source_id} is not a builtin" + ); + let path_source = RecursivePathSource::new(&path, source_id, gctx); + Self { + source_id, + path_source, + opaque_summaries: RefCell::new(HashMap::default()), + } + } +} + +#[async_trait::async_trait(?Send)] +impl<'gctx> Source for BuiltinSource<'gctx> { + /// All builtin dependencies are opaque, so this will return a summary without any dependencies when queried + async fn query( + &self, + dep: &Dependency, + kind: QueryKind, + f: &mut dyn FnMut(IndexSummary), + ) -> CargoResult<()> { + if !dep.source_id().is_builtin() { + // Avoid loading packages in the path source if it's not needed + return Ok(()); + } + self.path_source + .query(dep, kind, &mut |summary| { + let summary = match summary { + IndexSummary::Candidate(summary) => { + let package_id = summary.package_id(); + let opaque = self + .opaque_summaries + .borrow_mut() + .entry(package_id) + .or_insert_with(|| Summary::new_opaque(package_id, self.source_id)) + .clone(); + IndexSummary::Candidate(opaque) + } + summary => summary, + }; + f(summary); + }) + .await + } + + fn supports_checksums(&self) -> bool { + self.path_source.supports_checksums() + } + + fn requires_precise(&self) -> bool { + self.path_source.requires_precise() + } + + fn source_id(&self) -> SourceId { + self.source_id + } + + async fn download(&self, id: PackageId) -> CargoResult { + self.path_source.download(id).await + } + + async fn finish_download(&self, id: PackageId, data: Vec) -> CargoResult { + self.path_source.finish_download(id, data).await + } + + fn fingerprint(&self, pkg: &Package) -> CargoResult { + self.path_source.fingerprint(pkg) + } + + fn describe(&self) -> String { + self.source_id.to_string() + } + + fn invalidate_cache(&self) { + // The RecursivePathSource does not clear its cached Packages, meaning nothing can + // invalidate our cached summaries + } + + fn set_quiet(&mut self, quiet: bool) { + self.path_source.set_quiet(quiet); + } +} + +#[cfg(test)] +mod test { + use std::path::Path; + + use crate::{GlobalContext, sources::IndexSummary, workspace::Dependency}; + + #[test] + fn builtin_source() { + let gctx = GlobalContext::default().unwrap(); + let mock_std_root = + Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/testsuite/mock-std/library"); + + let dep = Dependency::new_implicit_builtin("core".into(), &mock_std_root).unwrap(); + assert!(dep.is_opaque()); + + let source = dep.source_id().load(&gctx).unwrap(); + let results = + crate::util::block_on(source.query_vec(&dep, crate::sources::source::QueryKind::Exact)) + .unwrap(); + + assert_eq!(results.len(), 1); + let result = results[0].clone(); + if let IndexSummary::Candidate(s) = result { + assert!(dep.matches(&s)); + assert!(s.dependencies().is_empty()); + } + } +} diff --git a/src/sources/mod.rs b/src/sources/mod.rs index 5d92c92770b..999d4cf2b58 100644 --- a/src/sources/mod.rs +++ b/src/sources/mod.rs @@ -26,6 +26,7 @@ //! //! [source replacement]: https://doc.rust-lang.org/nightly/cargo/reference/source-replacement.html +pub use self::builtin::BuiltinSource; pub use self::config::SourceConfigMap; pub use self::directory::DirectorySource; pub use self::git::GitSource; @@ -37,6 +38,7 @@ pub use self::registry::{ }; pub use self::replaced::ReplacedSource; +pub mod builtin; pub mod config; pub mod directory; pub mod git; diff --git a/src/sources/path.rs b/src/sources/path.rs index 1d235237bc3..ac376106e73 100644 --- a/src/sources/path.rs +++ b/src/sources/path.rs @@ -1184,12 +1184,15 @@ fn read_nested_packages( // Registry sources are not allowed to have `path=` dependencies because // they're all translated to actual registry dependencies. // + // The standard library source intentionally does not include some test crates leading to broken + // dev-dependencies links. The main directory walk already gets all the packages we need. + // // We normalize the path here ensure that we don't infinitely walk around // looking for crates. By normalizing we ensure that we visit this crate at // most once. // // TODO: filesystem/symlink implications? - if !source_id.is_registry() { + if !source_id.is_registry() && !source_id.is_builtin() { for p in nested.iter() { let path = paths::normalize_path(&path.join(p)); let result = diff --git a/src/workspace/dependency.rs b/src/workspace/dependency.rs index b4f4b9c8095..cb0dc1d9b46 100644 --- a/src/workspace/dependency.rs +++ b/src/workspace/dependency.rs @@ -51,6 +51,10 @@ struct Inner { // This dependency should be used only for this platform. // `None` means *all platforms*. platform: Option, + // Opaque dependencies should not be traversed any deeper by the resolver. Required packages should + // be resolved as roots of a separate resolver run and the dependency handled during unit + // generation. + opaque: bool, } #[derive(Serialize)] @@ -162,10 +166,36 @@ impl Dependency { platform: None, explicit_name_in_toml: None, artifact: None, + // All deps on builtin packages are opaque, and vice versa + opaque: source_id.is_builtin(), }), } } + pub fn new_implicit_builtin(name: InternedString, path: &Path) -> CargoResult { + Ok(Dependency { + inner: Arc::new(Inner { + name, + source_id: SourceId::for_builtin(path)?, + registry_id: None, + req: OptVersionReq::Any, + kind: DepKind::Normal, + only_match_name: false, + optional: false, + public: true, + // Build-std does not currently resolve features - any feature specifications here + // will be thrown away during Unit generation + features: Vec::new(), + default_features: false, + specified_req: false, + platform: None, + explicit_name_in_toml: None, + artifact: None, + opaque: true, + }), + }) + } + pub fn serialized( &self, unstable_flags: &CliUnstable, @@ -412,6 +442,10 @@ impl Dependency { self.inner.optional } + pub fn is_opaque(&self) -> bool { + self.inner.opaque + } + /// Returns `true` if the default features of the dependency are requested. pub fn uses_default_features(&self) -> bool { self.inner.default_features diff --git a/src/workspace/source_id.rs b/src/workspace/source_id.rs index dbfbc367dae..f26d48173a0 100644 --- a/src/workspace/source_id.rs +++ b/src/workspace/source_id.rs @@ -1,7 +1,9 @@ use crate::context; use crate::sources::registry::CRATES_IO_HTTP_INDEX; use crate::sources::source::Source; -use crate::sources::{CRATES_IO_DOMAIN, CRATES_IO_INDEX, CRATES_IO_REGISTRY, DirectorySource}; +use crate::sources::{ + BuiltinSource, CRATES_IO_DOMAIN, CRATES_IO_INDEX, CRATES_IO_REGISTRY, DirectorySource, +}; use crate::sources::{GitSource, PathSource, RegistrySource}; use crate::util::data_structures::HashSet; use crate::util::interning::InternedString; @@ -204,6 +206,14 @@ impl SourceId { SourceId::new(SourceKind::Path, url, None) } + /// Creates a `SourceId` from a filesystem path representing a builtin package. + /// + /// `path`: an absolute path. + pub fn for_builtin(path: &Path) -> CargoResult { + let url = path.into_url()?; + SourceId::new(SourceKind::Builtin, url, None) + } + /// Creates a `SourceId` from a filesystem path. /// /// `path`: an absolute path. @@ -345,13 +355,18 @@ impl SourceId { self.inner.kind == SourceKind::Path } + /// Returns `true` if this source is built into Cargo + pub fn is_builtin(self) -> bool { + self.inner.kind == SourceKind::Builtin + } + /// Returns the local path if this is a path dependency. pub fn local_path(self) -> Option { - if self.inner.kind != SourceKind::Path { - return None; + if let SourceKind::Path | SourceKind::Builtin = self.inner.kind { + Some(self.inner.url.to_file_path().unwrap()) + } else { + None } - - Some(self.inner.url.to_file_path().unwrap()) } pub fn kind(&self) -> &SourceKind { @@ -403,6 +418,14 @@ impl SourceId { } Ok(Box::new(PathSource::new(&path, self, gctx))) } + SourceKind::Builtin => { + let path = self + .inner + .url + .to_file_path() + .expect("builtin sources cannot be remote"); + Ok(Box::new(BuiltinSource::new(&path, self, gctx))) + } SourceKind::Registry | SourceKind::SparseRegistry => { Ok(Box::new(RegistrySource::remote(self, gctx)?)) } @@ -663,6 +686,7 @@ impl fmt::Display for SourceId { Ok(()) } SourceKind::Path => write!(f, "{}", url_display(&self.inner.url)), + SourceKind::Builtin => write!(f, "builtin {}", url_display(&self.inner.url)), SourceKind::Registry | SourceKind::SparseRegistry => { write!(f, "registry `{}`", self.display_registry_name()) } diff --git a/src/workspace/summary.rs b/src/workspace/summary.rs index 1cf96a88d99..29eed621154 100644 --- a/src/workspace/summary.rs +++ b/src/workspace/summary.rs @@ -97,6 +97,31 @@ impl Summary { }) } + /// Creates a dummy Summary to satisfy an opaque dependency + /// + /// The summary has no dependencies and is artificial - it is used purely guide the resolver + /// by satisfying opaque dependencies and is discarded during Unit generation. The real + /// packages that are converted into `Unit`s come from a different invocation of the resolver. + pub fn new_opaque(pkg_id: PackageId, sid: SourceId) -> Self { + // Currently only builtin packages can be opaque + assert!(sid.is_builtin()); + Summary { + inner: Arc::new(Inner { + package_id: pkg_id, + // opaque dependencies - the real deps are inserted during unit generation + dependencies: vec![], + // Features are currently ignored during unit generation. May need to be changed + // when implementing feature specification for explicit builtin dependencies + features: Arc::new(BTreeMap::new()), + checksum: None, + links: None, + // Builtins are always valid for our current toolchain + rust_version: None, + pubtime: None, + }), + } + } + pub fn package_id(&self) -> PackageId { self.inner.package_id }