diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index 16667fcdf0b..58a1eb49dc6 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -206,7 +206,7 @@ fn check_metadata(pkg: &Package, config: &Config) -> CargoResult<()> { // Checks that the package dependencies are safe to deploy. fn verify_dependencies(pkg: &Package) -> CargoResult<()> { for dep in pkg.dependencies() { - if dep.source_id().is_path() && !dep.specified_req() { + if dep.source_id().is_path() && !dep.specified_req() && dep.is_transitive() { failure::bail!( "all path dependencies must have a version specified \ when packaging.\ndependency `{}` does not specify \ diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index 2b33abd70a8..d6e6178dd8b 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -116,6 +116,10 @@ fn verify_dependencies( for dep in pkg.dependencies().iter() { if dep.source_id().is_path() || dep.source_id().is_git() { if !dep.specified_req() { + if !dep.is_transitive() { + // dev-dependencies will be stripped in TomlManifest::prepare_for_publish + continue; + } let which = if dep.source_id().is_path() { "path" } else { @@ -172,6 +176,10 @@ fn transmit( let deps = pkg .dependencies() .iter() + .filter(|dep| { + // Skip dev-dependency without version. + dep.is_transitive() || dep.specified_req() + }) .map(|dep| { // If the dependency is from a different registry, then include the // registry in the dependency. diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index d66ea182c1c..b3919d82d0c 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -722,6 +722,7 @@ impl TomlManifest { .unwrap() .clone(); package.workspace = None; + let all = |_d: &TomlDependency| true; return Ok(TomlManifest { package: Some(package), project: None, @@ -731,12 +732,13 @@ impl TomlManifest { example: self.example.clone(), test: self.test.clone(), bench: self.bench.clone(), - dependencies: map_deps(config, self.dependencies.as_ref())?, + dependencies: map_deps(config, self.dependencies.as_ref(), all)?, dev_dependencies: map_deps( config, self.dev_dependencies .as_ref() .or_else(|| self.dev_dependencies2.as_ref()), + TomlDependency::is_version_specified, )?, dev_dependencies2: None, build_dependencies: map_deps( @@ -744,6 +746,7 @@ impl TomlManifest { self.build_dependencies .as_ref() .or_else(|| self.build_dependencies2.as_ref()), + all, )?, build_dependencies2: None, features: self.features.clone(), @@ -754,12 +757,13 @@ impl TomlManifest { Ok(( k.clone(), TomlPlatform { - dependencies: map_deps(config, v.dependencies.as_ref())?, + dependencies: map_deps(config, v.dependencies.as_ref(), all)?, dev_dependencies: map_deps( config, v.dev_dependencies .as_ref() .or_else(|| v.dev_dependencies2.as_ref()), + TomlDependency::is_version_specified, )?, dev_dependencies2: None, build_dependencies: map_deps( @@ -767,6 +771,7 @@ impl TomlManifest { v.build_dependencies .as_ref() .or_else(|| v.build_dependencies2.as_ref()), + all, )?, build_dependencies2: None, }, @@ -788,6 +793,7 @@ impl TomlManifest { fn map_deps( config: &Config, deps: Option<&BTreeMap>, + filter: impl Fn(&TomlDependency) -> bool, ) -> CargoResult>> { let deps = match deps { Some(deps) => deps, @@ -795,6 +801,7 @@ impl TomlManifest { }; let deps = deps .iter() + .filter(|(_k, v)| filter(v)) .map(|(k, v)| Ok((k.clone(), map_dependency(config, v)?))) .collect::>>()?; Ok(Some(deps)) @@ -1221,11 +1228,7 @@ impl TomlManifest { spec.set_url(CRATES_IO_INDEX.parse().unwrap()); } - let version_specified = match *replacement { - TomlDependency::Detailed(ref d) => d.version.is_some(), - TomlDependency::Simple(..) => true, - }; - if version_specified { + if replacement.is_version_specified() { bail!( "replacements cannot specify a version \ requirement, but found one for `{}`", @@ -1330,6 +1333,13 @@ impl TomlDependency { TomlDependency::Detailed(ref details) => details.to_dependency(name, cx, kind), } } + + fn is_version_specified(&self) -> bool { + match self { + TomlDependency::Detailed(d) => d.version.is_some(), + TomlDependency::Simple(..) => true, + } + } } impl DetailedTomlDependency { diff --git a/src/doc/man/cargo-package.adoc b/src/doc/man/cargo-package.adoc index 0c1b5ac97c6..788f55f9b0d 100644 --- a/src/doc/man/cargo-package.adoc +++ b/src/doc/man/cargo-package.adoc @@ -21,6 +21,7 @@ steps: . Load and check the current workspace, performing some basic checks. - Path dependencies are not allowed unless they have a version key. Cargo will ignore the path key for dependencies in published packages. + `dev-dependencies` do not have this restriction. . Create the compressed `.crate` file. - The original `Cargo.toml` file is rewritten and normalized. - `[patch]`, `[replace]`, and `[workspace]` sections are removed from the diff --git a/src/doc/man/generated/cargo-package.html b/src/doc/man/generated/cargo-package.html index 2061369de6f..30fe3f82295 100644 --- a/src/doc/man/generated/cargo-package.html +++ b/src/doc/man/generated/cargo-package.html @@ -27,7 +27,8 @@

DESCRIPTION

  • Path dependencies are not allowed unless they have a version key. Cargo -will ignore the path key for dependencies in published packages.

    +will ignore the path key for dependencies in published packages. +dev-dependencies do not have this restriction.

diff --git a/src/etc/man/cargo-package.1 b/src/etc/man/cargo-package.1 index b27d7bc83dd..e3b95d5204d 100644 --- a/src/etc/man/cargo-package.1 +++ b/src/etc/man/cargo-package.1 @@ -1,13 +1,13 @@ '\" t .\" Title: cargo-package .\" Author: [see the "AUTHOR(S)" section] -.\" Generator: Asciidoctor 2.0.8 -.\" Date: 2019-07-15 +.\" Generator: Asciidoctor 2.0.10 +.\" Date: 2019-09-05 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "CARGO\-PACKAGE" "1" "2019-07-15" "\ \&" "\ \&" +.TH "CARGO\-PACKAGE" "1" "2019-09-05" "\ \&" "\ \&" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 @@ -59,6 +59,7 @@ Load and check the current workspace, performing some basic checks. .\} Path dependencies are not allowed unless they have a version key. Cargo will ignore the path key for dependencies in published packages. +\fBdev\-dependencies\fP do not have this restriction. .RE .RE .sp diff --git a/tests/testsuite/publish.rs b/tests/testsuite/publish.rs index bb9c1edb752..f5c339b43fb 100644 --- a/tests/testsuite/publish.rs +++ b/tests/testsuite/publish.rs @@ -1178,3 +1178,84 @@ fn publish_git_with_version() { ], ); } + +#[cargo_test] +fn publish_dev_dep_no_version() { + registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + license = "MIT" + description = "foo" + documentation = "foo" + homepage = "foo" + repository = "foo" + + [dev-dependencies] + bar = { path = "bar" } + "#, + ) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify --index") + .arg(registry_url().to_string()) + .with_stderr( + "\ +[UPDATING] [..] +[PACKAGING] foo v0.1.0 [..] +[UPLOADING] foo v0.1.0 [..] +", + ) + .run(); + + publish::validate_upload_with_contents( + r#" + { + "authors": [], + "badges": {}, + "categories": [], + "deps": [], + "description": "foo", + "documentation": "foo", + "features": {}, + "homepage": "foo", + "keywords": [], + "license": "MIT", + "license_file": null, + "links": null, + "name": "foo", + "readme": null, + "readme_file": null, + "repository": "foo", + "vers": "0.1.0" + } + "#, + "foo-0.1.0.crate", + &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], + &[( + "Cargo.toml", + r#"[..] +[package] +name = "foo" +version = "0.1.0" +authors = [] +description = "foo" +homepage = "foo" +documentation = "foo" +license = "MIT" +repository = "foo" + +[dev-dependencies] +"#, + )], + ); +}