run_make_support/
run.rs

1use std::ffi::{OsStr, OsString};
2use std::path::PathBuf;
3use std::{env, panic};
4
5use crate::command::{Command, CompletedProcess};
6use crate::util::handle_failed_output;
7use crate::{cwd, env_var};
8
9#[track_caller]
10fn run_common(name: &str, args: Option<&[&str]>) -> Command {
11    let mut bin_path = PathBuf::new();
12    bin_path.push(cwd());
13    bin_path.push(name);
14    let ld_lib_path_envvar = env_var("LD_LIB_PATH_ENVVAR");
15
16    let mut cmd = if let Some(rtc) = env::var_os("REMOTE_TEST_CLIENT") {
17        let mut cmd = Command::new(rtc);
18        cmd.arg("run");
19        // FIXME: the "0" indicates how many support files should be uploaded along with the binary
20        // to execute. If a test requires additional files to be pushed to the remote machine, this
21        // will have to be changed (and the support files will have to be uploaded).
22        cmd.arg("0");
23        cmd.arg(bin_path);
24        cmd
25    } else if let Ok(runner) = std::env::var("RUNNER") {
26        let mut args = split_maybe_args(&runner);
27
28        let prog = args.remove(0);
29        let mut cmd = Command::new(prog);
30
31        for arg in args {
32            cmd.arg(arg);
33        }
34
35        cmd.arg("--");
36        cmd.arg(bin_path);
37
38        cmd
39    } else {
40        Command::new(bin_path)
41    };
42
43    if let Some(args) = args {
44        for arg in args {
45            cmd.arg(arg);
46        }
47    }
48
49    cmd.env(&ld_lib_path_envvar, {
50        let mut paths = vec![];
51        paths.push(cwd());
52        for p in env::split_paths(&env_var("TARGET_EXE_DYLIB_PATH")) {
53            paths.push(p.to_path_buf());
54        }
55        for p in env::split_paths(&env_var(&ld_lib_path_envvar)) {
56            paths.push(p.to_path_buf());
57        }
58        env::join_paths(paths.iter()).unwrap()
59    });
60    cmd.env("LC_ALL", "C"); // force english locale
61
62    cmd
63}
64
65/// Run a built binary and make sure it succeeds.
66#[track_caller]
67pub fn run(name: &str) -> CompletedProcess {
68    let caller = panic::Location::caller();
69    let mut cmd = run_common(name, None);
70    let output = cmd.run();
71    if !output.status().success() {
72        handle_failed_output(&cmd, output, caller.line());
73    }
74    output
75}
76
77/// Run a built binary with one or more argument(s) and make sure it succeeds.
78#[track_caller]
79pub fn run_with_args(name: &str, args: &[&str]) -> CompletedProcess {
80    let caller = panic::Location::caller();
81    let mut cmd = run_common(name, Some(args));
82    let output = cmd.run();
83    if !output.status().success() {
84        handle_failed_output(&cmd, output, caller.line());
85    }
86    output
87}
88
89/// Run a built binary and make sure it fails.
90#[track_caller]
91pub fn run_fail(name: &str) -> CompletedProcess {
92    let caller = panic::Location::caller();
93    let mut cmd = run_common(name, None);
94    let output = cmd.run_fail();
95    if output.status().success() {
96        handle_failed_output(&cmd, output, caller.line());
97    }
98    output
99}
100
101/// Create a new custom [`Command`]. This should be preferred to creating [`std::process::Command`]
102/// directly.
103#[track_caller]
104pub fn cmd<S: AsRef<OsStr>>(program: S) -> Command {
105    let mut command = Command::new(program);
106    command.env("LC_ALL", "C"); // force english locale
107    command
108}
109
110fn split_maybe_args(s: &str) -> Vec<OsString> {
111    // FIXME(132599): implement proper env var/shell argument splitting.
112    s.split(' ')
113        .filter_map(|s| {
114            if s.chars().all(|c| c.is_whitespace()) { None } else { Some(OsString::from(s)) }
115        })
116        .collect()
117}