cargo/ops/
cargo_run.rs

1use std::ffi::OsString;
2use std::iter;
3use std::path::Path;
4
5use crate::core::compiler::UnitOutput;
6use crate::core::{TargetKind, Workspace};
7use crate::ops;
8use crate::util::CargoResult;
9
10pub fn run(
11    ws: &Workspace<'_>,
12    options: &ops::CompileOptions,
13    args: &[OsString],
14) -> CargoResult<()> {
15    let gctx = ws.gctx();
16
17    if options.filter.contains_glob_patterns() {
18        anyhow::bail!("`cargo run` does not support glob patterns on target selection")
19    }
20
21    // We compute the `bins` here *just for diagnosis*. The actual set of
22    // packages to be run is determined by the `ops::compile` call below.
23    let packages = options.spec.get_packages(ws)?;
24    let bins: Vec<_> = packages
25        .into_iter()
26        .flat_map(|pkg| {
27            iter::repeat(pkg).zip(pkg.manifest().targets().iter().filter(|target| {
28                !target.is_lib()
29                    && !target.is_custom_build()
30                    && if !options.filter.is_specific() {
31                        target.is_bin()
32                    } else {
33                        options.filter.target_run(target)
34                    }
35            }))
36        })
37        .collect();
38
39    if bins.is_empty() {
40        if !options.filter.is_specific() {
41            anyhow::bail!("a bin target must be available for `cargo run`")
42        } else {
43            // This will be verified in `cargo_compile`.
44        }
45    }
46
47    if bins.len() == 1 {
48        let target = bins[0].1;
49        if let TargetKind::ExampleLib(..) = target.kind() {
50            anyhow::bail!(
51                "example target `{}` is a library and cannot be executed",
52                target.name()
53            )
54        }
55    }
56
57    if bins.len() > 1 {
58        if !options.filter.is_specific() {
59            let mut names: Vec<&str> = bins
60                .into_iter()
61                .map(|(_pkg, target)| target.name())
62                .collect();
63            names.sort();
64            anyhow::bail!(
65                "`cargo run` could not determine which binary to run. \
66                 Use the `--bin` option to specify a binary, \
67                 or the `default-run` manifest key.\n\
68                 available binaries: {}",
69                names.join(", ")
70            )
71        } else {
72            anyhow::bail!(
73                "`cargo run` can run at most one executable, but \
74                 multiple were specified"
75            )
76        }
77    }
78
79    // `cargo run` is only compatible with one `--target` flag at most
80    options.build_config.single_requested_kind()?;
81
82    let compile = ops::compile(ws, options)?;
83    assert_eq!(compile.binaries.len(), 1);
84    let UnitOutput {
85        unit,
86        path,
87        script_meta,
88    } = &compile.binaries[0];
89    let exe = match path.strip_prefix(gctx.cwd()) {
90        Ok(path) if path.file_name() == Some(path.as_os_str()) => Path::new(".").join(path),
91        Ok(path) => path.to_path_buf(),
92        Err(_) => path.to_path_buf(),
93    };
94    let pkg = bins[0].0;
95    let mut process = compile.target_process(exe, unit.kind, pkg, *script_meta)?;
96
97    // Sets the working directory of the child process to the current working
98    // directory of the parent process.
99    // Overrides the default working directory of the `ProcessBuilder` returned
100    // by `compile.target_process` (the package's root directory)
101    process.args(args).cwd(gctx.cwd());
102
103    if gctx.extra_verbose() {
104        process.display_env_vars();
105    }
106
107    gctx.shell().status("Running", process.to_string())?;
108
109    process.exec_replace()
110}