#
cargo_test_support/
tools.rs

    1//! Common executables that can be reused by various tests.
    2
    3use crate::{basic_manifest, paths, project, Project};
    4use std::path::{Path, PathBuf};
    5use std::sync::Mutex;
    6use std::sync::OnceLock;
    7
    8static ECHO_WRAPPER: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new();
    9static ECHO: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new();
    10static CLIPPY_DRIVER: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new();
    11
    12/// Returns the path to an executable that works as a wrapper around rustc.
    13///
    14/// The wrapper will echo the command line it was called with to stderr.
    15pub fn echo_wrapper() -> PathBuf {
    16    let mut lock = ECHO_WRAPPER
    17        .get_or_init(|| Default::default())
    18        .lock()
    19        .unwrap();
    20    if let Some(path) = &*lock {
    21        return path.clone();
    22    }
    23    let p = project()
    24        .at(paths::global_root().join("rustc-echo-wrapper"))
    25        .file("Cargo.toml", &basic_manifest("rustc-echo-wrapper", "1.0.0"))
    26        .file(
    27            "src/main.rs",
    28            r#"
    29            use std::fs::read_to_string;
    30            use std::path::PathBuf;
    31            fn main() {
    32                // Handle args from `@path` argfile for rustc
    33                let args = std::env::args()
    34                    .flat_map(|p| if let Some(p) = p.strip_prefix("@") {
    35                        read_to_string(p).unwrap().lines().map(String::from).collect()
    36                    } else {
    37                        vec![p]
    38                    })
    39                    .collect::<Vec<_>>();
    40                eprintln!("WRAPPER CALLED: {}", args[1..].join(" "));
    41                let status = std::process::Command::new(&args[1])
    42                    .args(&args[2..]).status().unwrap();
    43                std::process::exit(status.code().unwrap_or(1));
    44            }
    45            "#,
    46        )
    47        .build();
    48    p.cargo("build").run();
    49    let path = p.bin("rustc-echo-wrapper");
    50    *lock = Some(path.clone());
    51    path
    52}
    53
    54/// Returns the path to an executable that prints its arguments.
    55///
    56/// Do not expect this to be anything fancy.
    57pub fn echo() -> PathBuf {
    58    let mut lock = ECHO.get_or_init(|| Default::default()).lock().unwrap();
    59    if let Some(path) = &*lock {
    60        return path.clone();
    61    }
    62    if let Ok(path) = cargo_util::paths::resolve_executable(Path::new("echo")) {
    63        *lock = Some(path.clone());
    64        return path;
    65    }
    66    // Often on Windows, `echo` is not available.
    67    let p = project()
    68        .at(paths::global_root().join("basic-echo"))
    69        .file("Cargo.toml", &basic_manifest("basic-echo", "1.0.0"))
    70        .file(
    71            "src/main.rs",
    72            r#"
    73                fn main() {
    74                    let mut s = String::new();
    75                    let mut it = std::env::args().skip(1).peekable();
    76                    while let Some(n) = it.next() {
    77                        s.push_str(&n);
    78                        if it.peek().is_some() {
    79                            s.push(' ');
    80                        }
    81                    }
    82                    println!("{}", s);
    83                }
    84            "#,
    85        )
    86        .build();
    87    p.cargo("build").run();
    88    let path = p.bin("basic-echo");
    89    *lock = Some(path.clone());
    90    path
    91}
    92
    93/// Returns a project which builds a cargo-echo simple subcommand
    94pub fn echo_subcommand() -> Project {
    95    let p = project()
    96        .at("cargo-echo")
    97        .file("Cargo.toml", &basic_manifest("cargo-echo", "0.0.1"))
    98        .file(
    99            "src/main.rs",
    100            r#"
    101                fn main() {
    102                    let args: Vec<_> = ::std::env::args().skip(1).collect();
    103                    println!("{}", args.join(" "));
    104                }
    105            "#,
    106        )
    107        .build();
    108    p.cargo("build").run();
    109    p
    110}
    111
    112/// A wrapper around `rustc` instead of calling `clippy`.
    113pub fn wrapped_clippy_driver() -> PathBuf {
    114    let mut lock = CLIPPY_DRIVER
    115        .get_or_init(|| Default::default())
    116        .lock()
    117        .unwrap();
    118    if let Some(path) = &*lock {
    119        return path.clone();
    120    }
    121    let clippy_driver = project()
    122        .at(paths::global_root().join("clippy-driver"))
    123        .file("Cargo.toml", &basic_manifest("clippy-driver", "0.0.1"))
    124        .file(
    125            "src/main.rs",
    126            r#"
    127            fn main() {
    128                let mut args = std::env::args_os();
    129                let _me = args.next().unwrap();
    130                let rustc = args.next().unwrap();
    131                let status = std::process::Command::new(rustc).args(args).status().unwrap();
    132                std::process::exit(status.code().unwrap_or(1));
    133            }
    134            "#,
    135        )
    136        .build();
    137    clippy_driver.cargo("build").run();
    138    let path = clippy_driver.bin("clippy-driver");
    139    *lock = Some(path.clone());
    140    path
    141}