From f5114a4560236ce20ede5abce14bbff5973cd09a Mon Sep 17 00:00:00 2001 From: Matteo Settenvini Date: Mon, 23 Aug 2021 17:04:43 +0200 Subject: [PATCH] cargo-run-deps: Make commands in dependencies available to run This approach teaches `cargo run` to handle also binary targets in dependencies. In turn, it allows using cargo as a shim when needing to invoke a specific package version from the set of dependencies, without resorting to install a user-wide version of a binary. Users of e.g. `mdbook` should rejoice. Fixes #2267. --- src/bin/cargo/commands/run.rs | 50 +++++++++++++++++++++++++++++------ src/cargo/ops/cargo_run.rs | 12 ++++++++- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/bin/cargo/commands/run.rs b/src/bin/cargo/commands/run.rs index ee74d5ff9e4..9f6ee0a4cf0 100644 --- a/src/bin/cargo/commands/run.rs +++ b/src/bin/cargo/commands/run.rs @@ -1,7 +1,8 @@ use crate::command_prelude::*; use crate::util::restricted_names::is_glob_pattern; -use cargo::core::Verbosity; -use cargo::ops::{self, CompileFilter, Packages}; +use cargo::core::{Verbosity, Workspace}; +use cargo::ops::{self, CompileFilter, CompileOptions, Packages}; +use cargo::util::CargoResult; use cargo_util::ProcessError; pub fn cli() -> App { @@ -52,12 +53,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { } if !args.is_present("example") && !args.is_present("bin") { - let default_runs: Vec<_> = compile_opts - .spec - .get_packages(&ws)? - .iter() - .filter_map(|pkg| pkg.manifest().default_run()) - .collect(); + let default_runs = get_default_runs(&ws, &compile_opts)?; if default_runs.len() == 1 { compile_opts.filter = CompileFilter::from_raw_arguments( false, @@ -106,3 +102,41 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { } }) } + +fn get_default_runs(ws: &Workspace<'_>, compile_opts: &CompileOptions) -> CargoResult> { + const ERROR: &'static str = + "`cargo run` cannot find pkgid either in the workspace or among the workspace dependencies"; + + let workspace_packages = compile_opts.spec.get_packages(ws); + + let default_runs = if let Ok(packages) = workspace_packages { + // Package is workspace member + packages + .iter() + .filter_map(|pkg| pkg.manifest().default_run()) + .map(str::to_owned) + .collect() + } else if let Packages::Packages(ref pkg_names) = compile_opts.spec { + // Search dependencies + let (package_set, resolver) = ops::resolve_ws(ws)?; + let deps: Vec<_> = pkg_names + .iter() + .flat_map(|name| resolver.query(name)) + .collect(); + + if deps.is_empty() { + anyhow::bail!(ERROR); + } + + package_set + .get_many(deps)? + .iter() + .filter_map(|pkg| pkg.manifest().default_run()) + .map(str::to_owned) + .collect() + } else { + anyhow::bail!(ERROR); + }; + + Ok(default_runs) +} diff --git a/src/cargo/ops/cargo_run.rs b/src/cargo/ops/cargo_run.rs index 69bae2c5912..4132c64e664 100644 --- a/src/cargo/ops/cargo_run.rs +++ b/src/cargo/ops/cargo_run.rs @@ -20,7 +20,17 @@ pub fn run( // We compute the `bins` here *just for diagnosis*. The actual set of // packages to be run is determined by the `ops::compile` call below. - let packages = options.spec.get_packages(ws)?; + let (package_set, resolver) = ops::resolve_ws(ws)?; + let packages = if let ops::Packages::Packages(ref pkg_names) = options.spec { + let pkgids: Vec<_> = pkg_names + .iter() + .flat_map(|name| resolver.query(name)) + .collect(); + package_set.get_many(pkgids)? + } else { + options.spec.get_packages(ws)? + }; + let bins: Vec<_> = packages .into_iter() .flat_map(|pkg| {