1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
use std::ffi::OsString;
use std::iter;
use std::path::Path;

use crate::core::compiler::UnitOutput;
use crate::core::{TargetKind, Workspace};
use crate::ops;
use crate::util::CargoResult;

pub fn run(
    ws: &Workspace<'_>,
    options: &ops::CompileOptions,
    args: &[OsString],
) -> CargoResult<()> {
    let config = ws.config();

    if options.filter.contains_glob_patterns() {
        anyhow::bail!("`cargo run` does not support glob patterns on target selection")
    }

    // 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 bins: Vec<_> = packages
        .into_iter()
        .flat_map(|pkg| {
            iter::repeat(pkg).zip(pkg.manifest().targets().iter().filter(|target| {
                !target.is_lib()
                    && !target.is_custom_build()
                    && if !options.filter.is_specific() {
                        target.is_bin()
                    } else {
                        options.filter.target_run(target)
                    }
            }))
        })
        .collect();

    if bins.is_empty() {
        if !options.filter.is_specific() {
            anyhow::bail!("a bin target must be available for `cargo run`")
        } else {
            // This will be verified in `cargo_compile`.
        }
    }

    if bins.len() == 1 {
        let target = bins[0].1;
        if let TargetKind::ExampleLib(..) = target.kind() {
            anyhow::bail!(
                "example target `{}` is a library and cannot be executed",
                target.name()
            )
        }
    }

    if bins.len() > 1 {
        if !options.filter.is_specific() {
            let mut names: Vec<&str> = bins
                .into_iter()
                .map(|(_pkg, target)| target.name())
                .collect();
            names.sort();
            anyhow::bail!(
                "`cargo run` could not determine which binary to run. \
                 Use the `--bin` option to specify a binary, \
                 or the `default-run` manifest key.\n\
                 available binaries: {}",
                names.join(", ")
            )
        } else {
            anyhow::bail!(
                "`cargo run` can run at most one executable, but \
                 multiple were specified"
            )
        }
    }

    // `cargo run` is only compatible with one `--target` flag at most
    options.build_config.single_requested_kind()?;

    let compile = ops::compile(ws, options)?;
    assert_eq!(compile.binaries.len(), 1);
    let UnitOutput {
        unit,
        path,
        script_meta,
    } = &compile.binaries[0];
    let exe = match path.strip_prefix(config.cwd()) {
        Ok(path) if path.file_name() == Some(path.as_os_str()) => Path::new(".").join(path),
        Ok(path) => path.to_path_buf(),
        Err(_) => path.to_path_buf(),
    };
    let pkg = bins[0].0;
    let mut process = compile.target_process(exe, unit.kind, pkg, *script_meta)?;
    process.args(args).cwd(config.cwd());

    config.shell().status("Running", process.to_string())?;

    process.exec_replace()
}