Skip to content

Commit

Permalink
fix(vendor): Make vendor work similar to package since it resolve…
Browse files Browse the repository at this point in the history
…s manifest correctly
  • Loading branch information
Muscraft committed Nov 23, 2022
1 parent ba607b2 commit dcaeaad
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 43 deletions.
20 changes: 10 additions & 10 deletions src/cargo/ops/cargo_package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,24 @@ pub struct PackageOpts<'cfg> {
const ORIGINAL_MANIFEST_FILE: &str = "Cargo.toml.orig";
const VCS_INFO_FILE: &str = ".cargo_vcs_info.json";

struct ArchiveFile {
pub struct ArchiveFile {
/// The relative path in the archive (not including the top-level package
/// name directory).
rel_path: PathBuf,
pub rel_path: PathBuf,
/// String variant of `rel_path`, for convenience.
rel_str: String,
pub rel_str: String,
/// The contents to add to the archive.
contents: FileContents,
pub contents: FileContents,
}

enum FileContents {
pub enum FileContents {
/// Absolute path to the file on disk to add to the archive.
OnDisk(PathBuf),
/// Generates a file.
Generated(GeneratedFile),
}

enum GeneratedFile {
pub enum GeneratedFile {
/// Generates `Cargo.toml` by rewriting the original.
Manifest,
/// Generates `Cargo.lock` in some cases (like if there is a binary).
Expand All @@ -67,14 +67,14 @@ enum GeneratedFile {
}

#[derive(Serialize)]
struct VcsInfo {
pub struct VcsInfo {
git: GitVcsInfo,
/// Path to the package within repo (empty string if root). / not \
path_in_vcs: String,
}

#[derive(Serialize)]
struct GitVcsInfo {
pub struct GitVcsInfo {
sha1: String,
}

Expand Down Expand Up @@ -219,7 +219,7 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Option
}

/// Builds list of files to archive.
fn build_ar_list(
pub fn build_ar_list(
ws: &Workspace<'_>,
pkg: &Package,
src_files: Vec<PathBuf>,
Expand Down Expand Up @@ -369,7 +369,7 @@ fn check_for_file_and_add(
}

/// Construct `Cargo.lock` for the package to be published.
fn build_lock(ws: &Workspace<'_>, orig_pkg: &Package) -> CargoResult<String> {
pub fn build_lock(ws: &Workspace<'_>, orig_pkg: &Package) -> CargoResult<String> {
let config = ws.config();
let orig_resolve = ops::load_pkg_lockfile(ws)?;

Expand Down
101 changes: 68 additions & 33 deletions src/cargo/ops/vendor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::core::shell::Verbosity;
use crate::core::{GitReference, Workspace};
use crate::core::{GitReference, Package, Workspace};
use crate::ops;
use crate::ops::cargo_package::{build_ar_list, build_lock, FileContents, GeneratedFile};
use crate::sources::path::PathSource;
use crate::sources::CRATES_IO_REGISTRY;
use crate::util::{CargoResult, Config};
Expand Down Expand Up @@ -160,10 +161,13 @@ fn sync(
}
ids.insert(
pkg,
packages
.get_one(pkg)
.with_context(|| "failed to fetch package")?
.clone(),
(
ws,
packages
.get_one(pkg)
.with_context(|| "failed to fetch package")?
.clone(),
),
);

checksums.insert(pkg, resolve.checksums().get(&pkg).cloned());
Expand Down Expand Up @@ -191,7 +195,7 @@ fn sync(

let mut sources = BTreeSet::new();
let mut tmp_buf = [0; 64 * 1024];
for (id, pkg) in ids.iter() {
for (id, (ws, pkg)) in ids.iter() {
// Next up, copy it to the vendor directory
let src = pkg
.manifest_path()
Expand Down Expand Up @@ -225,7 +229,7 @@ fn sync(
let pathsource = PathSource::new(src, id.source_id(), config);
let paths = pathsource.list_files(pkg)?;
let mut map = BTreeMap::new();
cp_sources(src, &paths, &dst, &mut map, &mut tmp_buf)
cp_sources(ws, pkg, &paths, &dst, &mut map, &mut tmp_buf)
.with_context(|| format!("failed to copy over vendored sources for: {}", id))?;

// Finally, emit the metadata about this package
Expand Down Expand Up @@ -313,74 +317,105 @@ fn sync(
}

fn cp_sources(
src: &Path,
ws: &Workspace<'_>,
pkg: &Package,
paths: &[PathBuf],
dst: &Path,
cksums: &mut BTreeMap<String, String>,
tmp_buf: &mut [u8],
) -> CargoResult<()> {
for p in paths {
let relative = p.strip_prefix(&src).unwrap();

match relative.to_str() {
let ar_files = build_ar_list(ws, pkg, paths.to_vec(), None)?;
for file in ar_files {
match file.rel_str.as_str() {
// Skip git config files as they're not relevant to builds most of
// the time and if we respect them (e.g. in git) then it'll
// probably mess with the checksums when a vendor dir is checked
// into someone else's source control
Some(".gitattributes") | Some(".gitignore") | Some(".git") => continue,
".gitattributes" | ".gitignore" | ".git" => continue,

// Temporary Cargo files
Some(".cargo-ok") => continue,
".cargo-ok" => continue,

// Skip patch-style orig/rej files. Published crates on crates.io
// have `Cargo.toml.orig` which we don't want to use here and
// otherwise these are rarely used as part of the build process.
Some(filename) => {
filename => {
if filename.ends_with(".orig") || filename.ends_with(".rej") {
continue;
}
}
_ => {}
};

// Join pathname components individually to make sure that the joined
// path uses the correct directory separators everywhere, since
// `relative` may use Unix-style and `dst` may require Windows-style
// backslashes.
let dst = relative
let dst = file
.rel_path
.iter()
.fold(dst.to_owned(), |acc, component| acc.join(&component));

paths::create_dir_all(dst.parent().unwrap())?;

let cksum = copy_and_checksum(p, &dst, tmp_buf)?;
cksums.insert(relative.to_str().unwrap().replace("\\", "/"), cksum);
let mut dst_opts = OpenOptions::new();
dst_opts.write(true).create(true).truncate(true);
let cksum = match file.contents {
FileContents::OnDisk(disk_path) => {
let mut src = File::open(&disk_path)
.with_context(|| format!("failed to open {:?}", &disk_path))?;
#[cfg(unix)]
{
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
let src_metadata = src
.metadata()
.with_context(|| format!("failed to stat {:?}", disk_path))?;
dst_opts.mode(src_metadata.mode());
}
copy_and_checksum(
&dst,
&mut dst_opts,
&mut src,
disk_path.to_str().unwrap(),
tmp_buf,
)?
}
FileContents::Generated(generated_kind) => {
let contents = match generated_kind {
GeneratedFile::Manifest => pkg.to_registry_toml(ws)?,
GeneratedFile::Lockfile => build_lock(ws, pkg)?,
GeneratedFile::VcsInfo(ref s) => serde_json::to_string_pretty(s)?,
};
copy_and_checksum(
&dst,
&mut dst_opts,
&mut contents.as_bytes(),
"Generated File",
tmp_buf,
)?
}
};
cksums.insert(file.rel_str.replace("\\", "/"), cksum);
}
Ok(())
}

fn copy_and_checksum(src_path: &Path, dst_path: &Path, buf: &mut [u8]) -> CargoResult<String> {
let mut src = File::open(src_path).with_context(|| format!("failed to open {:?}", src_path))?;
let mut dst_opts = OpenOptions::new();
dst_opts.write(true).create(true).truncate(true);
#[cfg(unix)]
{
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
let src_metadata = src
.metadata()
.with_context(|| format!("failed to stat {:?}", src_path))?;
dst_opts.mode(src_metadata.mode());
}
fn copy_and_checksum<T: Read>(
dst_path: &Path,
dst_opts: &mut OpenOptions,
contents: &mut T,
contents_path: &str,
buf: &mut [u8],
) -> CargoResult<String> {
let mut dst = dst_opts
.open(dst_path)
.with_context(|| format!("failed to create {:?}", dst_path))?;
// Not going to bother setting mode on pre-existing files, since there
// shouldn't be any under normal conditions.
let mut cksum = Sha256::new();
loop {
let n = src
let n = contents
.read(buf)
.with_context(|| format!("failed to read from {:?}", src_path))?;
.with_context(|| format!("failed to read from {:?}", contents_path))?;
if n == 0 {
break Ok(cksum.finish_hex());
}
Expand Down
61 changes: 61 additions & 0 deletions tests/testsuite/vendor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -926,3 +926,64 @@ fn no_remote_dependency_no_vendor() {
.run();
assert!(!p.root().join("vendor").exists());
}

#[cargo_test]
fn vendor_crate_with_ws_inherit() {
let git = git::new("ws", |p| {
p.file(
"Cargo.toml",
r#"
[workspace]
members = ["bar"]
[workspace.package]
version = "0.1.0"
"#,
)
.file(
"bar/Cargo.toml",
r#"
[package]
name = "bar"
version.workspace = true
"#,
)
.file("bar/src/lib.rs", "")
});

let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = {{ git = '{}' }}
"#,
git.url()
),
)
.file("src/lib.rs", "")
.build();

p.cargo("vendor --respect-source-config").run();
p.change_file(
".cargo/config",
&format!(
r#"
[source."{}"]
git = "{}"
replace-with = "vendor"
[source.vendor]
directory = "vendor"
"#,
git.url(),
git.url()
),
);

p.cargo("build").run()
}

0 comments on commit dcaeaad

Please sign in to comment.