    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
    102
    103
    104
    105
    106

    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)?;

        // Sets the working directory of the child process to the current working
        // directory of the parent process.
        // Overrides the default working directory of the `ProcessBuilder` returned
        // by `compile.target_process` (the package's root directory)
        process.args(args).cwd(config.cwd());

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

        process.exec_replace()
    }