Skip to content

Commit

Permalink
Verify build-std resolve against original lockfile
Browse files Browse the repository at this point in the history
Co-authored-by: Lawrence Tang <lawrence.tang@arm.com>
  • Loading branch information
adamgemmell and c272 committed May 15, 2024
1 parent 2f17770 commit 4f5aa4d
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 4 deletions.
52 changes: 48 additions & 4 deletions src/cargo/core/compiler/standard_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
28 changes: 28 additions & 0 deletions src/cargo/core/resolver/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,10 @@ impl EncodableResolve {
HashMap::new(),
))
}

pub fn package(&self) -> Option<&Vec<EncodableDependency>> {
self.package.as_ref()
}
}

fn build_path_deps(ws: &Workspace<'_>) -> CargoResult<HashMap<String, SourceId>> {
Expand Down Expand Up @@ -489,6 +493,20 @@ pub struct EncodableDependency {
replace: Option<EncodablePackageId>,
}

impl EncodableDependency {
pub fn name(&self) -> &str {
&self.name
}

pub fn version(&self) -> &str {
&self.version
}

pub fn dependencies(&self) -> Option<&Vec<EncodablePackageId>> {
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.
Expand Down Expand Up @@ -574,6 +592,16 @@ pub struct EncodablePackageId {
source: Option<EncodableSourceId>,
}

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)?;
Expand Down
5 changes: 5 additions & 0 deletions src/cargo/core/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
1 change: 1 addition & 0 deletions tests/testsuite/mock-std/library/alloc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ authors = ["Alex Crichton <alex@alexcrichton.com>"]
edition = "2018"

[dependencies]
core = { path = "../core" }
registry-dep-using-core = { version = "*", features = ['mockbuild'] }
1 change: 1 addition & 0 deletions tests/testsuite/mock-std/library/std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ authors = ["Alex Crichton <alex@alexcrichton.com>"]
edition = "2018"

[dependencies]
alloc = { path = "../alloc" }
registry-dep-using-alloc = { version = "*", features = ['mockbuild'] }

[features]
Expand Down

0 comments on commit 4f5aa4d

Please sign in to comment.