| use crate::command_prelude::*; |
| use crate::util::restricted_names::is_glob_pattern; |
| use cargo::core::Verbosity; |
| use cargo::ops::{self, CompileFilter, Packages}; |
| use cargo_util::ProcessError; |
| |
| pub fn cli() -> Command { |
| subcommand("run") |
| // subcommand aliases are handled in aliased_command() |
| // .alias("r") |
| .about("Run a binary or example of the local package") |
| .arg_quiet() |
| .arg( |
| Arg::new("args") |
| .help("Arguments for the binary or example to run") |
| .value_parser(value_parser!(std::ffi::OsString)) |
| .num_args(0..) |
| .trailing_var_arg(true), |
| ) |
| .arg_targets_bin_example( |
| "Name of the bin target to run", |
| "Name of the example target to run", |
| ) |
| .arg_package("Package with the target to run") |
| .arg_jobs() |
| .arg_release("Build artifacts in release mode, with optimizations") |
| .arg_profile("Build artifacts with the specified profile") |
| .arg_features() |
| .arg_target_triple("Build for the target triple") |
| .arg_target_dir() |
| .arg_manifest_path() |
| .arg_message_format() |
| .arg_unit_graph() |
| .arg_ignore_rust_version() |
| .arg_timings() |
| .after_help("Run `cargo help run` for more detailed information.\n") |
| } |
| |
| pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { |
| let ws = args.workspace(config)?; |
| |
| let mut compile_opts = args.compile_options( |
| config, |
| CompileMode::Build, |
| Some(&ws), |
| ProfileChecking::Custom, |
| )?; |
| |
| // Disallow `spec` to be an glob pattern |
| if let Packages::Packages(opt_in) = &compile_opts.spec { |
| if let Some(pattern) = opt_in.iter().find(|s| is_glob_pattern(s)) { |
| return Err(anyhow::anyhow!( |
| "`cargo run` does not support glob pattern `{}` on package selection", |
| pattern, |
| ) |
| .into()); |
| } |
| } |
| |
| if !args.contains_id("example") && !args.contains_id("bin") { |
| let default_runs: Vec<_> = compile_opts |
| .spec |
| .get_packages(&ws)? |
| .iter() |
| .filter_map(|pkg| pkg.manifest().default_run()) |
| .collect(); |
| if let [bin] = &default_runs[..] { |
| compile_opts.filter = CompileFilter::single_bin(bin.to_string()); |
| } else { |
| // ops::run will take care of errors if len pkgs != 1. |
| compile_opts.filter = CompileFilter::Default { |
| // Force this to false because the code in ops::run is not |
| // able to pre-check features before compilation starts to |
| // enforce that only 1 binary is built. |
| required_features_filterable: false, |
| }; |
| } |
| }; |
| |
| ops::run(&ws, &compile_opts, &values_os(args, "args")).map_err(|err| { |
| let proc_err = match err.downcast_ref::<ProcessError>() { |
| Some(e) => e, |
| None => return CliError::new(err, 101), |
| }; |
| |
| // If we never actually spawned the process then that sounds pretty |
| // bad and we always want to forward that up. |
| let exit_code = match proc_err.code { |
| Some(exit) => exit, |
| None => return CliError::new(err, 101), |
| }; |
| |
| // If `-q` was passed then we suppress extra error information about |
| // a failed process, we assume the process itself printed out enough |
| // information about why it failed so we don't do so as well |
| let is_quiet = config.shell().verbosity() == Verbosity::Quiet; |
| if is_quiet { |
| CliError::code(exit_code) |
| } else { |
| CliError::new(err, exit_code) |
| } |
| }) |
| } |