]>
Commit | Line | Data |
---|---|---|
a7813a04 XL |
1 | //! Various utility functions used throughout rustbuild. |
2 | //! | |
3 | //! Simple things like testing the various filesystem operations here and there, | |
4 | //! not a lot of interesting happenings here unfortunately. | |
5 | ||
7453a54e | 6 | use std::env; |
abe05a73 | 7 | use std::str; |
83c7162d | 8 | use std::fs; |
416331ca | 9 | use std::io; |
5bcae85e | 10 | use std::path::{Path, PathBuf}; |
7453a54e | 11 | use std::process::Command; |
416331ca | 12 | use std::time::Instant; |
7453a54e | 13 | |
48663c56 XL |
14 | use build_helper::t; |
15 | ||
0731742a XL |
16 | use crate::config::Config; |
17 | use crate::builder::Builder; | |
60c5eb7d | 18 | use crate::cache::Interned; |
7453a54e | 19 | |
a7813a04 | 20 | /// Returns the `name` as the filename of a static library for `target`. |
7453a54e | 21 | pub fn staticlib(name: &str, target: &str) -> String { |
9e0c209e | 22 | if target.contains("windows") { |
7453a54e SL |
23 | format!("{}.lib", name) |
24 | } else { | |
25 | format!("lib{}.a", name) | |
26 | } | |
27 | } | |
28 | ||
7453a54e SL |
29 | /// Given an executable called `name`, return the filename for the |
30 | /// executable for a particular target. | |
31 | pub fn exe(name: &str, target: &str) -> String { | |
32 | if target.contains("windows") { | |
33 | format!("{}.exe", name) | |
34 | } else { | |
35 | name.to_string() | |
36 | } | |
37 | } | |
38 | ||
9fa01778 | 39 | /// Returns `true` if the file name given looks like a dynamic library. |
7453a54e SL |
40 | pub fn is_dylib(name: &str) -> bool { |
41 | name.ends_with(".dylib") || name.ends_with(".so") || name.ends_with(".dll") | |
42 | } | |
43 | ||
a7813a04 XL |
44 | /// Returns the corresponding relative library directory that the compiler's |
45 | /// dylibs will be found in. | |
7453a54e SL |
46 | pub fn libdir(target: &str) -> &'static str { |
47 | if target.contains("windows") {"bin"} else {"lib"} | |
48 | } | |
49 | ||
a7813a04 | 50 | /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path. |
7453a54e SL |
51 | pub fn add_lib_path(path: Vec<PathBuf>, cmd: &mut Command) { |
52 | let mut list = dylib_path(); | |
53 | for path in path { | |
54 | list.insert(0, path); | |
55 | } | |
56 | cmd.env(dylib_path_var(), t!(env::join_paths(list))); | |
57 | } | |
58 | ||
5bcae85e SL |
59 | /// Returns the environment variable which the dynamic library lookup path |
60 | /// resides in for this platform. | |
61 | pub fn dylib_path_var() -> &'static str { | |
62 | if cfg!(target_os = "windows") { | |
63 | "PATH" | |
64 | } else if cfg!(target_os = "macos") { | |
65 | "DYLD_LIBRARY_PATH" | |
7cac9316 XL |
66 | } else if cfg!(target_os = "haiku") { |
67 | "LIBRARY_PATH" | |
5bcae85e SL |
68 | } else { |
69 | "LD_LIBRARY_PATH" | |
70 | } | |
71 | } | |
72 | ||
73 | /// Parses the `dylib_path_var()` environment variable, returning a list of | |
74 | /// paths that are members of this lookup path. | |
75 | pub fn dylib_path() -> Vec<PathBuf> { | |
9fa01778 XL |
76 | let var = match env::var_os(dylib_path_var()) { |
77 | Some(v) => v, | |
78 | None => return vec![], | |
79 | }; | |
80 | env::split_paths(&var).collect() | |
5bcae85e | 81 | } |
c30ab7b3 SL |
82 | |
83 | /// `push` all components to `buf`. On windows, append `.exe` to the last component. | |
84 | pub fn push_exe_path(mut buf: PathBuf, components: &[&str]) -> PathBuf { | |
85 | let (&file, components) = components.split_last().expect("at least one component required"); | |
86 | let mut file = file.to_owned(); | |
87 | ||
88 | if cfg!(windows) { | |
89 | file.push_str(".exe"); | |
90 | } | |
91 | ||
8faf50e0 | 92 | buf.extend(components); |
c30ab7b3 SL |
93 | buf.push(file); |
94 | ||
95 | buf | |
96 | } | |
476ff2be | 97 | |
83c7162d | 98 | pub struct TimeIt(bool, Instant); |
476ff2be SL |
99 | |
100 | /// Returns an RAII structure that prints out how long it took to drop. | |
9fa01778 | 101 | pub fn timeit(builder: &Builder<'_>) -> TimeIt { |
83c7162d | 102 | TimeIt(builder.config.dry_run, Instant::now()) |
476ff2be SL |
103 | } |
104 | ||
105 | impl Drop for TimeIt { | |
106 | fn drop(&mut self) { | |
83c7162d XL |
107 | let time = self.1.elapsed(); |
108 | if !self.0 { | |
109 | println!("\tfinished in {}.{:03}", | |
110 | time.as_secs(), | |
111 | time.subsec_nanos() / 1_000_000); | |
112 | } | |
476ff2be SL |
113 | } |
114 | } | |
8bb4bdeb XL |
115 | |
116 | /// Symlinks two directories, using junctions on Windows and normal symlinks on | |
117 | /// Unix. | |
83c7162d XL |
118 | pub fn symlink_dir(config: &Config, src: &Path, dest: &Path) -> io::Result<()> { |
119 | if config.dry_run { return Ok(()); } | |
8bb4bdeb XL |
120 | let _ = fs::remove_dir(dest); |
121 | return symlink_dir_inner(src, dest); | |
122 | ||
123 | #[cfg(not(windows))] | |
124 | fn symlink_dir_inner(src: &Path, dest: &Path) -> io::Result<()> { | |
125 | use std::os::unix::fs; | |
126 | fs::symlink(src, dest) | |
127 | } | |
128 | ||
129 | // Creating a directory junction on windows involves dealing with reparse | |
130 | // points and the DeviceIoControl function, and this code is a skeleton of | |
131 | // what can be found here: | |
132 | // | |
133 | // http://www.flexhex.com/docs/articles/hard-links.phtml | |
134 | // | |
135 | // Copied from std | |
136 | #[cfg(windows)] | |
b7449926 | 137 | #[allow(nonstandard_style)] |
8bb4bdeb XL |
138 | fn symlink_dir_inner(target: &Path, junction: &Path) -> io::Result<()> { |
139 | use std::ptr; | |
140 | use std::ffi::OsStr; | |
141 | use std::os::windows::ffi::OsStrExt; | |
142 | ||
143 | const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024; | |
144 | const GENERIC_WRITE: DWORD = 0x40000000; | |
145 | const OPEN_EXISTING: DWORD = 3; | |
146 | const FILE_FLAG_OPEN_REPARSE_POINT: DWORD = 0x00200000; | |
147 | const FILE_FLAG_BACKUP_SEMANTICS: DWORD = 0x02000000; | |
148 | const FSCTL_SET_REPARSE_POINT: DWORD = 0x900a4; | |
149 | const IO_REPARSE_TAG_MOUNT_POINT: DWORD = 0xa0000003; | |
150 | const FILE_SHARE_DELETE: DWORD = 0x4; | |
151 | const FILE_SHARE_READ: DWORD = 0x1; | |
152 | const FILE_SHARE_WRITE: DWORD = 0x2; | |
153 | ||
154 | type BOOL = i32; | |
155 | type DWORD = u32; | |
156 | type HANDLE = *mut u8; | |
157 | type LPCWSTR = *const u16; | |
158 | type LPDWORD = *mut DWORD; | |
159 | type LPOVERLAPPED = *mut u8; | |
160 | type LPSECURITY_ATTRIBUTES = *mut u8; | |
161 | type LPVOID = *mut u8; | |
162 | type WCHAR = u16; | |
163 | type WORD = u16; | |
164 | ||
165 | #[repr(C)] | |
166 | struct REPARSE_MOUNTPOINT_DATA_BUFFER { | |
167 | ReparseTag: DWORD, | |
168 | ReparseDataLength: DWORD, | |
169 | Reserved: WORD, | |
170 | ReparseTargetLength: WORD, | |
171 | ReparseTargetMaximumLength: WORD, | |
172 | Reserved1: WORD, | |
173 | ReparseTarget: WCHAR, | |
174 | } | |
175 | ||
176 | extern "system" { | |
177 | fn CreateFileW(lpFileName: LPCWSTR, | |
178 | dwDesiredAccess: DWORD, | |
179 | dwShareMode: DWORD, | |
180 | lpSecurityAttributes: LPSECURITY_ATTRIBUTES, | |
181 | dwCreationDisposition: DWORD, | |
182 | dwFlagsAndAttributes: DWORD, | |
183 | hTemplateFile: HANDLE) | |
184 | -> HANDLE; | |
185 | fn DeviceIoControl(hDevice: HANDLE, | |
186 | dwIoControlCode: DWORD, | |
187 | lpInBuffer: LPVOID, | |
188 | nInBufferSize: DWORD, | |
189 | lpOutBuffer: LPVOID, | |
190 | nOutBufferSize: DWORD, | |
191 | lpBytesReturned: LPDWORD, | |
192 | lpOverlapped: LPOVERLAPPED) -> BOOL; | |
0531ce1d | 193 | fn CloseHandle(hObject: HANDLE) -> BOOL; |
8bb4bdeb XL |
194 | } |
195 | ||
196 | fn to_u16s<S: AsRef<OsStr>>(s: S) -> io::Result<Vec<u16>> { | |
197 | Ok(s.as_ref().encode_wide().chain(Some(0)).collect()) | |
198 | } | |
199 | ||
200 | // We're using low-level APIs to create the junction, and these are more | |
201 | // picky about paths. For example, forward slashes cannot be used as a | |
202 | // path separator, so we should try to canonicalize the path first. | |
a1dfa0c6 | 203 | let target = fs::canonicalize(target)?; |
8bb4bdeb | 204 | |
a1dfa0c6 | 205 | fs::create_dir(junction)?; |
8bb4bdeb | 206 | |
a1dfa0c6 | 207 | let path = to_u16s(junction)?; |
8bb4bdeb XL |
208 | |
209 | unsafe { | |
210 | let h = CreateFileW(path.as_ptr(), | |
211 | GENERIC_WRITE, | |
212 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
dc9dc135 | 213 | ptr::null_mut(), |
8bb4bdeb XL |
214 | OPEN_EXISTING, |
215 | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, | |
216 | ptr::null_mut()); | |
217 | ||
218 | let mut data = [0u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; | |
ea8adc8c | 219 | let db = data.as_mut_ptr() |
8bb4bdeb | 220 | as *mut REPARSE_MOUNTPOINT_DATA_BUFFER; |
2c00a5a8 | 221 | let buf = &mut (*db).ReparseTarget as *mut u16; |
8bb4bdeb XL |
222 | let mut i = 0; |
223 | // FIXME: this conversion is very hacky | |
224 | let v = br"\??\"; | |
225 | let v = v.iter().map(|x| *x as u16); | |
226 | for c in v.chain(target.as_os_str().encode_wide().skip(4)) { | |
227 | *buf.offset(i) = c; | |
228 | i += 1; | |
229 | } | |
230 | *buf.offset(i) = 0; | |
231 | i += 1; | |
232 | (*db).ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; | |
233 | (*db).ReparseTargetMaximumLength = (i * 2) as WORD; | |
234 | (*db).ReparseTargetLength = ((i - 1) * 2) as WORD; | |
235 | (*db).ReparseDataLength = | |
236 | (*db).ReparseTargetLength as DWORD + 12; | |
237 | ||
238 | let mut ret = 0; | |
239 | let res = DeviceIoControl(h as *mut _, | |
240 | FSCTL_SET_REPARSE_POINT, | |
241 | data.as_ptr() as *mut _, | |
242 | (*db).ReparseDataLength + 8, | |
243 | ptr::null_mut(), 0, | |
244 | &mut ret, | |
245 | ptr::null_mut()); | |
246 | ||
0531ce1d | 247 | let out = if res == 0 { |
8bb4bdeb XL |
248 | Err(io::Error::last_os_error()) |
249 | } else { | |
250 | Ok(()) | |
0531ce1d XL |
251 | }; |
252 | CloseHandle(h); | |
253 | out | |
8bb4bdeb XL |
254 | } |
255 | } | |
256 | } | |
7cac9316 | 257 | |
7cac9316 XL |
258 | /// The CI environment rustbuild is running in. This mainly affects how the logs |
259 | /// are printed. | |
260 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
261 | pub enum CiEnv { | |
262 | /// Not a CI environment. | |
263 | None, | |
dc9dc135 XL |
264 | /// The Azure Pipelines environment, for Linux (including Docker), Windows, and macOS builds. |
265 | AzurePipelines, | |
60c5eb7d XL |
266 | /// The GitHub Actions environment, for Linux (including Docker), Windows and macOS builds. |
267 | GitHubActions, | |
7cac9316 XL |
268 | } |
269 | ||
270 | impl CiEnv { | |
271 | /// Obtains the current CI environment. | |
272 | pub fn current() -> CiEnv { | |
416331ca | 273 | if env::var("TF_BUILD").ok().map_or(false, |e| &*e == "True") { |
dc9dc135 | 274 | CiEnv::AzurePipelines |
60c5eb7d XL |
275 | } else if env::var("GITHUB_ACTIONS").ok().map_or(false, |e| &*e == "true") { |
276 | CiEnv::GitHubActions | |
7cac9316 XL |
277 | } else { |
278 | CiEnv::None | |
279 | } | |
280 | } | |
281 | ||
282 | /// If in a CI environment, forces the command to run with colors. | |
283 | pub fn force_coloring_in_ci(self, cmd: &mut Command) { | |
284 | if self != CiEnv::None { | |
285 | // Due to use of stamp/docker, the output stream of rustbuild is not | |
286 | // a TTY in CI, so coloring is by-default turned off. | |
287 | // The explicit `TERM=xterm` environment is needed for | |
288 | // `--color always` to actually work. This env var was lost when | |
289 | // compiling through the Makefile. Very strange. | |
290 | cmd.env("TERM", "xterm").args(&["--color", "always"]); | |
291 | } | |
292 | } | |
041b39d2 | 293 | } |
dc9dc135 XL |
294 | |
295 | pub fn forcing_clang_based_tests() -> bool { | |
296 | if let Some(var) = env::var_os("RUSTBUILD_FORCE_CLANG_BASED_TESTS") { | |
297 | match &var.to_string_lossy().to_lowercase()[..] { | |
298 | "1" | "yes" | "on" => true, | |
299 | "0" | "no" | "off" => false, | |
300 | other => { | |
301 | // Let's make sure typos don't go unnoticed | |
302 | panic!("Unrecognized option '{}' set in \ | |
303 | RUSTBUILD_FORCE_CLANG_BASED_TESTS", other) | |
304 | } | |
305 | } | |
306 | } else { | |
307 | false | |
308 | } | |
309 | } | |
60c5eb7d XL |
310 | |
311 | pub fn use_host_linker(target: &Interned<String>) -> bool { | |
312 | // FIXME: this information should be gotten by checking the linker flavor | |
313 | // of the rustc target | |
314 | !( | |
315 | target.contains("emscripten") || | |
316 | target.contains("wasm32") || | |
317 | target.contains("nvptx") || | |
318 | target.contains("fortanix") || | |
319 | target.contains("fuchsia") | |
320 | ) | |
321 | } |