diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index 63503193eba..6605a348e38 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -516,32 +516,19 @@ impl<'cfg> PackageSet<'cfg> { if !used.insert(pkg_id) { return Ok(()); } - let filtered_deps = resolve.deps(pkg_id).filter(|&(_id, deps)| { - deps.iter().any(|dep| { - if dep.kind() == DepKind::Development && has_dev_units == HasDevUnits::No { - return false; - } - // This is overly broad, since not all target-specific - // dependencies are used both for target and host. To tighten this - // up, this function would need to track "for_host" similar to how - // unit dependencies handles it. - if force_all_targets == ForceAllTargets::No { - let activated = requested_kinds - .iter() - .chain(Some(&CompileKind::Host)) - .any(|kind| target_data.dep_platform_activated(dep, *kind)); - if !activated { - return false; - } - } - true - }) - }); - for (dep_id, _deps) in filtered_deps { + let filtered_deps = PackageSet::filter_deps( + pkg_id, + resolve, + has_dev_units, + requested_kinds, + target_data, + force_all_targets, + ); + for pkg_id in filtered_deps { collect_used_deps( used, resolve, - dep_id, + pkg_id, has_dev_units, requested_kinds, target_data, @@ -571,6 +558,75 @@ impl<'cfg> PackageSet<'cfg> { Ok(()) } + /// Check if there are any dependency packages that do not have any libs. + pub(crate) fn no_lib_pkgs( + &self, + resolve: &Resolve, + root_ids: &[PackageId], + has_dev_units: HasDevUnits, + requested_kinds: &[CompileKind], + target_data: &RustcTargetData<'_>, + force_all_targets: ForceAllTargets, + ) -> BTreeMap> { + root_ids + .iter() + .map(|&root_id| { + let pkgs = PackageSet::filter_deps( + root_id, + resolve, + has_dev_units, + requested_kinds, + target_data, + force_all_targets, + ) + .filter_map(|package_id| { + if let Ok(dep_pkg) = self.get_one(package_id) { + if !dep_pkg.targets().iter().any(|t| t.is_lib()) { + Some(dep_pkg) + } else { + None + } + } else { + None + } + }) + .collect(); + (root_id, pkgs) + }) + .collect() + } + + fn filter_deps<'a>( + pkg_id: PackageId, + resolve: &'a Resolve, + has_dev_units: HasDevUnits, + requested_kinds: &'a [CompileKind], + target_data: &'a RustcTargetData<'_>, + force_all_targets: ForceAllTargets, + ) -> impl Iterator + 'a { + resolve + .deps(pkg_id) + .filter(move |&(_id, deps)| { + deps.iter().any(|dep| { + if dep.kind() == DepKind::Development && has_dev_units == HasDevUnits::No { + return false; + } + if force_all_targets == ForceAllTargets::No { + let activated = requested_kinds + .iter() + .chain(Some(&CompileKind::Host)) + .any(|kind| target_data.dep_platform_activated(dep, *kind)); + if !activated { + return false; + } + } + true + }) + }) + .map(|(pkg_id, _)| pkg_id) + .into_iter() + } + pub fn sources(&self) -> Ref<'_, SourceMap<'cfg>> { self.sources.borrow() } diff --git a/src/cargo/ops/resolve.rs b/src/cargo/ops/resolve.rs index c305bc3a353..f2d213f85d1 100644 --- a/src/cargo/ops/resolve.rs +++ b/src/cargo/ops/resolve.rs @@ -171,6 +171,24 @@ pub fn resolve_ws_with_opts<'cfg>( feature_opts, )?; + let no_lib_pkgs = pkg_set.no_lib_pkgs( + &resolved_with_overrides, + &member_ids, + has_dev_units, + requested_targets, + target_data, + force_all_targets, + ); + for (pkg_id, dep_pkgs) in no_lib_pkgs { + for dep_pkg in dep_pkgs { + ws.config().shell().warn(&format!( + "{} ignoring invalid dependency `{}` which is missing a lib target", + pkg_id, + dep_pkg.name(), + ))?; + } + } + Ok(WorkspaceResolve { pkg_set, workspace_resolve: resolve, diff --git a/tests/testsuite/run.rs b/tests/testsuite/run.rs index 106e2259fc4..5775e5b8e00 100644 --- a/tests/testsuite/run.rs +++ b/tests/testsuite/run.rs @@ -723,6 +723,196 @@ fn run_dylib_dep() { p.cargo("run hello world").run(); } +#[cargo_test] +fn run_with_bin_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies.bar] + path = "bar" + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [[bin]] + name = "bar" + "#, + ) + .file("bar/src/main.rs", r#"fn main() { println!("bar"); }"#) + .build(); + + p.cargo("run") + .with_stderr( + "\ +[WARNING] foo v0.0.1 ([CWD]) ignoring invalid dependency `bar` which is missing a lib target +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo[EXE]`", + ) + .with_stdout("hello") + .run(); +} + +#[cargo_test] +fn run_with_bin_deps() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies.bar1] + path = "bar1" + [dependencies.bar2] + path = "bar2" + "#, + ) + .file("src/main.rs", r#"fn main() { println!("hello"); }"#) + .file( + "bar1/Cargo.toml", + r#" + [package] + name = "bar1" + version = "0.0.1" + authors = [] + + [[bin]] + name = "bar1" + "#, + ) + .file("bar1/src/main.rs", r#"fn main() { println!("bar1"); }"#) + .file( + "bar2/Cargo.toml", + r#" + [package] + name = "bar2" + version = "0.0.1" + authors = [] + + [[bin]] + name = "bar2" + "#, + ) + .file("bar2/src/main.rs", r#"fn main() { println!("bar2"); }"#) + .build(); + + p.cargo("run") + .with_stderr( + "\ +[WARNING] foo v0.0.1 ([CWD]) ignoring invalid dependency `bar1` which is missing a lib target +[WARNING] foo v0.0.1 ([CWD]) ignoring invalid dependency `bar2` which is missing a lib target +[COMPILING] foo v0.0.1 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo[EXE]`", + ) + .with_stdout("hello") + .run(); +} + +#[cargo_test] +fn run_with_bin_dep_in_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo1", "foo2"] + "#, + ) + .file( + "foo1/Cargo.toml", + r#" + [package] + name = "foo1" + version = "0.0.1" + + [dependencies.bar1] + path = "bar1" + "#, + ) + .file("foo1/src/main.rs", r#"fn main() { println!("hello"); }"#) + .file( + "foo1/bar1/Cargo.toml", + r#" + [package] + name = "bar1" + version = "0.0.1" + authors = [] + + [[bin]] + name = "bar1" + "#, + ) + .file( + "foo1/bar1/src/main.rs", + r#"fn main() { println!("bar1"); }"#, + ) + .file( + "foo2/Cargo.toml", + r#" + [package] + name = "foo2" + version = "0.0.1" + + [dependencies.bar2] + path = "bar2" + "#, + ) + .file("foo2/src/main.rs", r#"fn main() { println!("hello"); }"#) + .file( + "foo2/bar2/Cargo.toml", + r#" + [package] + name = "bar2" + version = "0.0.1" + authors = [] + + [[bin]] + name = "bar2" + "#, + ) + .file( + "foo2/bar2/src/main.rs", + r#"fn main() { println!("bar2"); }"#, + ) + .build(); + + p.cargo("run") + .with_status(101) + .with_stderr( + "\ +[ERROR] `cargo run` could not determine which binary to run[..] +available binaries: bar1, bar2, foo1, foo2", + ) + .run(); + + p.cargo("run --bin foo1") + .with_stderr( + "\ +[WARNING] foo1 v0.0.1 ([CWD]/foo1) ignoring invalid dependency `bar1` which is missing a lib target +[WARNING] foo2 v0.0.1 ([CWD]/foo2) ignoring invalid dependency `bar2` which is missing a lib target +[COMPILING] foo1 v0.0.1 ([CWD]/foo1) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target/debug/foo1[EXE]`", + ) + .with_stdout("hello") + .run(); +} + #[cargo_test] fn release_works() { let p = project()