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..3fda05e20ee 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,67 @@ 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, _)| 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/workspace/dependency.rs b/src/workspace/dependency.rs index b4f4b9c8095..e9fc65caf83 100644 --- a/src/workspace/dependency.rs +++ b/src/workspace/dependency.rs @@ -166,6 +166,29 @@ impl Dependency { } } + 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, + }), + }) + } + pub fn serialized( &self, unstable_flags: &CliUnstable, diff --git a/src/workspace/source_id.rs b/src/workspace/source_id.rs index dbfbc367dae..280b83ebda4 100644 --- a/src/workspace/source_id.rs +++ b/src/workspace/source_id.rs @@ -204,6 +204,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 +353,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 +416,7 @@ impl SourceId { } Ok(Box::new(PathSource::new(&path, self, gctx))) } + SourceKind::Builtin => todo!("builtin source"), SourceKind::Registry | SourceKind::SparseRegistry => { Ok(Box::new(RegistrySource::remote(self, gctx)?)) } @@ -663,6 +677,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()) }