diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 684e4426c04..1ef64cfd3c3 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -5,7 +5,7 @@ use crate::core::compiler::UnitInterner; use crate::core::compiler::{CompileKind, CompileMode, RustcTargetData, Unit}; use crate::core::profiles::{Profiles, UnitFor}; use crate::core::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures}; -use crate::core::resolver::HasDevUnits; +use crate::core::resolver::{EncodableResolve, HasDevUnits}; use crate::core::{Dependency, PackageId, PackageSet, Resolve, SourceId, Workspace}; use crate::ops::{self, Packages}; use crate::util::errors::CargoResult; @@ -125,9 +125,12 @@ pub fn resolve_std<'gctx>( // now. Perhaps in the future features will be decoupled from the resolver // and it will be easier to control feature selection. let current_manifest = src_path.join("library/sysroot/Cargo.toml"); - // TODO: Consider doing something to enforce --locked? Or to prevent the - // lock file from being written, such as setting ephemeral. - let mut std_ws = Workspace::new_virtual(src_path, current_manifest, virtual_manifest, gctx)?; + let mut std_ws = + Workspace::new_virtual(src_path.clone(), current_manifest, virtual_manifest, gctx)?; + + // The source checkout isn't managed by us, so setting ephemeral here + // ensures the lockfile isn't updated. + std_ws.set_ephemeral(true); // Don't require optional dependencies in this workspace, aka std's own // `[dev-dependencies]`. No need for us to generate a `Resolve` which has // those included because we'll never use them anyway. @@ -158,6 +161,47 @@ pub fn resolve_std<'gctx>( HasDevUnits::No, crate::core::resolver::features::ForceAllTargets::No, )?; + + // Verify that we have resolved to a subset of the lockfile + let lockfile = std::fs::read_to_string(&src_path.join("Cargo.lock")) + .expect("Couldn't read the Rust source's lockfile"); + + let encoded_lockfile: EncodableResolve = toml::from_str(&lockfile).unwrap(); + let lockfile_packages = encoded_lockfile.package().expect("libstd has no packages!"); + + for resolved_pkg in resolve.targeted_resolve.iter() { + let pkg_name = resolved_pkg.name().to_string(); + let pkg_ver = resolved_pkg.version().to_string(); + let lockfile_pkg = lockfile_packages.binary_search_by_key( + &(pkg_name.as_str(), pkg_ver.as_str()), + |p| (p.name(), p.version()) + ) + .and_then(|idx| Ok(&lockfile_packages[idx])) + .unwrap_or_else(|_| + panic!("Standard library's package graph changed during resolution: + Package '{}' ({}) was present in the resolve but not in the original lockfile", + resolved_pkg.name(), resolved_pkg.version()) + ); + // If the lockfile wasn't sorted then the binary search result can be nonsensical + assert!(lockfile_pkg.name() == pkg_name); + + for (dep, _) in resolve.targeted_resolve.deps(resolved_pkg) { + lockfile_pkg.dependencies() + .and_then(|deps| { + deps.iter().find(|pkg| { + pkg.name() == dep.name().as_str() + && (pkg.version() == None + || pkg.version() == Some(dep.version().to_string().as_str())) + }) + }) + .unwrap_or_else(|| + panic!("Standard library's package graph changed during resolution: + Package '{}' ({}) was present as a dependency of '{} ({}) in the resolve but not in the lockfile", + dep.name(), dep.version(), resolved_pkg.name(), resolved_pkg.version()) + ); + } + } + Ok(( resolve.pkg_set, resolve.targeted_resolve, diff --git a/src/cargo/core/resolver/encode.rs b/src/cargo/core/resolver/encode.rs index 2b4c3008236..d1c8782a3c3 100644 --- a/src/cargo/core/resolver/encode.rs +++ b/src/cargo/core/resolver/encode.rs @@ -406,6 +406,10 @@ impl EncodableResolve { HashMap::new(), )) } + + pub fn package(&self) -> Option<&Vec> { + self.package.as_ref() + } } fn build_path_deps(ws: &Workspace<'_>) -> CargoResult> { @@ -489,6 +493,20 @@ pub struct EncodableDependency { replace: Option, } +impl EncodableDependency { + pub fn name(&self) -> &str { + &self.name + } + + pub fn version(&self) -> &str { + &self.version + } + + pub fn dependencies(&self) -> Option<&Vec> { + self.dependencies.as_ref() + } +} + /// Pretty much equivalent to [`SourceId`] with a different serialization method. /// /// The serialization for `SourceId` doesn't do URL encode for parameters. @@ -574,6 +592,16 @@ pub struct EncodablePackageId { source: Option, } +impl EncodablePackageId { + pub fn name(&self) -> &str { + &self.name + } + + pub fn version(&self) -> Option<&str> { + self.version.as_ref().map(String::as_str) + } +} + impl fmt::Display for EncodablePackageId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.name)?; diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index 015bee2341f..b5b3393c843 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -622,6 +622,11 @@ impl<'gctx> Workspace<'gctx> { self.is_ephemeral } + pub fn set_ephemeral(&mut self, is_ephemeral: bool) -> &mut Workspace<'gctx> { + self.is_ephemeral = is_ephemeral; + self + } + pub fn require_optional_deps(&self) -> bool { self.require_optional_deps } diff --git a/tests/testsuite/mock-std/Cargo.lock b/tests/testsuite/mock-std/Cargo.lock new file mode 100644 index 00000000000..f9f24e15acb --- /dev/null +++ b/tests/testsuite/mock-std/Cargo.lock @@ -0,0 +1,97 @@ +# This file was handwritten. +version = 3 + +[[package]] +name = "alloc" +version = "0.1.0" +dependencies = [ + "core", + "registry-dep-using-core" +] + +[[package]] +name = "compiler_builtins" +version = "0.1.0" + +[[package]] +name = "core" +version = "0.1.0" + +[[package]] +name = "panic_unwind" +version = "0.1.0" + +[[package]] +name = "proc_macro" +version = "0.1.0" + +[[package]] +name = "registry-dep-using-alloc" +version = "1.0.0" +dependencies = [ + "rustc-std-workspace-alloc", + "rustc-std-workspace-core" +] + +[[package]] +name = "registry-dep-using-core" +version = "1.0.0" +dependencies = [ + "rustc-std-workspace-core" +] + +[[package]] +name = "registry-dep-using-std" +version = "1.0.0" +dependencies = [ + "rustc-std-workspace-std", +] + +[[package]] +name = "rustc-std-workspace-alloc" +version = "1.9.0" +dependencies = [ + "alloc" +] + +[[package]] +name = "rustc-std-workspace-core" +version = "1.9.0" +dependencies = [ + "core" +] + +[[package]] +name = "rustc-std-workspace-std" +version = "1.9.0" +dependencies = [ + "std" +] + +[[package]] +name = "std" +version = "0.1.0" +dependencies = [ + "alloc", + "registry-dep-using-alloc" +] + +[[package]] +name = "sysroot" +version = "0.1.0" +dependencies = [ + "proc_macro", + "std", + "test" +] + +[[package]] +name = "test" +version = "0.1.0" +dependencies = [ + "compiler_builtins", + "panic_unwind", + "std", + "registry-dep-using-std" +] + diff --git a/tests/testsuite/mock-std/library/alloc/Cargo.toml b/tests/testsuite/mock-std/library/alloc/Cargo.toml index dc965abffac..0b8f9d750a9 100644 --- a/tests/testsuite/mock-std/library/alloc/Cargo.toml +++ b/tests/testsuite/mock-std/library/alloc/Cargo.toml @@ -5,4 +5,5 @@ authors = ["Alex Crichton "] edition = "2018" [dependencies] +core = { path = "../core" } registry-dep-using-core = { version = "*", features = ['mockbuild'] } diff --git a/tests/testsuite/mock-std/library/std/Cargo.toml b/tests/testsuite/mock-std/library/std/Cargo.toml index d2cfdea3986..2e0da03dfe2 100644 --- a/tests/testsuite/mock-std/library/std/Cargo.toml +++ b/tests/testsuite/mock-std/library/std/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Alex Crichton "] edition = "2018" [dependencies] +alloc = { path = "../alloc" } registry-dep-using-alloc = { version = "*", features = ['mockbuild'] } [features]