]>
Commit | Line | Data |
---|---|---|
c295e0f8 XL |
1 | //! A module for searching for libraries |
2 | ||
487cf647 | 3 | use smallvec::{smallvec, SmallVec}; |
85aaf69f | 4 | use std::env; |
c34b1796 | 5 | use std::fs; |
c34b1796 | 6 | use std::path::{Path, PathBuf}; |
1a4d82fc | 7 | |
5099ac24 | 8 | use crate::search_paths::{PathKind, SearchPath}; |
dfeec247 | 9 | use rustc_fs_util::fix_windows_verbatim_for_gcc; |
1a4d82fc | 10 | |
c34b1796 | 11 | #[derive(Copy, Clone)] |
1a4d82fc JJ |
12 | pub enum FileMatch { |
13 | FileMatches, | |
14 | FileDoesntMatch, | |
15 | } | |
970d7e83 | 16 | |
532ac7d7 | 17 | #[derive(Clone)] |
1a4d82fc | 18 | pub struct FileSearch<'a> { |
0731742a XL |
19 | sysroot: &'a Path, |
20 | triple: &'a str, | |
21 | search_paths: &'a [SearchPath], | |
22 | tlib_path: &'a SearchPath, | |
23 | kind: PathKind, | |
223e47cc LB |
24 | } |
25 | ||
1a4d82fc | 26 | impl<'a> FileSearch<'a> { |
0731742a XL |
27 | pub fn search_paths(&self) -> impl Iterator<Item = &'a SearchPath> { |
28 | let kind = self.kind; | |
dfeec247 XL |
29 | self.search_paths |
30 | .iter() | |
0731742a XL |
31 | .filter(move |sp| sp.kind.matches(kind)) |
32 | .chain(std::iter::once(self.tlib_path)) | |
223e47cc LB |
33 | } |
34 | ||
c34b1796 | 35 | pub fn get_lib_path(&self) -> PathBuf { |
1a4d82fc JJ |
36 | make_target_lib_path(self.sysroot, self.triple) |
37 | } | |
223e47cc | 38 | |
f035d41b XL |
39 | pub fn get_self_contained_lib_path(&self) -> PathBuf { |
40 | self.get_lib_path().join("self-contained") | |
41 | } | |
42 | ||
dfeec247 XL |
43 | pub fn new( |
44 | sysroot: &'a Path, | |
45 | triple: &'a str, | |
5869c6ff | 46 | search_paths: &'a [SearchPath], |
dfeec247 XL |
47 | tlib_path: &'a SearchPath, |
48 | kind: PathKind, | |
49 | ) -> FileSearch<'a> { | |
1a4d82fc | 50 | debug!("using sysroot = {}, triple = {}", sysroot.display(), triple); |
dfeec247 | 51 | FileSearch { sysroot, triple, search_paths, tlib_path, kind } |
223e47cc | 52 | } |
1a4d82fc | 53 | |
c295e0f8 | 54 | /// Returns just the directories within the search paths. |
0731742a | 55 | pub fn search_path_dirs(&self) -> Vec<PathBuf> { |
dfeec247 | 56 | self.search_paths().map(|sp| sp.dir.to_path_buf()).collect() |
1a4d82fc | 57 | } |
223e47cc LB |
58 | } |
59 | ||
0731742a | 60 | pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { |
17df50a5 | 61 | let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple); |
a2a8927a | 62 | PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("lib")]) |
223e47cc LB |
63 | } |
64 | ||
487cf647 FG |
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")]; | |
9c376795 | 125 | let path = current_dll_path().and_then(|s| s.canonicalize().map_err(|e| e.to_string())); |
487cf647 FG |
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 | ||
c295e0f8 | 155 | /// This function checks if sysroot is found using env::args().next(), and if it |
487cf647 FG |
156 | /// is not found, finds sysroot from current rustc_driver dll. |
157 | pub fn get_or_default_sysroot() -> Result<PathBuf, String> { | |
9c376795 | 158 | // Follow symlinks. If the resolved path is relative, make it absolute. |
3dfed10e XL |
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) | |
1a4d82fc JJ |
165 | } |
166 | ||
487cf647 | 167 | fn default_from_rustc_driver_dll() -> Result<PathBuf, String> { |
9c376795 | 168 | let dll = current_dll_path().map(|s| canonicalize(s))?; |
487cf647 FG |
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()) | |
5869c6ff XL |
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 | ||
17df50a5 | 214 | // Pop off `bin/rustc`, obtaining the suspected sysroot. |
5869c6ff XL |
215 | p.pop(); |
216 | p.pop(); | |
17df50a5 XL |
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 } | |
5869c6ff XL |
221 | } |
222 | None => None, | |
3dfed10e | 223 | } |
223e47cc | 224 | } |
5869c6ff | 225 | |
487cf647 | 226 | Ok(from_env_args_next().unwrap_or(default_from_rustc_driver_dll()?)) |
223e47cc | 227 | } |