Skip to content

Commit b033b97

Browse files
committed
refactor(package): extract verification code
1 parent 2f5788a commit b033b97

File tree

2 files changed

+197
-163
lines changed

2 files changed

+197
-163
lines changed

src/cargo/ops/cargo_package/mod.rs

Lines changed: 13 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ use std::fs::{self, File};
33
use std::io::prelude::*;
44
use std::io::SeekFrom;
55
use std::path::{Path, PathBuf};
6-
use std::sync::Arc;
76
use std::task::Poll;
87

9-
use crate::core::compiler::{BuildConfig, CompileMode, DefaultExecutor, Executor};
108
use crate::core::dependency::DepKind;
119
use crate::core::manifest::Target;
1210
use crate::core::resolver::CliFeatures;
1311
use crate::core::resolver::HasDevUnits;
14-
use crate::core::{Feature, PackageIdSpecQuery, Shell, Verbosity, Workspace};
12+
use crate::core::PackageIdSpecQuery;
13+
use crate::core::Shell;
14+
use crate::core::Verbosity;
15+
use crate::core::Workspace;
1516
use crate::core::{Package, PackageId, PackageSet, Resolve, SourceId};
1617
use crate::ops::lockfile::LOCKFILE_NAME;
1718
use crate::ops::registry::{infer_registry, RegistryOrIndex};
@@ -20,20 +21,23 @@ use crate::sources::{PathSource, CRATES_IO_REGISTRY};
2021
use crate::util::cache_lock::CacheLockMode;
2122
use crate::util::context::JobsConfig;
2223
use crate::util::errors::CargoResult;
24+
use crate::util::human_readable_bytes;
25+
use crate::util::restricted_names;
2326
use crate::util::toml::prepare_for_publish;
24-
use crate::util::{
25-
self, human_readable_bytes, restricted_names, FileLock, Filesystem, GlobalContext, Graph,
26-
};
27+
use crate::util::FileLock;
28+
use crate::util::Filesystem;
29+
use crate::util::GlobalContext;
30+
use crate::util::Graph;
2731
use crate::{drop_println, ops};
2832
use anyhow::{bail, Context as _};
2933
use cargo_util::paths;
30-
use flate2::read::GzDecoder;
3134
use flate2::{Compression, GzBuilder};
32-
use tar::{Archive, Builder, EntryType, Header, HeaderMode};
35+
use tar::{Builder, EntryType, Header, HeaderMode};
3336
use tracing::debug;
3437
use unicase::Ascii as UncasedAscii;
3538

3639
mod vcs;
40+
mod verify;
3741

3842
#[derive(Clone)]
3943
pub struct PackageOpts<'gctx> {
@@ -250,7 +254,7 @@ fn do_package<'a>(
250254
// are already all in the local registry overlay.
251255
if opts.verify {
252256
for (pkg, opts, tarball) in &outputs {
253-
run_verify(ws, pkg, tarball, local_reg.as_ref(), opts)
257+
verify::run_verify(ws, pkg, tarball, local_reg.as_ref(), opts)
254258
.context("failed to verify package tarball")?
255259
}
256260
}
@@ -926,160 +930,6 @@ pub fn check_yanked(
926930
Ok(())
927931
}
928932

929-
fn run_verify(
930-
ws: &Workspace<'_>,
931-
pkg: &Package,
932-
tar: &FileLock,
933-
local_reg: Option<&TmpRegistry<'_>>,
934-
opts: &PackageOpts<'_>,
935-
) -> CargoResult<()> {
936-
let gctx = ws.gctx();
937-
938-
gctx.shell().status("Verifying", pkg)?;
939-
940-
tar.file().seek(SeekFrom::Start(0))?;
941-
let f = GzDecoder::new(tar.file());
942-
let dst = tar
943-
.parent()
944-
.join(&format!("{}-{}", pkg.name(), pkg.version()));
945-
if dst.exists() {
946-
paths::remove_dir_all(&dst)?;
947-
}
948-
let mut archive = Archive::new(f);
949-
// We don't need to set the Modified Time, as it's not relevant to verification
950-
// and it errors on filesystems that don't support setting a modified timestamp
951-
archive.set_preserve_mtime(false);
952-
archive.unpack(dst.parent().unwrap())?;
953-
954-
// Manufacture an ephemeral workspace to ensure that even if the top-level
955-
// package has a workspace we can still build our new crate.
956-
let id = SourceId::for_path(&dst)?;
957-
let mut src = PathSource::new(&dst, id, ws.gctx());
958-
let new_pkg = src.root_package()?;
959-
let pkg_fingerprint = hash_all(&dst)?;
960-
let mut ws = Workspace::ephemeral(new_pkg, gctx, None, true)?;
961-
if let Some(local_reg) = local_reg {
962-
ws.add_local_overlay(
963-
local_reg.upstream,
964-
local_reg.root.as_path_unlocked().to_owned(),
965-
);
966-
}
967-
968-
let rustc_args = if pkg
969-
.manifest()
970-
.unstable_features()
971-
.require(Feature::public_dependency())
972-
.is_ok()
973-
|| ws.gctx().cli_unstable().public_dependency
974-
{
975-
// FIXME: Turn this on at some point in the future
976-
//Some(vec!["-D exported_private_dependencies".to_string()])
977-
Some(vec![])
978-
} else {
979-
None
980-
};
981-
982-
let exec: Arc<dyn Executor> = Arc::new(DefaultExecutor);
983-
ops::compile_with_exec(
984-
&ws,
985-
&ops::CompileOptions {
986-
build_config: BuildConfig::new(
987-
gctx,
988-
opts.jobs.clone(),
989-
opts.keep_going,
990-
&opts.targets,
991-
CompileMode::Build,
992-
)?,
993-
cli_features: opts.cli_features.clone(),
994-
spec: ops::Packages::Packages(Vec::new()),
995-
filter: ops::CompileFilter::Default {
996-
required_features_filterable: true,
997-
},
998-
target_rustdoc_args: None,
999-
target_rustc_args: rustc_args,
1000-
target_rustc_crate_types: None,
1001-
rustdoc_document_private_items: false,
1002-
honor_rust_version: None,
1003-
},
1004-
&exec,
1005-
)?;
1006-
1007-
// Check that `build.rs` didn't modify any files in the `src` directory.
1008-
let ws_fingerprint = hash_all(&dst)?;
1009-
if pkg_fingerprint != ws_fingerprint {
1010-
let changes = report_hash_difference(&pkg_fingerprint, &ws_fingerprint);
1011-
anyhow::bail!(
1012-
"Source directory was modified by build.rs during cargo publish. \
1013-
Build scripts should not modify anything outside of OUT_DIR.\n\
1014-
{}\n\n\
1015-
To proceed despite this, pass the `--no-verify` flag.",
1016-
changes
1017-
)
1018-
}
1019-
1020-
Ok(())
1021-
}
1022-
1023-
fn hash_all(path: &Path) -> CargoResult<HashMap<PathBuf, u64>> {
1024-
fn wrap(path: &Path) -> CargoResult<HashMap<PathBuf, u64>> {
1025-
let mut result = HashMap::new();
1026-
let walker = walkdir::WalkDir::new(path).into_iter();
1027-
for entry in walker.filter_entry(|e| !(e.depth() == 1 && e.file_name() == "target")) {
1028-
let entry = entry?;
1029-
let file_type = entry.file_type();
1030-
if file_type.is_file() {
1031-
let file = File::open(entry.path())?;
1032-
let hash = util::hex::hash_u64_file(&file)?;
1033-
result.insert(entry.path().to_path_buf(), hash);
1034-
} else if file_type.is_symlink() {
1035-
let hash = util::hex::hash_u64(&fs::read_link(entry.path())?);
1036-
result.insert(entry.path().to_path_buf(), hash);
1037-
} else if file_type.is_dir() {
1038-
let hash = util::hex::hash_u64(&());
1039-
result.insert(entry.path().to_path_buf(), hash);
1040-
}
1041-
}
1042-
Ok(result)
1043-
}
1044-
let result = wrap(path).with_context(|| format!("failed to verify output at {:?}", path))?;
1045-
Ok(result)
1046-
}
1047-
1048-
fn report_hash_difference(orig: &HashMap<PathBuf, u64>, after: &HashMap<PathBuf, u64>) -> String {
1049-
let mut changed = Vec::new();
1050-
let mut removed = Vec::new();
1051-
for (key, value) in orig {
1052-
match after.get(key) {
1053-
Some(after_value) => {
1054-
if value != after_value {
1055-
changed.push(key.to_string_lossy());
1056-
}
1057-
}
1058-
None => removed.push(key.to_string_lossy()),
1059-
}
1060-
}
1061-
let mut added: Vec<_> = after
1062-
.keys()
1063-
.filter(|key| !orig.contains_key(*key))
1064-
.map(|key| key.to_string_lossy())
1065-
.collect();
1066-
let mut result = Vec::new();
1067-
if !changed.is_empty() {
1068-
changed.sort_unstable();
1069-
result.push(format!("Changed: {}", changed.join("\n\t")));
1070-
}
1071-
if !added.is_empty() {
1072-
added.sort_unstable();
1073-
result.push(format!("Added: {}", added.join("\n\t")));
1074-
}
1075-
if !removed.is_empty() {
1076-
removed.sort_unstable();
1077-
result.push(format!("Removed: {}", removed.join("\n\t")));
1078-
}
1079-
assert!(!result.is_empty(), "unexpected empty change detection");
1080-
result.join("\n")
1081-
}
1082-
1083933
// It can often be the case that files of a particular name on one platform
1084934
// can't actually be created on another platform. For example files with colons
1085935
// in the name are allowed on Unix but not on Windows.

0 commit comments

Comments
 (0)