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