bootstrap/utils/
shared_helpers.rs

1//! This module serves two purposes:
2//!     1. It is part of the `utils` module and used in other parts of bootstrap.
3//!     2. It is embedded inside bootstrap shims to avoid a dependency on the bootstrap library.
4//!        Therefore, this module should never use any other bootstrap module. This reduces binary
5//!        size and improves compilation time by minimizing linking time.
6
7#![allow(dead_code)]
8
9#[cfg(test)]
10mod tests;
11
12use std::env;
13use std::ffi::OsString;
14use std::fs::OpenOptions;
15use std::io::Write;
16use std::process::Command;
17use std::str::FromStr;
18
19// If we were to declare a tests submodule here, the shim binaries that include this
20// module via `#[path]` would fail to find it, which breaks `./x check bootstrap`.
21// So instead the unit tests for this module are in `super::tests::shared_helpers_tests`.
22
23/// Returns the environment variable which the dynamic library lookup path
24/// resides in for this platform.
25pub fn dylib_path_var() -> &'static str {
26    if cfg!(any(target_os = "windows", target_os = "cygwin")) {
27        "PATH"
28    } else if cfg!(target_vendor = "apple") {
29        "DYLD_LIBRARY_PATH"
30    } else if cfg!(target_os = "haiku") {
31        "LIBRARY_PATH"
32    } else if cfg!(target_os = "aix") {
33        "LIBPATH"
34    } else {
35        "LD_LIBRARY_PATH"
36    }
37}
38
39/// Parses the `dylib_path_var()` environment variable, returning a list of
40/// paths that are members of this lookup path.
41pub fn dylib_path() -> Vec<std::path::PathBuf> {
42    let var = match std::env::var_os(dylib_path_var()) {
43        Some(v) => v,
44        None => return vec![],
45    };
46    std::env::split_paths(&var).collect()
47}
48
49/// Given an executable called `name`, return the filename for the
50/// executable for a particular target.
51pub fn exe(name: &str, target: &str) -> String {
52    // On Cygwin, the decision to append .exe or not is not as straightforward.
53    // Executable files do actually have .exe extensions so on hosts other than
54    // Cygwin it is necessary.  But on a Cygwin host there is magic happening
55    // that redirects requests for file X to file X.exe if it exists, and
56    // furthermore /proc/self/exe (and thus std::env::current_exe) always
57    // returns the name *without* the .exe extension.  For comparisons against
58    // that to match, we therefore do not append .exe for Cygwin targets on
59    // a Cygwin host.
60    if target.contains("windows") || (cfg!(not(target_os = "cygwin")) && target.contains("cygwin"))
61    {
62        format!("{name}.exe")
63    } else if target.contains("uefi") {
64        format!("{name}.efi")
65    } else if target.contains("wasm") {
66        format!("{name}.wasm")
67    } else {
68        name.to_string()
69    }
70}
71
72/// Parses the value of the "RUSTC_VERBOSE" environment variable and returns it as a `usize`.
73/// If it was not defined, returns 0 by default.
74///
75/// Panics if "RUSTC_VERBOSE" is defined with the value that is not an unsigned integer.
76pub fn parse_rustc_verbose() -> usize {
77    match env::var("RUSTC_VERBOSE") {
78        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
79        Err(_) => 0,
80    }
81}
82
83/// Parses the value of the "RUSTC_STAGE" environment variable and returns it as a `String`.
84///
85/// If "RUSTC_STAGE" was not set, the program will be terminated with 101.
86pub fn parse_rustc_stage() -> String {
87    env::var("RUSTC_STAGE").unwrap_or_else(|_| {
88        // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead.
89        eprintln!("rustc shim: FATAL: RUSTC_STAGE was not set");
90        eprintln!("rustc shim: NOTE: use `x.py build -vvv` to see all environment variables set by bootstrap");
91        std::process::exit(101);
92    })
93}
94
95/// Writes the command invocation to a file if `DUMP_BOOTSTRAP_SHIMS` is set during bootstrap.
96///
97/// Before writing it, replaces user-specific values to create generic dumps for cross-environment
98/// comparisons.
99pub fn maybe_dump(dump_name: String, cmd: &Command) {
100    if let Ok(dump_dir) = env::var("DUMP_BOOTSTRAP_SHIMS") {
101        let dump_file = format!("{dump_dir}/{dump_name}");
102
103        let mut file = OpenOptions::new().create(true).append(true).open(dump_file).unwrap();
104
105        let cmd_dump = format!("{cmd:?}\n");
106        let cmd_dump = cmd_dump.replace(&env::var("BUILD_OUT").unwrap(), "${BUILD_OUT}");
107        let cmd_dump = cmd_dump.replace(&env::var("CARGO_HOME").unwrap(), "${CARGO_HOME}");
108
109        file.write_all(cmd_dump.as_bytes()).expect("Unable to write file");
110    }
111}
112
113/// Finds `key` and returns its value from the given list of arguments `args`.
114pub fn parse_value_from_args<'a>(args: &'a [OsString], key: &str) -> Option<&'a str> {
115    let mut args = args.iter();
116    while let Some(arg) = args.next() {
117        let arg = arg.to_str().unwrap();
118
119        if let Some(value) = arg.strip_prefix(&format!("{key}=")) {
120            return Some(value);
121        } else if arg == key {
122            return args.next().map(|v| v.to_str().unwrap());
123        }
124    }
125
126    None
127}