    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
    107
    108

    //! Common executables that can be reused by various tests.

    use crate::{basic_manifest, paths, project, Project};
    use lazy_static::lazy_static;
    use std::path::{Path, PathBuf};
    use std::sync::Mutex;

    lazy_static! {
        static ref ECHO_WRAPPER: Mutex<Option<PathBuf>> = Mutex::new(None);
        static ref ECHO: Mutex<Option<PathBuf>> = Mutex::new(None);
    }

    /// Returns the path to an executable that works as a wrapper around rustc.
    ///
    /// The wrapper will echo the command line it was called with to stderr.
    pub fn echo_wrapper() -> PathBuf {
        let mut lock = ECHO_WRAPPER.lock().unwrap();
        if let Some(path) = &*lock {
            return path.clone();
        }
        let p = project()
            .at(paths::global_root().join("rustc-echo-wrapper"))
            .file("Cargo.toml", &basic_manifest("rustc-echo-wrapper", "1.0.0"))
            .file(
                "src/main.rs",
                r#"
                use std::fs::read_to_string;
                use std::path::PathBuf;
                fn main() {
                    // Handle args from `@path` argfile for rustc
                    let args = std::env::args()
                        .flat_map(|p| if let Some(p) = p.strip_prefix("@") {
                            read_to_string(p).unwrap().lines().map(String::from).collect()
                        } else {
                            vec![p]
                        })
                        .collect::<Vec<_>>();
                    eprintln!("WRAPPER CALLED: {}", args[1..].join(" "));
                    let status = std::process::Command::new(&args[1])
                        .args(&args[2..]).status().unwrap();
                    std::process::exit(status.code().unwrap_or(1));
                }
                "#,
            )
            .build();
        p.cargo("build").run();
        let path = p.bin("rustc-echo-wrapper");
        *lock = Some(path.clone());
        path
    }

    /// Returns the path to an executable that prints its arguments.
    ///
    /// Do not expect this to be anything fancy.
    pub fn echo() -> PathBuf {
        let mut lock = ECHO.lock().unwrap();
        if let Some(path) = &*lock {
            return path.clone();
        }
        if let Ok(path) = cargo_util::paths::resolve_executable(Path::new("echo")) {
            *lock = Some(path.clone());
            return path;
        }
        // Often on Windows, `echo` is not available.
        let p = project()
            .at(paths::global_root().join("basic-echo"))
            .file("Cargo.toml", &basic_manifest("basic-echo", "1.0.0"))
            .file(
                "src/main.rs",
                r#"
                    fn main() {
                        let mut s = String::new();
                        let mut it = std::env::args().skip(1).peekable();
                        while let Some(n) = it.next() {
                            s.push_str(&n);
                            if it.peek().is_some() {
                                s.push(' ');
                            }
                        }
                        println!("{}", s);
                    }
                "#,
            )
            .build();
        p.cargo("build").run();
        let path = p.bin("basic-echo");
        *lock = Some(path.clone());
        path
    }

    /// Returns a project which builds a cargo-echo simple subcommand
    pub fn echo_subcommand() -> Project {
        let p = project()
            .at("cargo-echo")
            .file("Cargo.toml", &basic_manifest("cargo-echo", "0.0.1"))
            .file(
                "src/main.rs",
                r#"
                    fn main() {
                        let args: Vec<_> = ::std::env::args().skip(1).collect();
                        println!("{}", args.join(" "));
                    }
                "#,
            )
            .build();
        p.cargo("build").run();
        p
    }