//! Tests for the `cargo vendor` command. //! //! Note that every test here uses `--respect-source-config` so that the //! "fake" crates.io is used. Otherwise `vendor` would download the crates.io //! index from the network. use std::fs; use cargo_test_support::compare::assert_e2e; use cargo_test_support::git; use cargo_test_support::prelude::*; use cargo_test_support::registry::{self, Package, RegistryBuilder}; use cargo_test_support::str; use cargo_test_support::{basic_lib_manifest, basic_manifest, paths, 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_crates_io_vendor_config(&p); p.cargo("check").run(); } #[cargo_test] fn vendor_sample_config() { 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") .with_stdout_data(str![[r#" [source.crates-io] replace-with = "vendored-sources" [source.vendored-sources] directory = "vendor" "#]]) .run(); } #[cargo_test] fn vendor_sample_config_alt_registry() { let registry = RegistryBuilder::new().alternative().http_index().build(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] log = { version = "0.3.5", registry = "alternative" } "#, ) .file("src/lib.rs", "") .build(); Package::new("log", "0.3.5").alternative(true).publish(); p.cargo("vendor --respect-source-config") .with_stdout_data(format!( r#"[source."{0}"] registry = "{0}" replace-with = "vendored-sources" [source.vendored-sources] directory = "vendor" "#, registry.index_url() )) .run(); } #[cargo_test] fn vendor_path_specified() { 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(); let path = if cfg!(windows) { r#"deps\.vendor"# } else { "deps/.vendor" }; let output = p .cargo("vendor --respect-source-config") .arg(path) .exec_with_output() .unwrap(); // Assert against original output to ensure that // path is normalized by `ops::vendor` on Windows. assert_eq!( &String::from_utf8(output.stdout).unwrap(), r#"[source.crates-io] replace-with = "vendored-sources" [source.vendored-sources] directory = "deps/.vendor" "# ); let lock = p.read_file("deps/.vendor/log/Cargo.toml"); assert!(lock.contains("version = \"0.3.5\"")); } fn add_crates_io_vendor_config(p: &Project) { p.change_file( ".cargo/config.toml", r#" [source.crates-io] replace-with = 'vendor' [source.vendor] directory = 'vendor' "#, ); } fn add_git_vendor_config(p: &Project, git_project: &Project) { p.change_file( ".cargo/config.toml", &format!( r#" [source."git+{url}"] git = "{url}" replace-with = 'vendor' [source.vendor] directory = 'vendor' "#, url = git_project.url() ), ); } #[cargo_test] fn package_exclude() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); Package::new("bar", "0.1.0") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.1.0" exclude = [".*", "!.include", "!.dotdir/include"] "#, ) .file("src/lib.rs", "") .file(".exclude", "") .file(".include", "") .file(".dotdir/exclude", "") .file(".dotdir/include", "") .publish(); p.cargo("vendor --respect-source-config").run(); let csum = p.read_file("vendor/bar/.cargo-checksum.json"); assert!(csum.contains(".include")); assert!(!csum.contains(".exclude")); assert!(!csum.contains(".dotdir/exclude")); // Gitignore doesn't re-include a file in an excluded parent directory, // even if negating it explicitly. assert!(!csum.contains(".dotdir/include")); } #[cargo_test] fn discovery_inferred_build_rs_included() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs", "build.rs"] "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config").run(); add_git_vendor_config(&p, &git_project); let lock = p.read_file("vendor/dep/Cargo.toml"); assert_e2e().eq( lock, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "dep" version = "0.0.1" authors = [] build = "build.rs" include = [ "src/lib.rs", "build.rs", ] autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "dep" path = "src/lib.rs" "##]], ); p.cargo("check").run(); } #[cargo_test] fn discovery_inferred_build_rs_excluded() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs"] "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config").run(); add_git_vendor_config(&p, &git_project); let lock = p.read_file("vendor/dep/Cargo.toml"); assert_e2e().eq( lock, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "dep" version = "0.0.1" authors = [] build = false include = ["src/lib.rs"] autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "dep" path = "src/lib.rs" "##]], ); p.cargo("check").run(); } #[cargo_test] fn discovery_inferred_lib_included() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/main.rs", "src/lib.rs"] "#, ) .file("src/main.rs", "fn main() {}") .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config").run(); add_git_vendor_config(&p, &git_project); let lock = p.read_file("vendor/dep/Cargo.toml"); assert_e2e().eq( lock, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "dep" version = "0.0.1" authors = [] build = false include = [ "src/main.rs", "src/lib.rs", ] autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "dep" path = "src/lib.rs" [[bin]] name = "dep" path = "src/main.rs" "##]], ); p.cargo("check").run(); } #[cargo_test] fn discovery_inferred_lib_excluded() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/main.rs"] "#, ) .file("src/main.rs", "fn main() {}") .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config").run(); add_git_vendor_config(&p, &git_project); let lock = p.read_file("vendor/dep/Cargo.toml"); assert_e2e().eq( lock, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "dep" version = "0.0.1" authors = [] build = false include = ["src/main.rs"] autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [[bin]] name = "dep" path = "src/main.rs" "##]], ); p.cargo("check").run(); } #[cargo_test] fn discovery_inferred_other_included() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs", "src/bin/foo/main.rs", "examples/example_foo.rs", "tests/test_foo.rs", "benches/bench_foo.rs"] "#, ) .file("src/lib.rs", "") .file("src/bin/foo/main.rs", "fn main() {}") .file("examples/example_foo.rs", "fn main() {}") .file("tests/test_foo.rs", "fn main() {}") .file("benches/bench_foo.rs", "fn main() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config").run(); add_git_vendor_config(&p, &git_project); let lock = p.read_file("vendor/dep/Cargo.toml"); assert_e2e().eq( lock, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "dep" version = "0.0.1" authors = [] build = false include = [ "src/lib.rs", "src/bin/foo/main.rs", "examples/example_foo.rs", "tests/test_foo.rs", "benches/bench_foo.rs", ] autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "dep" path = "src/lib.rs" [[bin]] name = "foo" path = "src/bin/foo/main.rs" [[example]] name = "example_foo" path = "examples/example_foo.rs" [[test]] name = "test_foo" path = "tests/test_foo.rs" [[bench]] name = "bench_foo" path = "benches/bench_foo.rs" "##]], ); p.cargo("check").run(); } #[cargo_test] fn discovery_inferred_other_excluded() { let git_project = git::new("dep", |project| { project .file( "Cargo.toml", r#" [package] name = "dep" version = "0.0.1" edition = "2015" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] include = ["src/lib.rs"] "#, ) .file("src/lib.rs", "") .file("src/bin/foo/main.rs", "fn main() {}") .file("examples/example_foo.rs", "fn main() {}") .file("tests/test_foo.rs", "fn main() {}") .file("benches/bench_foo.rs", "fn main() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.5.0" edition = "2015" [dependencies.dep] git = '{}' "#, git_project.url() ), ) .file("src/main.rs", "fn main() {}") .build(); p.cargo("vendor --respect-source-config").run(); add_git_vendor_config(&p, &git_project); let lock = p.read_file("vendor/dep/Cargo.toml"); assert_e2e().eq( lock, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2015" name = "dep" version = "0.0.1" authors = [] build = false include = ["src/lib.rs"] autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "dep" path = "src/lib.rs" "##]], ); p.cargo("check").run(); } #[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_crates_io_vendor_config(&p); p.cargo("check").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 --versioned-dirs") .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_crates_io_vendor_config(&p); p.cargo("check").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_crates_io_vendor_config(&p); p.cargo("check").cwd("foo").run(); p.cargo("check").cwd("bar").run(); } #[cargo_test] fn test_sync_argument() { 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", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" [dependencies] bitflags = "=0.8.0" "#, ) .file("baz/src/lib.rs", "") .build(); Package::new("bitflags", "0.7.0").publish(); Package::new("bitflags", "0.8.0").publish(); p.cargo("vendor --respect-source-config --manifest-path foo/Cargo.toml -s bar/Cargo.toml baz/Cargo.toml test_vendor") .with_stderr_data(str![[r#" [ERROR] unexpected argument 'test_vendor' found Usage: cargo[EXE] vendor [OPTIONS] [path] For more information, try '--help'. "#]] ) .with_status(1) .run(); p.cargo("vendor --respect-source-config --manifest-path foo/Cargo.toml -s bar/Cargo.toml -s baz/Cargo.toml test_vendor") .run(); let lock = p.read_file("test_vendor/bitflags/Cargo.toml"); assert!(lock.contains("version = \"0.8.0\"")); let lock = p.read_file("test_vendor/bitflags-0.7.0/Cargo.toml"); assert!(lock.contains("version = \"0.7.0\"")); } #[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_crates_io_vendor_config(&p); p.cargo("check").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_diff_rev() { let (git_project, git_repo) = git::new_repo("git", |p| { p.file("Cargo.toml", &basic_manifest("a", "0.1.0")) .file("src/lib.rs", "") }); let url = git_project.url(); let ref_1 = "v0.1.0"; let ref_2 = "v0.2.0"; git::tag(&git_repo, ref_1); git_project.change_file("Cargo.toml", &basic_manifest("a", "0.2.0")); git::add(&git_repo); git::commit(&git_repo); git::tag(&git_repo, ref_2); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] a_1 = {{ package = "a", git = '{url}', rev = '{ref_1}' }} a_2 = {{ package = "a", git = '{url}', rev = '{ref_2}' }} "# ), ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config") .with_stdout_data(str![[r#" [source."git+[ROOTURL]/git?rev=v0.1.0"] git = "[ROOTURL]/git" rev = "v0.1.0" replace-with = "vendored-sources" [source."git+[ROOTURL]/git?rev=v0.2.0"] git = "[ROOTURL]/git" rev = "v0.2.0" replace-with = "vendored-sources" [source.vendored-sources] directory = "vendor" "#]]) .run(); } #[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_data(str![[r#" [UPDATING] git repository `[ROOTURL]/a` [UPDATING] `dummy-registry` index [LOCKING] 4 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] b v0.5.0 (registry `dummy-registry`) [ERROR] failed to sync Caused by: found duplicate version of package `b v0.5.0` vendored from two sources: source 1: registry `crates-io` source 2: [ROOTURL]/a#[..] "#]]) .with_status(101) .run(); } #[cargo_test] fn git_complex() { let git_b = git::new("git_b", |p| { p.file( "Cargo.toml", r#" [package] name = "b" version = "0.1.0" edition = "2021" [dependencies] dep_b = { path = 'dep_b' } "#, ) .file("src/lib.rs", "") .file("dep_b/Cargo.toml", &basic_lib_manifest("dep_b")) .file("dep_b/src/lib.rs", "") }); let git_a = git::new("git_a", |p| { p.file( "Cargo.toml", &format!( r#" [package] name = "a" version = "0.1.0" edition = "2021" [dependencies] b = {{ git = '{}' }} dep_a = {{ path = 'dep_a' }} "#, git_b.url() ), ) .file("src/lib.rs", "") .file("dep_a/Cargo.toml", &basic_lib_manifest("dep_a")) .file("dep_a/src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] a = {{ git = '{}' }} "#, git_a.url() ), ) .file("src/lib.rs", "") .build(); let output = p .cargo("vendor --respect-source-config") .exec_with_output() .unwrap(); let output = String::from_utf8(output.stdout).unwrap(); p.change_file(".cargo/config.toml", &output); p.cargo("check -v") .with_stderr_data( str![[r#" [CHECKING] dep_b v0.5.0 ([ROOTURL]/git_b#[..]) [CHECKING] dep_a v0.5.0 ([ROOTURL]/git_a#[..]) [RUNNING] `rustc [..] [ROOT]/foo/vendor/dep_b/src/lib.rs [..]` [RUNNING] `rustc [..] [ROOT]/foo/vendor/dep_a/src/lib.rs [..]` [CHECKING] b v0.1.0 ([ROOTURL]/git_b#[..]) [RUNNING] `rustc [..] [ROOT]/foo/vendor/b/src/lib.rs [..]` [CHECKING] a v0.1.0 ([ROOTURL]/git_a#[..]) [RUNNING] `rustc [..] [ROOT]/foo/vendor/a/src/lib.rs [..]` [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); } #[cargo_test] fn git_deterministic() { let git_dep = git::new("git_dep", |p| { p.file( "Cargo.toml", r#" [package] name = "git_dep" version = "0.0.1" edition = "2021" license = "MIT" description = "foo" documentation = "docs.rs/foo" authors = [] [[example]] name = "c" [[example]] name = "b" [[example]] name = "a" "#, ) .file("src/lib.rs", "") .file("examples/z.rs", "fn main() {}") .file("examples/y.rs", "fn main() {}") .file("examples/x.rs", "fn main() {}") .file("examples/c.rs", "fn main() {}") .file("examples/b.rs", "fn main() {}") .file("examples/a.rs", "fn main() {}") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] git_dep = {{ git = '{}' }} "#, git_dep.url() ), ) .file("src/lib.rs", "") .build(); let output = p .cargo("vendor --respect-source-config") .exec_with_output() .unwrap(); let output = String::from_utf8(output.stdout).unwrap(); p.change_file(".cargo/config.toml", &output); let git_dep_manifest = p.read_file("vendor/git_dep/Cargo.toml"); assert_e2e().eq( git_dep_manifest, str![[r##" # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "git_dep" version = "0.0.1" authors = [] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" documentation = "docs.rs/foo" readme = false license = "MIT" [lib] name = "git_dep" path = "src/lib.rs" [[example]] name = "a" path = [..] [[example]] name = "b" path = [..] [[example]] name = "c" path = [..] [[example]] name = "x" path = [..] [[example]] name = "y" path = [..] [[example]] name = "z" path = [..] "##]], ); } #[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)); } #[cargo_test] fn config_instructions_works() { // Check that the config instructions work for all dependency kinds. registry::alt_init(); Package::new("dep", "0.1.0").publish(); Package::new("altdep", "0.1.0").alternative(true).publish(); let git_project = git::new("gitdep", |project| { project .file("Cargo.toml", &basic_lib_manifest("gitdep")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] dep = "0.1" altdep = {{version="0.1", registry="alternative"}} gitdep = {{git='{}'}} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); let output = p .cargo("vendor --respect-source-config") .exec_with_output() .unwrap(); let output = String::from_utf8(output.stdout).unwrap(); p.change_file(".cargo/config.toml", &output); p.cargo("check -v") .with_stderr_data( str![[r#" [CHECKING] altdep v0.1.0 (registry `alternative`) [CHECKING] dep v0.1.0 [RUNNING] `rustc [..] [ROOT]/foo/vendor/altdep/src/lib.rs [..]` [RUNNING] `rustc [..] [ROOT]/foo/vendor/gitdep/src/lib.rs [..]` [RUNNING] `rustc [..] [ROOT]/foo/vendor/dep/src/lib.rs [..]` [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [CHECKING] gitdep v0.5.0 ([ROOTURL]/gitdep#[..]) "#]] .unordered(), ) .run(); } #[cargo_test] fn git_crlf_preservation() { // Check that newlines don't get changed when you vendor // (will only fail if your system is setup with core.autocrlf=true on windows) let input = "hello \nthere\nmy newline\nfriends"; let git_project = git::new("git", |p| { p.file("Cargo.toml", &basic_lib_manifest("a")) .file("src/lib.rs", input) }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] a = {{ git = '{}' }} "#, git_project.url() ), ) .file("src/lib.rs", "") .build(); fs::write( paths::home().join(".gitconfig"), r#" [core] autocrlf = true "#, ) .unwrap(); p.cargo("vendor --respect-source-config").run(); let output = p.read_file("vendor/a/src/lib.rs"); assert_eq!(input, output); } #[cargo_test] #[cfg(unix)] fn vendor_preserves_permissions() { use std::os::unix::fs::MetadataExt; Package::new("bar", "1.0.0") .file_with_mode("example.sh", 0o755, "#!/bin/sh") .file("src/lib.rs", "") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config").run(); let umask = cargo::util::get_umask(); let metadata = fs::metadata(p.root().join("vendor/bar/src/lib.rs")).unwrap(); assert_eq!(metadata.mode() & 0o777, 0o644 & !umask); let metadata = fs::metadata(p.root().join("vendor/bar/example.sh")).unwrap(); assert_eq!(metadata.mode() & 0o777, 0o755 & !umask); } #[cargo_test] fn no_remote_dependency_no_vendor() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] bar = { path = "bar" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("vendor") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions There is no dependency to vendor in this project. "#]]) .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 edition = "2021" "#, ) .file("bar/src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] bar = {{ git = '{}' }} "#, git.url() ), ) .file("src/lib.rs", "") .build(); p.cargo("vendor --respect-source-config").run(); p.change_file( ".cargo/config.toml", &format!( r#" [source."{}"] git = "{}" replace-with = "vendor" [source.vendor] directory = "vendor" "#, git.url(), git.url() ), ); p.cargo("check -v") .with_stderr_data(str![[r#" [CHECKING] bar v0.1.0 ([ROOTURL]/ws#[..]) [RUNNING] `rustc [..] [ROOT]/foo/vendor/bar/src/lib.rs [..]` [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] src/lib.rs [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); }