|
1 | 1 | //! A module for searching for libraries
|
2 | 2 |
|
| 3 | +use smallvec::{smallvec, SmallVec}; |
3 | 4 | use std::env;
|
4 | 5 | use std::fs;
|
5 | 6 | use std::iter::FromIterator;
|
6 | 7 | use std::path::{Path, PathBuf};
|
7 | 8 |
|
8 | 9 | use crate::search_paths::{PathKind, SearchPath};
|
9 |
| -use rustc_fs_util::fix_windows_verbatim_for_gcc; |
10 | 10 |
|
11 | 11 | #[derive(Copy, Clone)]
|
12 | 12 | pub enum FileMatch {
|
@@ -62,29 +62,126 @@ pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
|
62 | 62 | PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("lib")])
|
63 | 63 | }
|
64 | 64 |
|
65 |
| -/// This function checks if sysroot is found using env::args().next(), and if it |
66 |
| -/// is not found, uses env::current_exe() to imply sysroot. |
67 |
| -pub fn get_or_default_sysroot() -> PathBuf { |
68 |
| - // Follow symlinks. If the resolved path is relative, make it absolute. |
69 |
| - fn canonicalize(path: PathBuf) -> PathBuf { |
70 |
| - let path = fs::canonicalize(&path).unwrap_or(path); |
71 |
| - // See comments on this target function, but the gist is that |
72 |
| - // gcc chokes on verbatim paths which fs::canonicalize generates |
73 |
| - // so we try to avoid those kinds of paths. |
74 |
| - fix_windows_verbatim_for_gcc(&path) |
| 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)) |
75 | 82 | }
|
| 83 | +} |
76 | 84 |
|
77 |
| - // Use env::current_exe() to get the path of the executable following |
78 |
| - // symlinks/canonicalizing components. |
79 |
| - fn from_current_exe() -> PathBuf { |
80 |
| - match env::current_exe() { |
81 |
| - Ok(exe) => { |
82 |
| - let mut p = canonicalize(exe); |
83 |
| - p.pop(); |
84 |
| - p.pop(); |
85 |
| - p |
| 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| Ok(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 | + ); |
86 | 148 | }
|
87 |
| - Err(e) => panic!("failed to get current_exe: {e}"), |
| 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 | + fn default_from_rustc_driver_dll() -> Result<PathBuf, String> { |
| 159 | + let dll = |
| 160 | + current_dll_path().and_then(|s| Ok(s.canonicalize().map_err(|e| e.to_string())?))?; |
| 161 | + |
| 162 | + // `dll` will be in one of the following two: |
| 163 | + // - compiler's libdir: $sysroot/lib/*.dll |
| 164 | + // - target's libdir: $sysroot/lib/rustlib/$target/lib/*.dll |
| 165 | + // |
| 166 | + // use `parent` twice to chop off the file name and then also the |
| 167 | + // directory containing the dll |
| 168 | + let dir = dll.parent().and_then(|p| p.parent()).ok_or(format!( |
| 169 | + "Could not move 2 levels upper using `parent()` on {}", |
| 170 | + dll.display() |
| 171 | + ))?; |
| 172 | + |
| 173 | + // if `dir` points target's dir, move up to the sysroot |
| 174 | + if dir.ends_with(crate::config::host_triple()) { |
| 175 | + dir.parent() // chop off `$target` |
| 176 | + .and_then(|p| p.parent()) // chop off `rustlib` |
| 177 | + .and_then(|p| p.parent()) // chop off `lib` |
| 178 | + .map(|s| s.to_owned()) |
| 179 | + .ok_or(format!( |
| 180 | + "Could not move 3 levels upper using `parent()` on {}", |
| 181 | + dir.display() |
| 182 | + )) |
| 183 | + } else { |
| 184 | + Ok(dir.to_owned()) |
88 | 185 | }
|
89 | 186 | }
|
90 | 187 |
|
@@ -118,7 +215,5 @@ pub fn get_or_default_sysroot() -> PathBuf {
|
118 | 215 | }
|
119 | 216 | }
|
120 | 217 |
|
121 |
| - // Check if sysroot is found using env::args().next(), and if is not found, |
122 |
| - // use env::current_exe() to imply sysroot. |
123 |
| - from_env_args_next().unwrap_or_else(from_current_exe) |
| 218 | + Ok(from_env_args_next().unwrap_or(default_from_rustc_driver_dll()?)) |
124 | 219 | }
|
0 commit comments