diff --git a/src/bin/cargo/commands/vendor.rs b/src/bin/cargo/commands/vendor.rs index 3f9c2dcaf3e..68a91b43287 100644 --- a/src/bin/cargo/commands/vendor.rs +++ b/src/bin/cargo/commands/vendor.rs @@ -32,6 +32,7 @@ pub fn cli() -> Command { "versioned-dirs", "Always include version in subdir name", )) + .arg(flag("include-path-deps", "Include path dependencies")) .arg(unsupported("no-merge-sources")) .arg(unsupported("relative-path")) .arg(unsupported("only-git-deps")) @@ -75,6 +76,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { no_delete: args.flag("no-delete"), destination: &path, versioned_dirs: args.flag("versioned-dirs"), + include_path_deps: args.flag("include-path-deps"), extra: args .get_many::("tomls") .unwrap_or_default() diff --git a/src/cargo/ops/vendor.rs b/src/cargo/ops/vendor.rs index 3ee46db3284..0555377ff10 100644 --- a/src/cargo/ops/vendor.rs +++ b/src/cargo/ops/vendor.rs @@ -18,6 +18,7 @@ use std::path::{Path, PathBuf}; pub struct VendorOptions<'a> { pub no_delete: bool, pub versioned_dirs: bool, + pub include_path_deps: bool, pub destination: &'a Path, pub extra: Vec, } @@ -62,6 +63,8 @@ struct VendorConfig { enum VendorSource { Directory { directory: String, + #[serde(rename = "replace-with", skip_serializing_if = "Option::is_none")] + replace_with: Option, }, Registry { registry: Option, @@ -153,12 +156,21 @@ fn sync( .get_many(resolve.iter()) .with_context(|| "failed to download packages")?; + let current = ws + .current() + .with_context(|| "using virtual manifest in workspace")? + .package_id(); for pkg in resolve.iter() { - // No need to vendor path crates since they're already in the - // repository - if pkg.source_id().is_path() { + // Since the target crate itself is also part of the packages, + // make sure we skip it. + if pkg == current { + continue; + } + + if pkg.source_id().is_path() && !opts.include_path_deps { continue; } + ids.insert( pkg, packages @@ -291,6 +303,11 @@ fn sync( rev, replace_with: merged_source_name.to_string(), } + } else if source_id.is_path() && opts.include_path_deps { + VendorSource::Directory { + directory: source_id.url().to_string(), + replace_with: Some(merged_source_name.to_string()), + } } else { panic!("Invalid source ID: {}", source_id) }; @@ -305,6 +322,7 @@ fn sync( // This backslash normalization is for making output paths more // cross-platform compatible. directory: opts.destination.to_string_lossy().replace("\\", "/"), + replace_with: None, }, ); } else if !dest_dir_already_exists { diff --git a/tests/testsuite/cargo_vendor/help/stdout.log b/tests/testsuite/cargo_vendor/help/stdout.log index 7f37ab56edd..94f212967f5 100644 --- a/tests/testsuite/cargo_vendor/help/stdout.log +++ b/tests/testsuite/cargo_vendor/help/stdout.log @@ -10,6 +10,7 @@ Options: -s, --sync Additional `Cargo.toml` to sync and vendor --respect-source-config Respect `[source]` config in `.cargo/config` --versioned-dirs Always include version in subdir name + --include-path-deps Include path dependencies -q, --quiet Do not print cargo log messages -v, --verbose... Use verbose output (-vv very verbose/build.rs output) --color Coloring: auto, always, never diff --git a/tests/testsuite/vendor.rs b/tests/testsuite/vendor.rs index 2b8b090c2e6..d1956bfbed2 100644 --- a/tests/testsuite/vendor.rs +++ b/tests/testsuite/vendor.rs @@ -1024,6 +1024,40 @@ fn git_crlf_preservation() { assert_eq!(input, output); } +#[cargo_test] +fn path_deps() { + let p = project() + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + "#, + ) + .file("bar/src/lib.rs", "") + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("vendor --respect-source-config").run(); + assert!(!p.root().join("vendor/bar").is_dir()); + + p.cargo("vendor --respect-source-config --include-path-deps") + .run(); + assert!(p.root().join("vendor/bar").is_dir()); +} + #[cargo_test] #[cfg(unix)] fn vendor_preserves_permissions() {