Skip to main content

cargo/ops/
cargo_run.rs

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    // We compute the `bins` here *just for diagnosis*. The actual set of
17    // packages to be run is determined by the `ops::compile` call below.
18    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            // This will be verified in `cargo_compile`.
39        }
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}