Skip to main content

compiletest/
util.rs

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