#
cargo/util/
workspace.rs

    1use crate::core::compiler::Unit;
    2use crate::core::manifest::TargetSourcePath;
    3use crate::core::{Target, Workspace};
    4use crate::ops::CompileOptions;
    5use crate::util::CargoResult;
    6use anyhow::bail;
    7use cargo_util::ProcessBuilder;
    8use cargo_util::paths::normalize_path;
    9use std::fmt::Write;
    10use std::path::Path;
    11use std::path::PathBuf;
    12
    13const ITEM_INDENT: &str = "    ";
    14
    15fn get_available_targets<'a>(
    16    filter_fn: fn(&Target) -> bool,
    17    ws: &'a Workspace<'_>,
    18    options: &'a CompileOptions,
    19) -> CargoResult<Vec<(&'a str, &'a Path)>> {
    20    let packages = options.spec.get_packages(ws)?;
    21
    22    let mut targets: Vec<_> = packages
    23        .into_iter()
    24        .flat_map(|pkg| {
    25            pkg.manifest()
    26                .targets()
    27                .iter()
    28                .filter(|target| filter_fn(target))
    29        })
    30        .map(|target| {
    31            (
    32                target.name(),
    33                target.src_path().path().expect("Target is not a `Metabuild` but one of `Bin` | `Test` | `Bench` | `ExampleBin`")
    34            )
    35        })
    36        .collect();
    37
    38    targets.sort();
    39
    40    Ok(targets)
    41}
    42
    43fn print_available_targets(
    44    filter_fn: fn(&Target) -> bool,
    45    ws: &Workspace<'_>,
    46    options: &CompileOptions,
    47    option_name: &str,
    48    plural_name: &str,
    49) -> CargoResult<()> {
    50    let targets = get_available_targets(filter_fn, ws, options)?;
    51
    52    let mut output = String::new();
    53    writeln!(output, "\"{}\" takes one argument.", option_name)?;
    54
    55    if targets.is_empty() {
    56        writeln!(output, "No {} available.", plural_name)?;
    57    } else {
    58        writeln!(output, "Available {}:", plural_name)?;
    59        let mut shell = ws.gctx().shell();
    60        for (name, src_path) in targets {
    61            let link = shell.err_file_hyperlink(src_path);
    62            writeln!(output, "{ITEM_INDENT}{link}{}{link:#}", name)?;
    63        }
    64    }
    65    bail!("{}", output)
    66}
    67
    68pub fn print_available_packages(ws: &Workspace<'_>) -> CargoResult<()> {
    69    let packages = ws
    70        .members()
    71        .map(|pkg| (pkg.name().as_str(), pkg.manifest_path()))
    72        .collect::<Vec<_>>();
    73
    74    let mut output = "\"--package <SPEC>\" requires a SPEC format value, \
    75        which can be any package ID specifier in the dependency graph.\n\
    76        Run `cargo help pkgid` for more information about SPEC format.\n\n"
    77        .to_string();
    78
    79    if packages.is_empty() {
    80        // This would never happen.
    81        // Just in case something regresses we covers it here.
    82        writeln!(output, "No packages available.")?;
    83    } else {
    84        writeln!(output, "Possible packages/workspace members:")?;
    85        let mut shell = ws.gctx().shell();
    86        for (name, manifest_path) in packages {
    87            let link = shell.err_file_hyperlink(manifest_path);
    88            writeln!(output, "{ITEM_INDENT}{link}{}{link:#}", name)?;
    89        }
    90    }
    91    bail!("{}", output)
    92}
    93
    94pub fn print_available_examples(ws: &Workspace<'_>, options: &CompileOptions) -> CargoResult<()> {
    95    print_available_targets(Target::is_example, ws, options, "--example", "examples")
    96}
    97
    98pub fn print_available_binaries(ws: &Workspace<'_>, options: &CompileOptions) -> CargoResult<()> {
    99    print_available_targets(Target::is_bin, ws, options, "--bin", "binaries")
    100}
    101
    102pub fn print_available_benches(ws: &Workspace<'_>, options: &CompileOptions) -> CargoResult<()> {
    103    print_available_targets(Target::is_bench, ws, options, "--bench", "bench targets")
    104}
    105
    106pub fn print_available_tests(ws: &Workspace<'_>, options: &CompileOptions) -> CargoResult<()> {
    107    print_available_targets(Target::is_test, ws, options, "--test", "test targets")
    108}
    109
    110/// The source path and its current dir for use in compilation.
    111///
    112/// The path that we pass to rustc is actually fairly important because it will
    113/// show up in error messages (important for readability), debug information
    114/// (important for caching), etc. As a result we need to be pretty careful how we
    115/// actually invoke rustc.
    116///
    117/// In general users don't expect `cargo build` to cause rebuilds if you change
    118/// directories. That could be if you just change directories in the package or
    119/// if you literally move the whole package wholesale to a new directory. As a
    120/// result we mostly don't factor in `cwd` to this calculation. Instead we try to
    121/// track the workspace as much as possible and we update the current directory
    122/// of rustc/rustdoc where appropriate.
    123///
    124/// The first returned value here is the argument to pass to rustc, and the
    125/// second is the cwd that rustc should operate in.
    126pub fn path_args(ws: &Workspace<'_>, unit: &Unit) -> (PathBuf, PathBuf) {
    127    let src = match unit.target.src_path() {
    128        TargetSourcePath::Path(path) => path.to_path_buf(),
    129        TargetSourcePath::Metabuild => unit.pkg.manifest().metabuild_path(ws.build_dir()),
    130    };
    131    assert!(src.is_absolute());
    132    if unit.pkg.package_id().source_id().is_path() {
    133        // Determine which path we make this relative to: usually it's the workspace root,
    134        // but this can be overwritten with a `-Z` flag.
    135        let root = match &ws.gctx().cli_unstable().root_dir {
    136            None => ws.root().to_owned(),
    137            Some(root_dir) => normalize_path(&ws.gctx().cwd().join(root_dir)),
    138        };
    139        if let Ok(path) = src.strip_prefix(&root) {
    140            return (path.to_path_buf(), root);
    141        }
    142    }
    143    (src, unit.pkg.root().to_path_buf())
    144}
    145
    146pub fn add_path_args(ws: &Workspace<'_>, unit: &Unit, cmd: &mut ProcessBuilder) {
    147    let (arg, cwd) = path_args(ws, unit);
    148    cmd.arg(arg);
    149    cmd.cwd(cwd);
    150}