1use std::ffi::OsString;
2use std::iter;
3use std::path::Path;
4
5use crate::core::{TargetKind, Workspace};
6use crate::ops;
7use crate::util::{CargoResult, ProcessError};
8
9pub fn run(
10 ws: &Workspace<'_>,
11 options: &ops::CompileOptions,
12 args: &[OsString],
13) -> CargoResult<Option<ProcessError>> {
14 let config = ws.config();
15
16 let packages = options.spec.get_packages(ws)?;
19 let bins: Vec<_> = packages
20 .into_iter()
21 .flat_map(|pkg| {
22 iter::repeat(pkg).zip(pkg.manifest().targets().iter().filter(|target| {
23 !target.is_lib()
24 && !target.is_custom_build()
25 && if !options.filter.is_specific() {
26 target.is_bin()
27 } else {
28 options.filter.target_run(target)
29 }
30 }))
31 })
32 .collect();
33
34 if bins.is_empty() {
35 if !options.filter.is_specific() {
36 anyhow::bail!("a bin target must be available for `cargo run`")
37 } else {
38 }
40 }
41
42 if bins.len() == 1 {
43 let target = bins[0].1;
44 if let TargetKind::ExampleLib(..) = target.kind() {
45 anyhow::bail!(
46 "example target `{}` is a library and cannot be executed",
47 target.name()
48 )
49 }
50 }
51
52 if bins.len() > 1 {
53 if !options.filter.is_specific() {
54 let names: Vec<&str> = bins
55 .into_iter()
56 .map(|(_pkg, target)| target.name())
57 .collect();
58 anyhow::bail!(
59 "`cargo run` could not determine which binary to run. \
60 Use the `--bin` option to specify a binary, \
61 or the `default-run` manifest key.\n\
62 available binaries: {}",
63 names.join(", ")
64 )
65 } else {
66 anyhow::bail!(
67 "`cargo run` can run at most one executable, but \
68 multiple were specified"
69 )
70 }
71 }
72
73 let compile = ops::compile(ws, options)?;
74 assert_eq!(compile.binaries.len(), 1);
75 let exe = &compile.binaries[0];
76 let exe = match exe.strip_prefix(config.cwd()) {
77 Ok(path) if path.file_name() == Some(path.as_os_str()) => Path::new(".").join(path),
78 Ok(path) => path.to_path_buf(),
79 Err(_) => exe.to_path_buf(),
80 };
81 let pkg = bins[0].0;
82 let mut process = compile.target_process(exe, pkg)?;
83 process.args(args).cwd(config.cwd());
84
85 config.shell().status("Running", process.to_string())?;
86
87 let result = process.exec_replace();
88
89 match result {
90 Ok(()) => Ok(None),
91 Err(e) => {
92 let err = e.downcast::<ProcessError>()?;
93 Ok(Some(err))
94 }
95 }
96}