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}