]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/util.rs
rustdoc: Hide `self: Box<Self>` in list of deref methods
[rust.git] / src / bootstrap / util.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Various utility functions used throughout rustbuild.
12 //!
13 //! Simple things like testing the various filesystem operations here and there,
14 //! not a lot of interesting happenings here unfortunately.
15
16 use std::env;
17 use std::ffi::OsString;
18 use std::fs;
19 use std::io;
20 use std::path::{Path, PathBuf};
21 use std::process::Command;
22 use std::time::Instant;
23
24 use filetime::{self, FileTime};
25
26 /// Returns the `name` as the filename of a static library for `target`.
27 pub fn staticlib(name: &str, target: &str) -> String {
28     if target.contains("windows") {
29         format!("{}.lib", name)
30     } else {
31         format!("lib{}.a", name)
32     }
33 }
34
35 /// Copies a file from `src` to `dst`, attempting to use hard links and then
36 /// falling back to an actually filesystem copy if necessary.
37 pub fn copy(src: &Path, dst: &Path) {
38     // A call to `hard_link` will fail if `dst` exists, so remove it if it
39     // already exists so we can try to help `hard_link` succeed.
40     let _ = fs::remove_file(&dst);
41
42     // Attempt to "easy copy" by creating a hard link (symlinks don't work on
43     // windows), but if that fails just fall back to a slow `copy` operation.
44     // let res = fs::hard_link(src, dst);
45     let res = fs::copy(src, dst);
46     if let Err(e) = res {
47         panic!("failed to copy `{}` to `{}`: {}", src.display(),
48                dst.display(), e)
49     }
50     let metadata = t!(src.metadata());
51     t!(fs::set_permissions(dst, metadata.permissions()));
52     let atime = FileTime::from_last_access_time(&metadata);
53     let mtime = FileTime::from_last_modification_time(&metadata);
54     t!(filetime::set_file_times(dst, atime, mtime));
55
56 }
57
58 /// Copies the `src` directory recursively to `dst`. Both are assumed to exist
59 /// when this function is called.
60 pub fn cp_r(src: &Path, dst: &Path) {
61     for f in t!(fs::read_dir(src)) {
62         let f = t!(f);
63         let path = f.path();
64         let name = path.file_name().unwrap();
65         let dst = dst.join(name);
66         if t!(f.file_type()).is_dir() {
67             t!(fs::create_dir_all(&dst));
68             cp_r(&path, &dst);
69         } else {
70             let _ = fs::remove_file(&dst);
71             copy(&path, &dst);
72         }
73     }
74 }
75
76 /// Copies the `src` directory recursively to `dst`. Both are assumed to exist
77 /// when this function is called. Unwanted files or directories can be skipped
78 /// by returning `false` from the filter function.
79 pub fn cp_filtered(src: &Path, dst: &Path, filter: &Fn(&Path) -> bool) {
80     // Inner function does the actual work
81     fn recurse(src: &Path, dst: &Path, relative: &Path, filter: &Fn(&Path) -> bool) {
82         for f in t!(fs::read_dir(src)) {
83             let f = t!(f);
84             let path = f.path();
85             let name = path.file_name().unwrap();
86             let dst = dst.join(name);
87             let relative = relative.join(name);
88             // Only copy file or directory if the filter function returns true
89             if filter(&relative) {
90                 if t!(f.file_type()).is_dir() {
91                     let _ = fs::remove_dir_all(&dst);
92                     t!(fs::create_dir(&dst));
93                     recurse(&path, &dst, &relative, filter);
94                 } else {
95                     let _ = fs::remove_file(&dst);
96                     copy(&path, &dst);
97                 }
98             }
99         }
100     }
101     // Immediately recurse with an empty relative path
102     recurse(src, dst, Path::new(""), filter)
103 }
104
105 /// Given an executable called `name`, return the filename for the
106 /// executable for a particular target.
107 pub fn exe(name: &str, target: &str) -> String {
108     if target.contains("windows") {
109         format!("{}.exe", name)
110     } else {
111         name.to_string()
112     }
113 }
114
115 /// Returns whether the file name given looks like a dynamic library.
116 pub fn is_dylib(name: &str) -> bool {
117     name.ends_with(".dylib") || name.ends_with(".so") || name.ends_with(".dll")
118 }
119
120 /// Returns the corresponding relative library directory that the compiler's
121 /// dylibs will be found in.
122 pub fn libdir(target: &str) -> &'static str {
123     if target.contains("windows") {"bin"} else {"lib"}
124 }
125
126 /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path.
127 pub fn add_lib_path(path: Vec<PathBuf>, cmd: &mut Command) {
128     let mut list = dylib_path();
129     for path in path {
130         list.insert(0, path);
131     }
132     cmd.env(dylib_path_var(), t!(env::join_paths(list)));
133 }
134
135 /// Returns the environment variable which the dynamic library lookup path
136 /// resides in for this platform.
137 pub fn dylib_path_var() -> &'static str {
138     if cfg!(target_os = "windows") {
139         "PATH"
140     } else if cfg!(target_os = "macos") {
141         "DYLD_LIBRARY_PATH"
142     } else if cfg!(target_os = "haiku") {
143         "LIBRARY_PATH"
144     } else {
145         "LD_LIBRARY_PATH"
146     }
147 }
148
149 /// Parses the `dylib_path_var()` environment variable, returning a list of
150 /// paths that are members of this lookup path.
151 pub fn dylib_path() -> Vec<PathBuf> {
152     env::split_paths(&env::var_os(dylib_path_var()).unwrap_or(OsString::new()))
153         .collect()
154 }
155
156 /// `push` all components to `buf`. On windows, append `.exe` to the last component.
157 pub fn push_exe_path(mut buf: PathBuf, components: &[&str]) -> PathBuf {
158     let (&file, components) = components.split_last().expect("at least one component required");
159     let mut file = file.to_owned();
160
161     if cfg!(windows) {
162         file.push_str(".exe");
163     }
164
165     for c in components {
166         buf.push(c);
167     }
168
169     buf.push(file);
170
171     buf
172 }
173
174 pub struct TimeIt(Instant);
175
176 /// Returns an RAII structure that prints out how long it took to drop.
177 pub fn timeit() -> TimeIt {
178     TimeIt(Instant::now())
179 }
180
181 impl Drop for TimeIt {
182     fn drop(&mut self) {
183         let time = self.0.elapsed();
184         println!("\tfinished in {}.{:03}",
185                  time.as_secs(),
186                  time.subsec_nanos() / 1_000_000);
187     }
188 }
189
190 /// Symlinks two directories, using junctions on Windows and normal symlinks on
191 /// Unix.
192 pub fn symlink_dir(src: &Path, dest: &Path) -> io::Result<()> {
193     let _ = fs::remove_dir(dest);
194     return symlink_dir_inner(src, dest);
195
196     #[cfg(not(windows))]
197     fn symlink_dir_inner(src: &Path, dest: &Path) -> io::Result<()> {
198         use std::os::unix::fs;
199         fs::symlink(src, dest)
200     }
201
202     // Creating a directory junction on windows involves dealing with reparse
203     // points and the DeviceIoControl function, and this code is a skeleton of
204     // what can be found here:
205     //
206     // http://www.flexhex.com/docs/articles/hard-links.phtml
207     //
208     // Copied from std
209     #[cfg(windows)]
210     #[allow(bad_style)]
211     fn symlink_dir_inner(target: &Path, junction: &Path) -> io::Result<()> {
212         use std::ptr;
213         use std::ffi::OsStr;
214         use std::os::windows::ffi::OsStrExt;
215
216         const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024;
217         const GENERIC_WRITE: DWORD = 0x40000000;
218         const OPEN_EXISTING: DWORD = 3;
219         const FILE_FLAG_OPEN_REPARSE_POINT: DWORD = 0x00200000;
220         const FILE_FLAG_BACKUP_SEMANTICS: DWORD = 0x02000000;
221         const FSCTL_SET_REPARSE_POINT: DWORD = 0x900a4;
222         const IO_REPARSE_TAG_MOUNT_POINT: DWORD = 0xa0000003;
223         const FILE_SHARE_DELETE: DWORD = 0x4;
224         const FILE_SHARE_READ: DWORD = 0x1;
225         const FILE_SHARE_WRITE: DWORD = 0x2;
226
227         type BOOL = i32;
228         type DWORD = u32;
229         type HANDLE = *mut u8;
230         type LPCWSTR = *const u16;
231         type LPDWORD = *mut DWORD;
232         type LPOVERLAPPED = *mut u8;
233         type LPSECURITY_ATTRIBUTES = *mut u8;
234         type LPVOID = *mut u8;
235         type WCHAR = u16;
236         type WORD = u16;
237
238         #[repr(C)]
239         struct REPARSE_MOUNTPOINT_DATA_BUFFER {
240             ReparseTag: DWORD,
241             ReparseDataLength: DWORD,
242             Reserved: WORD,
243             ReparseTargetLength: WORD,
244             ReparseTargetMaximumLength: WORD,
245             Reserved1: WORD,
246             ReparseTarget: WCHAR,
247         }
248
249         extern "system" {
250             fn CreateFileW(lpFileName: LPCWSTR,
251                            dwDesiredAccess: DWORD,
252                            dwShareMode: DWORD,
253                            lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
254                            dwCreationDisposition: DWORD,
255                            dwFlagsAndAttributes: DWORD,
256                            hTemplateFile: HANDLE)
257                            -> HANDLE;
258             fn DeviceIoControl(hDevice: HANDLE,
259                                dwIoControlCode: DWORD,
260                                lpInBuffer: LPVOID,
261                                nInBufferSize: DWORD,
262                                lpOutBuffer: LPVOID,
263                                nOutBufferSize: DWORD,
264                                lpBytesReturned: LPDWORD,
265                                lpOverlapped: LPOVERLAPPED) -> BOOL;
266         }
267
268         fn to_u16s<S: AsRef<OsStr>>(s: S) -> io::Result<Vec<u16>> {
269             Ok(s.as_ref().encode_wide().chain(Some(0)).collect())
270         }
271
272         // We're using low-level APIs to create the junction, and these are more
273         // picky about paths. For example, forward slashes cannot be used as a
274         // path separator, so we should try to canonicalize the path first.
275         let target = try!(fs::canonicalize(target));
276
277         try!(fs::create_dir(junction));
278
279         let path = try!(to_u16s(junction));
280
281         unsafe {
282             let h = CreateFileW(path.as_ptr(),
283                                 GENERIC_WRITE,
284                                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
285                                 0 as *mut _,
286                                 OPEN_EXISTING,
287                                 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
288                                 ptr::null_mut());
289
290             let mut data = [0u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
291             let mut db = data.as_mut_ptr()
292                             as *mut REPARSE_MOUNTPOINT_DATA_BUFFER;
293             let buf = &mut (*db).ReparseTarget as *mut _;
294             let mut i = 0;
295             // FIXME: this conversion is very hacky
296             let v = br"\??\";
297             let v = v.iter().map(|x| *x as u16);
298             for c in v.chain(target.as_os_str().encode_wide().skip(4)) {
299                 *buf.offset(i) = c;
300                 i += 1;
301             }
302             *buf.offset(i) = 0;
303             i += 1;
304             (*db).ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
305             (*db).ReparseTargetMaximumLength = (i * 2) as WORD;
306             (*db).ReparseTargetLength = ((i - 1) * 2) as WORD;
307             (*db).ReparseDataLength =
308                     (*db).ReparseTargetLength as DWORD + 12;
309
310             let mut ret = 0;
311             let res = DeviceIoControl(h as *mut _,
312                                       FSCTL_SET_REPARSE_POINT,
313                                       data.as_ptr() as *mut _,
314                                       (*db).ReparseDataLength + 8,
315                                       ptr::null_mut(), 0,
316                                       &mut ret,
317                                       ptr::null_mut());
318
319             if res == 0 {
320                 Err(io::Error::last_os_error())
321             } else {
322                 Ok(())
323             }
324         }
325     }
326 }