Skip to content

Commit 1316dc7

Browse files
committed
fix(vendor): Make vendor use Manifest's "original" Cargo.toml
1 parent 23424fd commit 1316dc7

File tree

2 files changed

+187
-18
lines changed

2 files changed

+187
-18
lines changed

src/cargo/ops/vendor.rs

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
use crate::core::package::MANIFEST_PREAMBLE;
12
use crate::core::shell::Verbosity;
2-
use crate::core::{GitReference, Workspace};
3+
use crate::core::{GitReference, Package, Workspace};
34
use crate::ops;
45
use crate::sources::path::PathSource;
56
use crate::sources::CRATES_IO_REGISTRY;
@@ -9,9 +10,10 @@ use cargo_util::{paths, Sha256};
910
use serde::Serialize;
1011
use std::collections::HashSet;
1112
use std::collections::{BTreeMap, BTreeSet, HashMap};
13+
use std::ffi::OsStr;
1214
use std::fs::{self, File, OpenOptions};
1315
use std::io::{Read, Write};
14-
use std::path::{Path, PathBuf};
16+
use std::path::{Display, Path, PathBuf};
1517
use toml_edit::easy as toml;
1618

1719
pub struct VendorOptions<'a> {
@@ -225,7 +227,7 @@ fn sync(
225227
let pathsource = PathSource::new(src, id.source_id(), config);
226228
let paths = pathsource.list_files(pkg)?;
227229
let mut map = BTreeMap::new();
228-
cp_sources(src, &paths, &dst, &mut map, &mut tmp_buf)
230+
cp_sources(pkg, src, &paths, &dst, &mut map, &mut tmp_buf)
229231
.with_context(|| format!("failed to copy over vendored sources for: {}", id))?;
230232

231233
// Finally, emit the metadata about this package
@@ -315,6 +317,7 @@ fn sync(
315317
}
316318

317319
fn cp_sources(
320+
pkg: &Package,
318321
src: &Path,
319322
paths: &[PathBuf],
320323
dst: &Path,
@@ -354,35 +357,65 @@ fn cp_sources(
354357
.fold(dst.to_owned(), |acc, component| acc.join(&component));
355358

356359
paths::create_dir_all(dst.parent().unwrap())?;
360+
let mut dst_opts = OpenOptions::new();
361+
dst_opts.write(true).create(true).truncate(true);
362+
// When vendoring git dependencies, the manifest has not been normalized like it would be
363+
// when published. This causes issue when the manifest is using workspace inheritance.
364+
// To get around this issue we use the "original" manifest after `{}.workspace = true`
365+
// has been resolved for git dependencies.
366+
let cksum = if dst.file_name() == Some(OsStr::new("Cargo.toml"))
367+
&& pkg.package_id().source_id().is_git()
368+
{
369+
let original_toml = toml::to_string_pretty(pkg.manifest().original())?;
370+
let contents = format!("{}\n{}", MANIFEST_PREAMBLE, original_toml);
371+
copy_and_checksum(
372+
&dst,
373+
&mut dst_opts,
374+
&mut contents.as_bytes(),
375+
"Generated Cargo.toml",
376+
tmp_buf,
377+
)?
378+
} else {
379+
let mut src = File::open(&p).with_context(|| format!("failed to open {:?}", &p))?;
380+
#[cfg(unix)]
381+
{
382+
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
383+
let src_metadata = src
384+
.metadata()
385+
.with_context(|| format!("failed to stat {:?}", p))?;
386+
dst_opts.mode(src_metadata.mode());
387+
}
388+
copy_and_checksum(
389+
&dst,
390+
&mut dst_opts,
391+
&mut src,
392+
&p.display().to_string(),
393+
tmp_buf,
394+
)?
395+
};
357396

358-
let cksum = copy_and_checksum(p, &dst, tmp_buf)?;
359397
cksums.insert(relative.to_str().unwrap().replace("\\", "/"), cksum);
360398
}
361399
Ok(())
362400
}
363401

364-
fn copy_and_checksum(src_path: &Path, dst_path: &Path, buf: &mut [u8]) -> CargoResult<String> {
365-
let mut src = File::open(src_path).with_context(|| format!("failed to open {:?}", src_path))?;
366-
let mut dst_opts = OpenOptions::new();
367-
dst_opts.write(true).create(true).truncate(true);
368-
#[cfg(unix)]
369-
{
370-
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
371-
let src_metadata = src
372-
.metadata()
373-
.with_context(|| format!("failed to stat {:?}", src_path))?;
374-
dst_opts.mode(src_metadata.mode());
375-
}
402+
fn copy_and_checksum<T: Read>(
403+
dst_path: &Path,
404+
dst_opts: &mut OpenOptions,
405+
contents: &mut T,
406+
contents_path: &str,
407+
buf: &mut [u8],
408+
) -> CargoResult<String> {
376409
let mut dst = dst_opts
377410
.open(dst_path)
378411
.with_context(|| format!("failed to create {:?}", dst_path))?;
379412
// Not going to bother setting mode on pre-existing files, since there
380413
// shouldn't be any under normal conditions.
381414
let mut cksum = Sha256::new();
382415
loop {
383-
let n = src
416+
let n = contents
384417
.read(buf)
385-
.with_context(|| format!("failed to read from {:?}", src_path))?;
418+
.with_context(|| format!("failed to read from {:?}", contents_path))?;
386419
if n == 0 {
387420
break Ok(cksum.finish_hex());
388421
}

tests/testsuite/vendor.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,79 @@ Caused by:
789789
.run();
790790
}
791791

792+
#[cargo_test]
793+
fn git_complex() {
794+
let git_b = git::new("git_b", |p| {
795+
p.file(
796+
"Cargo.toml",
797+
r#"
798+
[package]
799+
name = "b"
800+
version = "0.1.0"
801+
802+
[dependencies]
803+
dep_b = { path = 'dep_b' }
804+
"#,
805+
)
806+
.file("src/lib.rs", "")
807+
.file("dep_b/Cargo.toml", &basic_lib_manifest("dep_b"))
808+
.file("dep_b/src/lib.rs", "")
809+
});
810+
811+
let git_a = git::new("git_a", |p| {
812+
p.file(
813+
"Cargo.toml",
814+
&format!(
815+
r#"
816+
[package]
817+
name = "a"
818+
version = "0.1.0"
819+
820+
[dependencies]
821+
b = {{ git = '{}' }}
822+
dep_a = {{ path = 'dep_a' }}
823+
"#,
824+
git_b.url()
825+
),
826+
)
827+
.file("src/lib.rs", "")
828+
.file("dep_a/Cargo.toml", &basic_lib_manifest("dep_a"))
829+
.file("dep_a/src/lib.rs", "")
830+
});
831+
832+
let p = project()
833+
.file(
834+
"Cargo.toml",
835+
&format!(
836+
r#"
837+
[package]
838+
name = "foo"
839+
version = "0.1.0"
840+
841+
[dependencies]
842+
a = {{ git = '{}' }}
843+
"#,
844+
git_a.url()
845+
),
846+
)
847+
.file("src/lib.rs", "")
848+
.build();
849+
850+
let output = p
851+
.cargo("vendor --respect-source-config")
852+
.exec_with_output()
853+
.unwrap();
854+
let output = String::from_utf8(output.stdout).unwrap();
855+
p.change_file(".cargo/config", &output);
856+
857+
p.cargo("check -v")
858+
.with_stderr_contains("[..]foo/vendor/a/src/lib.rs[..]")
859+
.with_stderr_contains("[..]foo/vendor/dep_a/src/lib.rs[..]")
860+
.with_stderr_contains("[..]foo/vendor/b/src/lib.rs[..]")
861+
.with_stderr_contains("[..]foo/vendor/dep_b/src/lib.rs[..]")
862+
.run();
863+
}
864+
792865
#[cargo_test]
793866
fn depend_on_vendor_dir_not_deleted() {
794867
let p = project()
@@ -1015,3 +1088,66 @@ fn no_remote_dependency_no_vendor() {
10151088
.run();
10161089
assert!(!p.root().join("vendor").exists());
10171090
}
1091+
1092+
#[cargo_test]
1093+
fn vendor_crate_with_ws_inherit() {
1094+
let git = git::new("ws", |p| {
1095+
p.file(
1096+
"Cargo.toml",
1097+
r#"
1098+
[workspace]
1099+
members = ["bar"]
1100+
[workspace.package]
1101+
version = "0.1.0"
1102+
"#,
1103+
)
1104+
.file(
1105+
"bar/Cargo.toml",
1106+
r#"
1107+
[package]
1108+
name = "bar"
1109+
version.workspace = true
1110+
"#,
1111+
)
1112+
.file("bar/src/lib.rs", "")
1113+
});
1114+
1115+
let p = project()
1116+
.file(
1117+
"Cargo.toml",
1118+
&format!(
1119+
r#"
1120+
[package]
1121+
name = "foo"
1122+
version = "0.1.0"
1123+
1124+
[dependencies]
1125+
bar = {{ git = '{}' }}
1126+
"#,
1127+
git.url()
1128+
),
1129+
)
1130+
.file("src/lib.rs", "")
1131+
.build();
1132+
1133+
p.cargo("vendor --respect-source-config").run();
1134+
p.change_file(
1135+
".cargo/config",
1136+
&format!(
1137+
r#"
1138+
[source."{}"]
1139+
git = "{}"
1140+
replace-with = "vendor"
1141+
1142+
[source.vendor]
1143+
directory = "vendor"
1144+
"#,
1145+
git.url(),
1146+
git.url()
1147+
),
1148+
);
1149+
1150+
p.cargo("check")
1151+
.with_stderr_contains("[..]foo/vendor/bar/src/lib.rs[..]")
1152+
.run();
1153+
}

0 commit comments

Comments
 (0)