]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_session/src/filesearch.rs
Replace a command line flag with an env var to allow tools to initialize the tracing...
[rust.git] / compiler / rustc_session / src / filesearch.rs
1 //! A module for searching for libraries
2
3 use smallvec::{smallvec, SmallVec};
4 use std::env;
5 use std::fs;
6 use std::path::{Path, PathBuf};
7
8 use crate::search_paths::{PathKind, SearchPath};
9 use rustc_fs_util::fix_windows_verbatim_for_gcc;
10
11 #[derive(Copy, Clone)]
12 pub enum FileMatch {
13     FileMatches,
14     FileDoesntMatch,
15 }
16
17 #[derive(Clone)]
18 pub struct FileSearch<'a> {
19     sysroot: &'a Path,
20     triple: &'a str,
21     search_paths: &'a [SearchPath],
22     tlib_path: &'a SearchPath,
23     kind: PathKind,
24 }
25
26 impl<'a> FileSearch<'a> {
27     pub fn search_paths(&self) -> impl Iterator<Item = &'a SearchPath> {
28         let kind = self.kind;
29         self.search_paths
30             .iter()
31             .filter(move |sp| sp.kind.matches(kind))
32             .chain(std::iter::once(self.tlib_path))
33     }
34
35     pub fn get_lib_path(&self) -> PathBuf {
36         make_target_lib_path(self.sysroot, self.triple)
37     }
38
39     pub fn get_self_contained_lib_path(&self) -> PathBuf {
40         self.get_lib_path().join("self-contained")
41     }
42
43     pub fn new(
44         sysroot: &'a Path,
45         triple: &'a str,
46         search_paths: &'a [SearchPath],
47         tlib_path: &'a SearchPath,
48         kind: PathKind,
49     ) -> FileSearch<'a> {
50         debug!("using sysroot = {}, triple = {}", sysroot.display(), triple);
51         FileSearch { sysroot, triple, search_paths, tlib_path, kind }
52     }
53
54     /// Returns just the directories within the search paths.
55     pub fn search_path_dirs(&self) -> Vec<PathBuf> {
56         self.search_paths().map(|sp| sp.dir.to_path_buf()).collect()
57     }
58 }
59
60 pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
61     let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple);
62     PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("lib")])
63 }
64
65 #[cfg(unix)]
66 fn current_dll_path() -> Result<PathBuf, String> {
67     use std::ffi::{CStr, OsStr};
68     use std::os::unix::prelude::*;
69
70     unsafe {
71         let addr = current_dll_path as usize as *mut _;
72         let mut info = std::mem::zeroed();
73         if libc::dladdr(addr, &mut info) == 0 {
74             return Err("dladdr failed".into());
75         }
76         if info.dli_fname.is_null() {
77             return Err("dladdr returned null pointer".into());
78         }
79         let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
80         let os = OsStr::from_bytes(bytes);
81         Ok(PathBuf::from(os))
82     }
83 }
84
85 #[cfg(windows)]
86 fn current_dll_path() -> Result<PathBuf, String> {
87     use std::ffi::OsString;
88     use std::io;
89     use std::os::windows::prelude::*;
90     use std::ptr;
91
92     use winapi::um::libloaderapi::{
93         GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
94     };
95
96     unsafe {
97         let mut module = ptr::null_mut();
98         let r = GetModuleHandleExW(
99             GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
100             current_dll_path as usize as *mut _,
101             &mut module,
102         );
103         if r == 0 {
104             return Err(format!("GetModuleHandleExW failed: {}", io::Error::last_os_error()));
105         }
106         let mut space = Vec::with_capacity(1024);
107         let r = GetModuleFileNameW(module, space.as_mut_ptr(), space.capacity() as u32);
108         if r == 0 {
109             return Err(format!("GetModuleFileNameW failed: {}", io::Error::last_os_error()));
110         }
111         let r = r as usize;
112         if r >= space.capacity() {
113             return Err(format!("our buffer was too small? {}", io::Error::last_os_error()));
114         }
115         space.set_len(r);
116         let os = OsString::from_wide(&space);
117         Ok(PathBuf::from(os))
118     }
119 }
120
121 pub fn sysroot_candidates() -> SmallVec<[PathBuf; 2]> {
122     let target = crate::config::host_triple();
123     let mut sysroot_candidates: SmallVec<[PathBuf; 2]> =
124         smallvec![get_or_default_sysroot().expect("Failed finding sysroot")];
125     let path = current_dll_path().and_then(|s| s.canonicalize().map_err(|e| e.to_string()));
126     if let Ok(dll) = path {
127         // use `parent` twice to chop off the file name and then also the
128         // directory containing the dll which should be either `lib` or `bin`.
129         if let Some(path) = dll.parent().and_then(|p| p.parent()) {
130             // The original `path` pointed at the `rustc_driver` crate's dll.
131             // Now that dll should only be in one of two locations. The first is
132             // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The
133             // other is the target's libdir, for example
134             // `$sysroot/lib/rustlib/$target/lib/*.dll`.
135             //
136             // We don't know which, so let's assume that if our `path` above
137             // ends in `$target` we *could* be in the target libdir, and always
138             // assume that we may be in the main libdir.
139             sysroot_candidates.push(path.to_owned());
140
141             if path.ends_with(target) {
142                 sysroot_candidates.extend(
143                     path.parent() // chop off `$target`
144                         .and_then(|p| p.parent()) // chop off `rustlib`
145                         .and_then(|p| p.parent()) // chop off `lib`
146                         .map(|s| s.to_owned()),
147                 );
148             }
149         }
150     }
151
152     return sysroot_candidates;
153 }
154
155 /// This function checks if sysroot is found using env::args().next(), and if it
156 /// is not found, finds sysroot from current rustc_driver dll.
157 pub fn get_or_default_sysroot() -> Result<PathBuf, String> {
158     // Follow symlinks. If the resolved path is relative, make it absolute.
159     fn canonicalize(path: PathBuf) -> PathBuf {
160         let path = fs::canonicalize(&path).unwrap_or(path);
161         // See comments on this target function, but the gist is that
162         // gcc chokes on verbatim paths which fs::canonicalize generates
163         // so we try to avoid those kinds of paths.
164         fix_windows_verbatim_for_gcc(&path)
165     }
166
167     fn default_from_rustc_driver_dll() -> Result<PathBuf, String> {
168         let dll = current_dll_path().map(|s| canonicalize(s))?;
169
170         // `dll` will be in one of the following two:
171         // - compiler's libdir: $sysroot/lib/*.dll
172         // - target's libdir: $sysroot/lib/rustlib/$target/lib/*.dll
173         //
174         // use `parent` twice to chop off the file name and then also the
175         // directory containing the dll
176         let dir = dll.parent().and_then(|p| p.parent()).ok_or(format!(
177             "Could not move 2 levels upper using `parent()` on {}",
178             dll.display()
179         ))?;
180
181         // if `dir` points target's dir, move up to the sysroot
182         if dir.ends_with(crate::config::host_triple()) {
183             dir.parent() // chop off `$target`
184                 .and_then(|p| p.parent()) // chop off `rustlib`
185                 .and_then(|p| p.parent()) // chop off `lib`
186                 .map(|s| s.to_owned())
187                 .ok_or(format!(
188                     "Could not move 3 levels upper using `parent()` on {}",
189                     dir.display()
190                 ))
191         } else {
192             Ok(dir.to_owned())
193         }
194     }
195
196     // Use env::args().next() to get the path of the executable without
197     // following symlinks/canonicalizing any component. This makes the rustc
198     // binary able to locate Rust libraries in systems using content-addressable
199     // storage (CAS).
200     fn from_env_args_next() -> Option<PathBuf> {
201         match env::args_os().next() {
202             Some(first_arg) => {
203                 let mut p = PathBuf::from(first_arg);
204
205                 // Check if sysroot is found using env::args().next() only if the rustc in argv[0]
206                 // is a symlink (see #79253). We might want to change/remove it to conform with
207                 // https://www.gnu.org/prep/standards/standards.html#Finding-Program-Files in the
208                 // future.
209                 if fs::read_link(&p).is_err() {
210                     // Path is not a symbolic link or does not exist.
211                     return None;
212                 }
213
214                 // Pop off `bin/rustc`, obtaining the suspected sysroot.
215                 p.pop();
216                 p.pop();
217                 // Look for the target rustlib directory in the suspected sysroot.
218                 let mut rustlib_path = rustc_target::target_rustlib_path(&p, "dummy");
219                 rustlib_path.pop(); // pop off the dummy target.
220                 if rustlib_path.exists() { Some(p) } else { None }
221             }
222             None => None,
223         }
224     }
225
226     Ok(from_env_args_next().unwrap_or(default_from_rustc_driver_dll()?))
227 }