compiletest/
util.rs

1use std::env;
2use std::process::Command;
3
4use camino::{Utf8Path, Utf8PathBuf};
5use tracing::*;
6
7use crate::common::Config;
8
9#[cfg(test)]
10mod tests;
11
12pub fn make_new_path(path: &str) -> String {
13    assert!(cfg!(windows));
14    // Windows just uses PATH as the library search path, so we have to
15    // maintain the current value while adding our own
16    match env::var(lib_path_env_var()) {
17        Ok(curr) => format!("{}{}{}", path, path_div(), curr),
18        Err(..) => path.to_owned(),
19    }
20}
21
22pub fn lib_path_env_var() -> &'static str {
23    "PATH"
24}
25fn path_div() -> &'static str {
26    ";"
27}
28
29pub fn logv(config: &Config, s: String) {
30    debug!("{}", s);
31    if config.verbose {
32        // Note: `./x test ... --verbose --no-capture` is needed to see this print.
33        println!("{}", s);
34    }
35}
36
37pub trait Utf8PathBufExt {
38    /// Append an extension to the path, even if it already has one.
39    fn with_extra_extension(&self, extension: &str) -> Utf8PathBuf;
40}
41
42impl Utf8PathBufExt for Utf8PathBuf {
43    fn with_extra_extension(&self, extension: &str) -> Utf8PathBuf {
44        if extension.is_empty() {
45            self.clone()
46        } else {
47            let mut fname = self.file_name().unwrap().to_string();
48            if !extension.starts_with('.') {
49                fname.push_str(".");
50            }
51            fname.push_str(extension);
52            self.with_file_name(fname)
53        }
54    }
55}
56
57/// The name of the environment variable that holds dynamic library locations.
58pub fn dylib_env_var() -> &'static str {
59    if cfg!(windows) {
60        "PATH"
61    } else if cfg!(target_vendor = "apple") {
62        "DYLD_LIBRARY_PATH"
63    } else if cfg!(target_os = "haiku") {
64        "LIBRARY_PATH"
65    } else if cfg!(target_os = "aix") {
66        "LIBPATH"
67    } else {
68        "LD_LIBRARY_PATH"
69    }
70}
71
72/// Adds a list of lookup paths to `cmd`'s dynamic library lookup path.
73/// If the dylib_path_var is already set for this cmd, the old value will be overwritten!
74pub fn add_dylib_path(
75    cmd: &mut Command,
76    paths: impl Iterator<Item = impl Into<std::path::PathBuf>>,
77) {
78    let path_env = env::var_os(dylib_env_var());
79    let old_paths = path_env.as_ref().map(env::split_paths);
80    let new_paths = paths.map(Into::into).chain(old_paths.into_iter().flatten());
81    cmd.env(dylib_env_var(), env::join_paths(new_paths).unwrap());
82}
83
84pub fn copy_dir_all(src: &Utf8Path, dst: &Utf8Path) -> std::io::Result<()> {
85    std::fs::create_dir_all(dst.as_std_path())?;
86    for entry in std::fs::read_dir(src.as_std_path())? {
87        let entry = entry?;
88        let path = Utf8PathBuf::try_from(entry.path()).unwrap();
89        let file_name = path.file_name().unwrap();
90        let ty = entry.file_type()?;
91        if ty.is_dir() {
92            copy_dir_all(&path, &dst.join(file_name))?;
93        } else {
94            std::fs::copy(path.as_std_path(), dst.join(file_name).as_std_path())?;
95        }
96    }
97    Ok(())
98}
99
100macro_rules! static_regex {
101    ($re:literal) => {{
102        static RE: ::std::sync::OnceLock<::regex::Regex> = ::std::sync::OnceLock::new();
103        RE.get_or_init(|| ::regex::Regex::new($re).unwrap())
104    }};
105}
106pub(crate) use static_regex;
107
108macro_rules! string_enum {
109    ($(#[$meta:meta])* $vis:vis enum $name:ident { $($variant:ident => $repr:expr,)* }) => {
110        $(#[$meta])*
111        $vis enum $name {
112            $($variant,)*
113        }
114
115        impl $name {
116            $vis const VARIANTS: &'static [Self] = &[$(Self::$variant,)*];
117            $vis const STR_VARIANTS: &'static [&'static str] = &[$(Self::$variant.to_str(),)*];
118
119            $vis const fn to_str(&self) -> &'static str {
120                match self {
121                    $(Self::$variant => $repr,)*
122                }
123            }
124        }
125
126        impl ::std::fmt::Display for $name {
127            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
128                ::std::fmt::Display::fmt(self.to_str(), f)
129            }
130        }
131
132        impl ::std::str::FromStr for $name {
133            type Err = String;
134
135            fn from_str(s: &str) -> Result<Self, Self::Err> {
136                match s {
137                    $($repr => Ok(Self::$variant),)*
138                    _ => Err(format!(concat!("unknown `", stringify!($name), "` variant: `{}`"), s)),
139                }
140            }
141        }
142    }
143}
144
145pub(crate) use string_enum;