(index<- )        ./librustc/back/rpath.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   1  // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
   2  // file at the top-level directory of this distribution and at
   3  // http://rust-lang.org/COPYRIGHT.
   4  //
   5  // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
   6  // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
   7  // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
   8  // option. This file may not be copied, modified, or distributed
   9  // except according to those terms.
  10  
  11  
  12  use driver::session::Session;
  13  use metadata::cstore;
  14  use metadata::filesearch;
  15  use util::fs;
  16  
  17  use collections::HashSet;
  18  use std::os;
  19  use syntax::abi;
  20  
  21  fn not_win32(osabi::Os) -> bool {
  22    os != abi::OsWin32
  23  }
  24  
  25  pub fn get_rpath_flags(sess: &Session, out_filename: &Path) -> Vec<~str> {
  26      let os = sess.targ_cfg.os;
  27  
  28      // No rpath on windows
  29      if os == abi::OsWin32 {
  30          return Vec::new();
  31      }
  32  
  33      let mut flags = Vec::new();
  34  
  35      if sess.targ_cfg.os == abi::OsFreebsd {
  36          flags.push_all(["-Wl,-rpath,/usr/local/lib/gcc46".to_owned(),
  37                          "-Wl,-rpath,/usr/local/lib/gcc44".to_owned(),
  38                          "-Wl,-z,origin".to_owned()]);
  39      }
  40  
  41      debug!("preparing the RPATH!");
  42  
  43      let sysroot = sess.sysroot();
  44      let output = out_filename;
  45      let libs = sess.cstore.get_used_crates(cstore::RequireDynamic);
  46      let libs = libs.move_iter().filter_map(|(_, l)| {
  47          l.map(|p| p.clone())
  48      }).collect::<Vec<_>>();
  49  
  50      let rpaths = get_rpaths(os, sysroot, output, libs.as_slice(),
  51                              sess.opts.target_triple);
  52      flags.push_all(rpaths_to_flags(rpaths.as_slice()).as_slice());
  53      flags
  54  }
  55  
  56  pub fn rpaths_to_flags(rpaths: &[~str]) -> Vec<~str> {
  57      let mut ret = Vec::new();
  58      for rpath in rpaths.iter() {
  59          ret.push("-Wl,-rpath," + *rpath);
  60      }
  61      return ret;
  62  }
  63  
  64  fn get_rpaths(osabi::Os,
  65                sysroot: &Path,
  66                output: &Path,
  67                libs: &[Path],
  68                target_triple: &str) -> Vec<~str> {
  69      debug!("sysroot: {}", sysroot.display());
  70      debug!("output: {}", output.display());
  71      debug!("libs:");
  72      for libpath in libs.iter() {
  73          debug!("    {}", libpath.display());
  74      }
  75      debug!("target_triple: {}", target_triple);
  76  
  77      // Use relative paths to the libraries. Binaries can be moved
  78      // as long as they maintain the relative relationship to the
  79      // crates they depend on.
  80      let rel_rpaths = get_rpaths_relative_to_output(os, output, libs);
  81  
  82      // And a final backup rpath to the global library location.
  83      let fallback_rpaths = vec!(get_install_prefix_rpath(sysroot, target_triple));
  84  
  85      fn log_rpaths(desc: &str, rpaths: &[~str]) {
  86          debug!("{} rpaths:", desc);
  87          for rpath in rpaths.iter() {
  88              debug!("    {}", *rpath);
  89          }
  90      }
  91  
  92      log_rpaths("relative", rel_rpaths.as_slice());
  93      log_rpaths("fallback", fallback_rpaths.as_slice());
  94  
  95      let mut rpaths = rel_rpaths;
  96      rpaths.push_all(fallback_rpaths.as_slice());
  97  
  98      // Remove duplicates
  99      let rpaths = minimize_rpaths(rpaths.as_slice());
 100      return rpaths;
 101  }
 102  
 103  fn get_rpaths_relative_to_output(osabi::Os,
 104                                   output: &Path,
 105                                   libs: &[Path]) -> Vec<~str> {
 106      libs.iter().map(|a| get_rpath_relative_to_output(os, output, a)).collect()
 107  }
 108  
 109  pub fn get_rpath_relative_to_output(osabi::Os,
 110                                      output: &Path,
 111                                      lib: &Path)
 112                                   -> ~str {
 113      use std::os;
 114  
 115      assert!(not_win32(os));
 116  
 117      // Mac doesn't appear to support $ORIGIN
 118      let prefix = match os {
 119          abi::OsAndroid | abi::OsLinux | abi::OsFreebsd
 120                            => "$ORIGIN",
 121          abi::OsMacos => "@loader_path",
 122          abi::OsWin32 => unreachable!()
 123      };
 124  
 125      let mut lib = fs::realpath(&os::make_absolute(lib)).unwrap();
 126      lib.pop();
 127      let mut output = fs::realpath(&os::make_absolute(output)).unwrap();
 128      output.pop();
 129      let relative = lib.path_relative_from(&output);
 130      let relative = relative.expect("could not create rpath relative to output");
 131      // FIXME (#9639): This needs to handle non-utf8 paths
 132      prefix+"/"+relative.as_str().expect("non-utf8 component in path")
 133  }
 134  
 135  pub fn get_install_prefix_rpath(sysroot: &Path, target_triple: &str) -> ~str {
 136      let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX");
 137  
 138      let tlib = filesearch::relative_target_lib_path(sysroot, target_triple);
 139      let mut path = Path::new(install_prefix);
 140      path.push(&tlib);
 141      let path = os::make_absolute(&path);
 142      // FIXME (#9639): This needs to handle non-utf8 paths
 143      path.as_str().expect("non-utf8 component in rpath").to_owned()
 144  }
 145  
 146  pub fn minimize_rpaths(rpaths: &[~str]) -> Vec<~str> {
 147      let mut set = HashSet::new();
 148      let mut minimized = Vec::new();
 149      for rpath in rpaths.iter() {
 150          if set.insert(rpath.as_slice()) {
 151              minimized.push(rpath.clone());
 152          }
 153      }
 154      minimized
 155  }
 156  
 157  #[cfg(unix, test)]
 158  mod test {
 159      use back::rpath::get_install_prefix_rpath;
 160      use back::rpath::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
 161      use syntax::abi;
 162      use metadata::filesearch;
 163  
 164      #[test]
 165      fn test_rpaths_to_flags() {
 166          let flags = rpaths_to_flags(["path1".to_owned(), "path2".to_owned()]);
 167          assert_eq!(flags, vec!("-Wl,-rpath,path1".to_owned(), "-Wl,-rpath,path2".to_owned()));
 168      }
 169  
 170      #[test]
 171      fn test_prefix_rpath() {
 172          let sysroot = filesearch::get_or_default_sysroot();
 173          let res = get_install_prefix_rpath(&sysroot, "triple");
 174          let mut d = Path::new((option_env!("CFG_PREFIX")).expect("CFG_PREFIX"));
 175          d.push("lib");
 176          d.push(filesearch::rustlibdir());
 177          d.push("triple/lib");
 178          debug!("test_prefix_path: {} vs. {}",
 179                 res,
 180                 d.display());
 181          assert!(res.as_bytes().ends_with(d.as_vec()));
 182      }
 183  
 184      #[test]
 185      fn test_prefix_rpath_abs() {
 186          let sysroot = filesearch::get_or_default_sysroot();
 187          let res = get_install_prefix_rpath(&sysroot, "triple");
 188          assert!(Path::new(res).is_absolute());
 189      }
 190  
 191      #[test]
 192      fn test_minimize1() {
 193          let res = minimize_rpaths(["rpath1".to_owned(), "rpath2".to_owned(), "rpath1".to_owned()]);
 194          assert!(res.as_slice() == ["rpath1".to_owned(), "rpath2".to_owned()]);
 195      }
 196  
 197      #[test]
 198      fn test_minimize2() {
 199          let res = minimize_rpaths(["1a".to_owned(), "2".to_owned(),  "2".to_owned(),
 200                                     "1a".to_owned(), "4a".to_owned(), "1a".to_owned(),
 201                                     "2".to_owned(),  "3".to_owned(),  "4a".to_owned(),
 202                                     "3".to_owned()]);
 203          assert!(res.as_slice() == ["1a".to_owned(), "2".to_owned(), "4a".to_owned(),
 204                                     "3".to_owned()]);
 205      }
 206  
 207      #[test]
 208      #[cfg(target_os = "linux")]
 209      #[cfg(target_os = "android")]
 210      fn test_rpath_relative() {
 211        let o = abi::OsLinux;
 212        let res = get_rpath_relative_to_output(o,
 213              &Path::new("bin/rustc"), &Path::new("lib/libstd.so"));
 214        assert_eq!(res.as_slice(), "$ORIGIN/../lib");
 215      }
 216  
 217      #[test]
 218      #[cfg(target_os = "freebsd")]
 219      fn test_rpath_relative() {
 220          let o = abi::OsFreebsd;
 221          let res = get_rpath_relative_to_output(o,
 222              &Path::new("bin/rustc"), &Path::new("lib/libstd.so"));
 223          assert_eq!(res.as_slice(), "$ORIGIN/../lib");
 224      }
 225  
 226      #[test]
 227      #[cfg(target_os = "macos")]
 228      fn test_rpath_relative() {
 229          let o = abi::OsMacos;
 230          let res = get_rpath_relative_to_output(o,
 231                                                 &Path::new("bin/rustc"),
 232                                                 &Path::new("lib/libstd.so"));
 233          assert_eq!(res.as_slice(), "@loader_path/../lib");
 234      }
 235  }