diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_codegen_ssa/src/back/rpath.rs | 114 | ||||
-rw-r--r-- | compiler/rustc_codegen_ssa/src/back/rpath/tests.rs | 72 |
2 files changed, 186 insertions, 0 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs new file mode 100644 index 000000000..0b5656c9a --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -0,0 +1,114 @@ +use pathdiff::diff_paths; +use rustc_data_structures::fx::FxHashSet; +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; + +pub struct RPathConfig<'a> { + pub libs: &'a [&'a Path], + pub out_filename: PathBuf, + pub is_like_osx: bool, + pub has_rpath: bool, + pub linker_is_gnu: bool, +} + +pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec<String> { + // No rpath on windows + if !config.has_rpath { + return Vec::new(); + } + + debug!("preparing the RPATH!"); + + let rpaths = get_rpaths(config); + let mut flags = rpaths_to_flags(&rpaths); + + if config.linker_is_gnu { + // Use DT_RUNPATH instead of DT_RPATH if available + flags.push("-Wl,--enable-new-dtags".to_owned()); + + // Set DF_ORIGIN for substitute $ORIGIN + flags.push("-Wl,-z,origin".to_owned()); + } + + flags +} + +fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> { + let mut ret = Vec::with_capacity(rpaths.len()); // the minimum needed capacity + + for rpath in rpaths { + if rpath.contains(',') { + ret.push("-Wl,-rpath".into()); + ret.push("-Xlinker".into()); + ret.push(rpath.clone()); + } else { + ret.push(format!("-Wl,-rpath,{}", &(*rpath))); + } + } + + ret +} + +fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec<String> { + 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: &mut RPathConfig<'_>) -> Vec<String> { + config.libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect() +} + +fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> String { + // Mac doesn't appear to support $ORIGIN + let prefix = if config.is_like_osx { "@loader_path" } else { "$ORIGIN" }; + + let cwd = env::current_dir().unwrap(); + let mut lib = fs::canonicalize(&cwd.join(lib)).unwrap_or_else(|_| cwd.join(lib)); + lib.pop(); // strip filename + let mut output = cwd.join(&config.out_filename); + output.pop(); // strip filename + let output = fs::canonicalize(&output).unwrap_or(output); + let relative = path_relative_from(&lib, &output) + .unwrap_or_else(|| panic!("couldn't create relative path from {:?} to {:?}", output, lib)); + // FIXME (#9639): This needs to handle non-utf8 paths + format!("{}/{}", prefix, relative.to_str().expect("non-utf8 component in path")) +} + +// 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: &[String]) -> Vec<String> { + 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; diff --git a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs new file mode 100644 index 000000000..604f19144 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs @@ -0,0 +1,72 @@ +use super::RPathConfig; +use super::{get_rpath_relative_to_output, minimize_rpaths, rpaths_to_flags}; +use std::path::{Path, PathBuf}; + +#[test] +fn test_rpaths_to_flags() { + let flags = rpaths_to_flags(&["path1".to_string(), "path2".to_string()]); + assert_eq!(flags, ["-Wl,-rpath,path1", "-Wl,-rpath,path2"]); +} + +#[test] +fn test_minimize1() { + let res = minimize_rpaths(&["rpath1".to_string(), "rpath2".to_string(), "rpath1".to_string()]); + assert!(res == ["rpath1", "rpath2",]); +} + +#[test] +fn test_minimize2() { + let res = minimize_rpaths(&[ + "1a".to_string(), + "2".to_string(), + "2".to_string(), + "1a".to_string(), + "4a".to_string(), + "1a".to_string(), + "2".to_string(), + "3".to_string(), + "4a".to_string(), + "3".to_string(), + ]); + assert!(res == ["1a", "2", "4a", "3",]); +} + +#[test] +fn test_rpath_relative() { + if cfg!(target_os = "macos") { + let config = &mut RPathConfig { + libs: &[], + has_rpath: true, + is_like_osx: true, + linker_is_gnu: false, + out_filename: PathBuf::from("bin/rustc"), + }; + let res = get_rpath_relative_to_output(config, Path::new("lib/libstd.so")); + assert_eq!(res, "@loader_path/../lib"); + } else { + let config = &mut RPathConfig { + libs: &[], + out_filename: PathBuf::from("bin/rustc"), + has_rpath: true, + is_like_osx: false, + linker_is_gnu: true, + }; + let res = get_rpath_relative_to_output(config, Path::new("lib/libstd.so")); + assert_eq!(res, "$ORIGIN/../lib"); + } +} + +#[test] +fn test_xlinker() { + let args = rpaths_to_flags(&["a/normal/path".to_string(), "a,comma,path".to_string()]); + + assert_eq!( + args, + vec![ + "-Wl,-rpath,a/normal/path".to_string(), + "-Wl,-rpath".to_string(), + "-Xlinker".to_string(), + "a,comma,path".to_string() + ] + ); +} |