mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-25 11:14:46 +00:00

Implement --explicit-version from standalone cargo-vendor. This helps with vendoring performance as it avoids redundantly deleting and re-copying already vendored packages. For example, when re-vendoring cargo's dependencies it makes a big improvement on wallclock time. For initial vendoring it makes no difference, but re-vendoring (ie, when most or all dependencies haven't changed) without explicit versions is actually slightly slower (5.8s -> 6s), but with explicit versions it goes from 5.8s -> 1.6s. Timings: Without explicit versions, initial vendor real 0m5.810s user 0m0.924s sys 0m2.491s Re-vendor: real 0m6.083s user 0m0.937s sys 0m2.654s With explicit versions, initial vendor: real 0m5.810s user 0m0.937s sys 0m2.461s Re-vendor: real 0m1.567s user 0m0.578s sys 0m0.967s The summaries of syscalls executed shows why: Revendoring without explicit versions: ``` % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 25.17 1.104699 18 59432 1065 openat 19.86 0.871574 21 41156 13825 unlink 13.64 0.598739 2 210510 lstat 9.02 0.395948 29 13208 copy_file_range 8.00 0.351242 11 30245 read 6.36 0.279005 3 72487 4476 statx 5.35 0.235027 6 37219 write 4.02 0.176267 3 58368 close ``` with explicit versions: ``` 29.38 0.419068 15 27798 13825 unlink 25.52 0.364021 1 209586 lstat 20.67 0.294788 16 17967 1032 openat 10.42 0.148586 4 35646 write 3.53 0.050350 3 13825 chmod 3.14 0.044786 2 16701 1622 statx 2.19 0.031171 1 16936 close 1.86 0.026538 24 1078 rmdir ``` Specifically, there are a lot fewer opens, copy_file_ranges, and unlinks.
587 lines
14 KiB
Rust
587 lines
14 KiB
Rust
//! Tests for the `cargo vendor` command.
|
|
|
|
use cargo_test_support::git;
|
|
use cargo_test_support::registry::Package;
|
|
use cargo_test_support::{basic_lib_manifest, project, Project};
|
|
|
|
#[cargo_test]
|
|
fn vendor_simple() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
log = "0.3.5"
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
|
|
Package::new("log", "0.3.5").publish();
|
|
|
|
p.cargo("vendor --respect-source-config").run();
|
|
let lock = p.read_file("vendor/log/Cargo.toml");
|
|
assert!(lock.contains("version = \"0.3.5\""));
|
|
|
|
add_vendor_config(&p);
|
|
p.cargo("build").run();
|
|
}
|
|
|
|
fn add_vendor_config(p: &Project) {
|
|
p.change_file(
|
|
".cargo/config",
|
|
r#"
|
|
[source.crates-io]
|
|
replace-with = 'vendor'
|
|
|
|
[source.vendor]
|
|
directory = 'vendor'
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn two_versions() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
bitflags = "0.8.0"
|
|
bar = { path = "bar" }
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.file(
|
|
"bar/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
bitflags = "0.7.0"
|
|
"#,
|
|
)
|
|
.file("bar/src/lib.rs", "")
|
|
.build();
|
|
|
|
Package::new("bitflags", "0.7.0").publish();
|
|
Package::new("bitflags", "0.8.0").publish();
|
|
|
|
p.cargo("vendor --respect-source-config").run();
|
|
|
|
let lock = p.read_file("vendor/bitflags/Cargo.toml");
|
|
assert!(lock.contains("version = \"0.8.0\""));
|
|
let lock = p.read_file("vendor/bitflags-0.7.0/Cargo.toml");
|
|
assert!(lock.contains("version = \"0.7.0\""));
|
|
|
|
add_vendor_config(&p);
|
|
p.cargo("build").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn two_explicit_versions() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
bitflags = "0.8.0"
|
|
bar = { path = "bar" }
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.file(
|
|
"bar/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
bitflags = "0.7.0"
|
|
"#,
|
|
)
|
|
.file("bar/src/lib.rs", "")
|
|
.build();
|
|
|
|
Package::new("bitflags", "0.7.0").publish();
|
|
Package::new("bitflags", "0.8.0").publish();
|
|
|
|
p.cargo("vendor --respect-source-config --explicit-version")
|
|
.run();
|
|
|
|
let lock = p.read_file("vendor/bitflags-0.8.0/Cargo.toml");
|
|
assert!(lock.contains("version = \"0.8.0\""));
|
|
let lock = p.read_file("vendor/bitflags-0.7.0/Cargo.toml");
|
|
assert!(lock.contains("version = \"0.7.0\""));
|
|
|
|
add_vendor_config(&p);
|
|
p.cargo("build").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn help() {
|
|
let p = project().build();
|
|
p.cargo("vendor -h").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn update_versions() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
bitflags = "0.7.0"
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
|
|
Package::new("bitflags", "0.7.0").publish();
|
|
Package::new("bitflags", "0.8.0").publish();
|
|
|
|
p.cargo("vendor --respect-source-config").run();
|
|
|
|
let lock = p.read_file("vendor/bitflags/Cargo.toml");
|
|
assert!(lock.contains("version = \"0.7.0\""));
|
|
|
|
p.change_file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
bitflags = "0.8.0"
|
|
"#,
|
|
);
|
|
p.cargo("vendor --respect-source-config").run();
|
|
|
|
let lock = p.read_file("vendor/bitflags/Cargo.toml");
|
|
assert!(lock.contains("version = \"0.8.0\""));
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn two_lockfiles() {
|
|
let p = project()
|
|
.no_manifest()
|
|
.file(
|
|
"foo/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
bitflags = "=0.7.0"
|
|
"#,
|
|
)
|
|
.file("foo/src/lib.rs", "")
|
|
.file(
|
|
"bar/Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "bar"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
bitflags = "=0.8.0"
|
|
"#,
|
|
)
|
|
.file("bar/src/lib.rs", "")
|
|
.build();
|
|
|
|
Package::new("bitflags", "0.7.0").publish();
|
|
Package::new("bitflags", "0.8.0").publish();
|
|
|
|
p.cargo("vendor --respect-source-config -s bar/Cargo.toml --manifest-path foo/Cargo.toml")
|
|
.run();
|
|
|
|
let lock = p.read_file("vendor/bitflags/Cargo.toml");
|
|
assert!(lock.contains("version = \"0.8.0\""));
|
|
let lock = p.read_file("vendor/bitflags-0.7.0/Cargo.toml");
|
|
assert!(lock.contains("version = \"0.7.0\""));
|
|
|
|
add_vendor_config(&p);
|
|
p.cargo("build").cwd("foo").run();
|
|
p.cargo("build").cwd("bar").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn delete_old_crates() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
bitflags = "=0.7.0"
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
|
|
Package::new("bitflags", "0.7.0").publish();
|
|
Package::new("log", "0.3.5").publish();
|
|
|
|
p.cargo("vendor --respect-source-config").run();
|
|
p.read_file("vendor/bitflags/Cargo.toml");
|
|
|
|
p.change_file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
log = "=0.3.5"
|
|
"#,
|
|
);
|
|
|
|
p.cargo("vendor --respect-source-config").run();
|
|
let lock = p.read_file("vendor/log/Cargo.toml");
|
|
assert!(lock.contains("version = \"0.3.5\""));
|
|
assert!(!p.root().join("vendor/bitflags/Cargo.toml").exists());
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn ignore_files() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
url = "1.4.1"
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
|
|
Package::new("url", "1.4.1")
|
|
.file("src/lib.rs", "")
|
|
.file("foo.orig", "")
|
|
.file(".gitignore", "")
|
|
.file(".gitattributes", "")
|
|
.file("foo.rej", "")
|
|
.publish();
|
|
|
|
p.cargo("vendor --respect-source-config").run();
|
|
let csum = p.read_file("vendor/url/.cargo-checksum.json");
|
|
assert!(!csum.contains("foo.orig"));
|
|
assert!(!csum.contains(".gitignore"));
|
|
assert!(!csum.contains(".gitattributes"));
|
|
assert!(!csum.contains(".cargo-ok"));
|
|
assert!(!csum.contains("foo.rej"));
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn included_files_only() {
|
|
let git = git::new("a", |p| {
|
|
p.file("Cargo.toml", &basic_lib_manifest("a"))
|
|
.file("src/lib.rs", "")
|
|
.file(".gitignore", "a")
|
|
.file("a/b.md", "")
|
|
});
|
|
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
&format!(
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
a = {{ git = '{}' }}
|
|
"#,
|
|
git.url()
|
|
),
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("vendor --respect-source-config").run();
|
|
let csum = p.read_file("vendor/a/.cargo-checksum.json");
|
|
assert!(!csum.contains("a/b.md"));
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn dependent_crates_in_crates() {
|
|
let git = git::new("a", |p| {
|
|
p.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
b = { path = 'b' }
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.file("b/Cargo.toml", &basic_lib_manifest("b"))
|
|
.file("b/src/lib.rs", "")
|
|
});
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
&format!(
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
a = {{ git = '{}' }}
|
|
"#,
|
|
git.url()
|
|
),
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("vendor --respect-source-config").run();
|
|
p.read_file("vendor/a/.cargo-checksum.json");
|
|
p.read_file("vendor/b/.cargo-checksum.json");
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn vendoring_git_crates() {
|
|
let git = git::new("git", |p| {
|
|
p.file("Cargo.toml", &basic_lib_manifest("serde_derive"))
|
|
.file("src/lib.rs", "")
|
|
.file("src/wut.rs", "")
|
|
});
|
|
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
&format!(
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies.serde]
|
|
version = "0.5.0"
|
|
|
|
[dependencies.serde_derive]
|
|
version = "0.5.0"
|
|
|
|
[patch.crates-io]
|
|
serde_derive = {{ git = '{}' }}
|
|
"#,
|
|
git.url()
|
|
),
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
Package::new("serde", "0.5.0")
|
|
.dep("serde_derive", "0.5")
|
|
.publish();
|
|
Package::new("serde_derive", "0.5.0").publish();
|
|
|
|
p.cargo("vendor --respect-source-config").run();
|
|
p.read_file("vendor/serde_derive/src/wut.rs");
|
|
|
|
add_vendor_config(&p);
|
|
p.cargo("build").run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn git_simple() {
|
|
let git = git::new("git", |p| {
|
|
p.file("Cargo.toml", &basic_lib_manifest("a"))
|
|
.file("src/lib.rs", "")
|
|
});
|
|
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
&format!(
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
a = {{ git = '{}' }}
|
|
"#,
|
|
git.url()
|
|
),
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
|
|
p.cargo("vendor --respect-source-config").run();
|
|
let csum = p.read_file("vendor/a/.cargo-checksum.json");
|
|
assert!(csum.contains("\"package\":null"));
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn git_duplicate() {
|
|
let git = git::new("a", |p| {
|
|
p.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "a"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
b = { path = 'b' }
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.file("b/Cargo.toml", &basic_lib_manifest("b"))
|
|
.file("b/src/lib.rs", "")
|
|
});
|
|
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
&format!(
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
a = {{ git = '{}' }}
|
|
b = '0.5.0'
|
|
|
|
"#,
|
|
git.url()
|
|
),
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
Package::new("b", "0.5.0").publish();
|
|
|
|
p.cargo("vendor --respect-source-config")
|
|
.with_stderr(
|
|
"\
|
|
[UPDATING] [..]
|
|
[UPDATING] [..]
|
|
[DOWNLOADING] [..]
|
|
[DOWNLOADED] [..]
|
|
error: failed to sync
|
|
|
|
Caused by:
|
|
found duplicate version of package `b v0.5.0` vendored from two sources:
|
|
|
|
<tab>source 1: [..]
|
|
<tab>source 2: [..]
|
|
",
|
|
)
|
|
.with_status(101)
|
|
.run();
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn depend_on_vendor_dir_not_deleted() {
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
libc = "0.2.30"
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
|
|
Package::new("libc", "0.2.30").publish();
|
|
|
|
p.cargo("vendor --respect-source-config").run();
|
|
assert!(p.root().join("vendor/libc").is_dir());
|
|
|
|
p.change_file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "0.1.0"
|
|
|
|
[dependencies]
|
|
libc = "0.2.30"
|
|
|
|
[patch.crates-io]
|
|
libc = { path = 'vendor/libc' }
|
|
"#,
|
|
);
|
|
|
|
p.cargo("vendor --respect-source-config").run();
|
|
assert!(p.root().join("vendor/libc").is_dir());
|
|
}
|
|
|
|
#[cargo_test]
|
|
fn ignore_hidden() {
|
|
// Don't delete files starting with `.`
|
|
Package::new("bar", "0.1.0").publish();
|
|
let p = project()
|
|
.file(
|
|
"Cargo.toml",
|
|
r#"
|
|
[package]
|
|
name = "foo"
|
|
version = "1.0.0"
|
|
[dependencies]
|
|
bar = "0.1.0"
|
|
"#,
|
|
)
|
|
.file("src/lib.rs", "")
|
|
.build();
|
|
p.cargo("vendor --respect-source-config").run();
|
|
// Add a `.git` directory.
|
|
let repo = git::init(&p.root().join("vendor"));
|
|
git::add(&repo);
|
|
git::commit(&repo);
|
|
assert!(p.root().join("vendor/.git").exists());
|
|
// Vendor again, shouldn't change anything.
|
|
p.cargo("vendor --respect-source-config").run();
|
|
// .git should not be removed.
|
|
assert!(p.root().join("vendor/.git").exists());
|
|
// And just for good measure, make sure no files changed.
|
|
let mut opts = git2::StatusOptions::new();
|
|
assert!(repo
|
|
.statuses(Some(&mut opts))
|
|
.unwrap()
|
|
.iter()
|
|
.all(|status| status.status() == git2::Status::CURRENT));
|
|
}
|