#
cargo/ops/
cargo_run.rs

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