| use std::ffi::OsString; | |
| use std::path::{Path, PathBuf}; | |
| use pathdiff::diff_paths; | |
| use rustc_data_structures::fx::FxHashSet; | |
| use rustc_fs_util::try_canonicalize; | |
| use tracing::debug; | |
| pub(super) struct RPathConfig<'a> { | |
| pub libs: &'a [&'a Path], | |
| pub out_filename: PathBuf, | |
| pub is_like_darwin: bool, | |
| pub linker_is_gnu: bool, | |
| } | |
| pub(super) fn get_rpath_linker_args(config: &RPathConfig<'_>) -> Vec<OsString> { | |
| debug!("preparing the RPATH!"); | |
| let rpaths = get_rpaths(config); | |
| let mut args = Vec::with_capacity(rpaths.len() * 2); // the minimum needed capacity | |
| for rpath in rpaths { | |
| args.push("-rpath".into()); | |
| args.push(rpath); | |
| } | |
| if config.linker_is_gnu { | |
| // Use DT_RUNPATH instead of DT_RPATH if available | |
| args.push("--enable-new-dtags".into()); | |
| // Set DF_ORIGIN for substitute $ORIGIN | |
| args.push("-z".into()); | |
| args.push("origin".into()); | |
| } | |
| args | |
| } | |
| fn get_rpaths(config: &RPathConfig<'_>) -> Vec<OsString> { | |
| debug!("output: {:?}", config.out_filename.display()); | |
| debug!("libs:"); | |
| for libpath in config.libs { | |
| debug!(" {:?}", libpath.display()); | |
| } | |
| // Use relative paths to the libraries. Binaries can be moved | |
| // as long as they maintain the relative relationship to the | |
| // crates they depend on. | |
| let rpaths = get_rpaths_relative_to_output(config); | |
| debug!("rpaths:"); | |
| for rpath in &rpaths { | |
| debug!(" {:?}", rpath); | |
| } | |
| // Remove duplicates | |
| minimize_rpaths(&rpaths) | |
| } | |
| fn get_rpaths_relative_to_output(config: &RPathConfig<'_>) -> Vec<OsString> { | |
| config.libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect() | |
| } | |
| fn get_rpath_relative_to_output(config: &RPathConfig<'_>, lib: &Path) -> OsString { | |
| // Mac doesn't appear to support $ORIGIN | |
| let prefix = if config.is_like_darwin { "@loader_path" } else { "$ORIGIN" }; | |
| // Strip filenames | |
| let lib = lib.parent().unwrap(); | |
| let output = config.out_filename.parent().unwrap(); | |
| // If output or lib is empty, just assume it locates in current path | |
| let lib = if lib == Path::new("") { Path::new(".") } else { lib }; | |
| let output = if output == Path::new("") { Path::new(".") } else { output }; | |
| let lib = try_canonicalize(lib).unwrap(); | |
| let output = try_canonicalize(output).unwrap(); | |
| let relative = path_relative_from(&lib, &output) | |
| .unwrap_or_else(|| panic!("couldn't create relative path from {output:?} to {lib:?}")); | |
| let mut rpath = OsString::from(prefix); | |
| rpath.push("/"); | |
| rpath.push(relative); | |
| rpath | |
| } | |
| // This routine is adapted from the *old* Path's `path_relative_from` | |
| // function, which works differently from the new `relative_from` function. | |
| // In particular, this handles the case on unix where both paths are | |
| // absolute but with only the root as the common directory. | |
| fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> { | |
| diff_paths(path, base) | |
| } | |
| fn minimize_rpaths(rpaths: &[OsString]) -> Vec<OsString> { | |
| let mut set = FxHashSet::default(); | |
| let mut minimized = Vec::new(); | |
| for rpath in rpaths { | |
| if set.insert(rpath) { | |
| minimized.push(rpath.clone()); | |
| } | |
| } | |
| minimized | |
| } | |
| #[cfg(all(unix, test))] | |
| mod tests; |