1 // Copyright 2012-2015 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.
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.
12 use std::collections::HashSet;
14 use std::old_io::IoError;
17 pub struct RPathConfig<F, G> where
19 G: FnMut(&Path) -> Result<Path, IoError>,
21 pub used_crates: Vec<(ast::CrateNum, Option<Path>)>,
22 pub out_filename: Path,
23 pub is_like_osx: bool,
25 pub get_install_prefix_lib_path: F,
29 pub fn get_rpath_flags<F, G>(config: RPathConfig<F, G>) -> Vec<String> where
31 G: FnMut(&Path) -> Result<Path, IoError>,
33 // No rpath on windows
34 if !config.has_rpath {
38 let mut flags = Vec::new();
40 debug!("preparing the RPATH!");
42 let libs = config.used_crates.clone();
43 let libs = libs.into_iter().filter_map(|(_, l)| {
45 }).collect::<Vec<_>>();
47 let rpaths = get_rpaths(config, &libs[]);
48 flags.push_all(&rpaths_to_flags(&rpaths[])[]);
52 fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> {
53 let mut ret = Vec::new();
55 ret.push(format!("-Wl,-rpath,{}", &(*rpath)[]));
60 fn get_rpaths<F, G>(mut config: RPathConfig<F, G>, libs: &[Path]) -> Vec<String> where
62 G: FnMut(&Path) -> Result<Path, IoError>,
64 debug!("output: {:?}", config.out_filename.display());
67 debug!(" {:?}", libpath.display());
70 // Use relative paths to the libraries. Binaries can be moved
71 // as long as they maintain the relative relationship to the
72 // crates they depend on.
73 let rel_rpaths = get_rpaths_relative_to_output(&mut config, libs);
75 // And a final backup rpath to the global library location.
76 let fallback_rpaths = vec!(get_install_prefix_rpath(config));
78 fn log_rpaths(desc: &str, rpaths: &[String]) {
79 debug!("{} rpaths:", desc);
81 debug!(" {}", *rpath);
85 log_rpaths("relative", &rel_rpaths[]);
86 log_rpaths("fallback", &fallback_rpaths[]);
88 let mut rpaths = rel_rpaths;
89 rpaths.push_all(&fallback_rpaths[]);
92 let rpaths = minimize_rpaths(&rpaths[]);
96 fn get_rpaths_relative_to_output<F, G>(config: &mut RPathConfig<F, G>,
97 libs: &[Path]) -> Vec<String> where
99 G: FnMut(&Path) -> Result<Path, IoError>,
101 libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect()
104 fn get_rpath_relative_to_output<F, G>(config: &mut RPathConfig<F, G>, lib: &Path) -> String where
106 G: FnMut(&Path) -> Result<Path, IoError>,
108 // Mac doesn't appear to support $ORIGIN
109 let prefix = if config.is_like_osx {
115 let cwd = env::current_dir().unwrap();
116 let mut lib = (config.realpath)(&cwd.join(lib)).unwrap();
118 let mut output = (config.realpath)(&cwd.join(&config.out_filename)).unwrap();
120 let relative = lib.path_relative_from(&output);
121 let relative = relative.expect("could not create rpath relative to output");
122 // FIXME (#9639): This needs to handle non-utf8 paths
125 relative.as_str().expect("non-utf8 component in path"))
128 fn get_install_prefix_rpath<F, G>(config: RPathConfig<F, G>) -> String where
130 G: FnMut(&Path) -> Result<Path, IoError>,
132 let path = (config.get_install_prefix_lib_path)();
133 let path = env::current_dir().unwrap().join(&path);
134 // FIXME (#9639): This needs to handle non-utf8 paths
135 path.as_str().expect("non-utf8 component in rpath").to_string()
138 fn minimize_rpaths(rpaths: &[String]) -> Vec<String> {
139 let mut set = HashSet::new();
140 let mut minimized = Vec::new();
141 for rpath in rpaths {
142 if set.insert(&rpath[]) {
143 minimized.push(rpath.clone());
149 #[cfg(all(unix, test))]
151 use super::{RPathConfig};
152 use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
155 fn test_rpaths_to_flags() {
156 let flags = rpaths_to_flags(&[
162 "-Wl,-rpath,path2"]);
166 fn test_minimize1() {
167 let res = minimize_rpaths(&[
168 "rpath1".to_string(),
169 "rpath2".to_string(),
179 fn test_minimize2() {
180 let res = minimize_rpaths(&[
201 #[cfg(any(target_os = "linux", target_os = "android"))]
202 fn test_rpath_relative() {
203 let config = &mut RPathConfig {
204 used_crates: Vec::new(),
205 out_filename: Path::new("bin/rustc"),
206 get_install_prefix_lib_path: || panic!(),
209 realpath: |p| Ok(p.clone())
211 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
212 assert_eq!(res, "$ORIGIN/../lib");
216 #[cfg(any(target_os = "freebsd",
217 target_os = "dragonfly",
218 target_os = "openbsd"))]
219 fn test_rpath_relative() {
220 let config = &mut RPathConfig {
221 used_crates: Vec::new(),
224 out_filename: Path::new("bin/rustc"),
225 get_install_prefix_lib_path: || panic!(),
226 realpath: |p| Ok(p.clone())
228 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
229 assert_eq!(res, "$ORIGIN/../lib");
233 #[cfg(target_os = "macos")]
234 fn test_rpath_relative() {
235 let config = &mut RPathConfig {
236 used_crates: Vec::new(),
239 out_filename: Path::new("bin/rustc"),
240 get_install_prefix_lib_path: || panic!(),
241 realpath: |p| Ok(p.clone())
243 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
244 assert_eq!(res, "@loader_path/../lib");