]> git.lizzy.rs Git - rust.git/blob - src/librustc_back/rpath.rs
rollup merge of #17355 : gamazeps/issue17210
[rust.git] / src / librustc_back / rpath.rs
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 std::collections::HashSet;
13 use std::os;
14 use std::io::IoError;
15 use syntax::abi;
16 use syntax::ast;
17
18 pub struct RPathConfig<'a> {
19     pub os: abi::Os,
20     pub used_crates: Vec<(ast::CrateNum, Option<Path>)>,
21     pub out_filename: Path,
22     pub get_install_prefix_lib_path: ||:'a -> Path,
23     pub realpath: |&Path|:'a -> Result<Path, IoError>
24 }
25
26 pub fn get_rpath_flags(config: RPathConfig) -> Vec<String> {
27
28     // No rpath on windows
29     if config.os == abi::OsWindows {
30         return Vec::new();
31     }
32
33     let mut flags = Vec::new();
34
35     if config.os == abi::OsFreebsd {
36         flags.push_all(["-Wl,-rpath,/usr/local/lib/gcc46".to_string(),
37                         "-Wl,-rpath,/usr/local/lib/gcc44".to_string(),
38                         "-Wl,-z,origin".to_string()]);
39     }
40     else if config.os == abi::OsDragonfly {
41         flags.push_all(["-Wl,-rpath,/usr/lib/gcc47".to_string(),
42                         "-Wl,-rpath,/usr/lib/gcc44".to_string(),
43                         "-Wl,-z,origin".to_string()]);
44     }
45
46
47     debug!("preparing the RPATH!");
48
49     let libs = config.used_crates.clone();
50     let libs = libs.into_iter().filter_map(|(_, l)| {
51         l.map(|p| p.clone())
52     }).collect::<Vec<_>>();
53
54     let rpaths = get_rpaths(config, libs.as_slice());
55     flags.push_all(rpaths_to_flags(rpaths.as_slice()).as_slice());
56     flags
57 }
58
59 fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> {
60     let mut ret = Vec::new();
61     for rpath in rpaths.iter() {
62         ret.push(format!("-Wl,-rpath,{}", (*rpath).as_slice()));
63     }
64     return ret;
65 }
66
67 fn get_rpaths(mut config: RPathConfig,
68               libs: &[Path]) -> Vec<String> {
69     debug!("output: {}", config.out_filename.display());
70     debug!("libs:");
71     for libpath in libs.iter() {
72         debug!("    {}", libpath.display());
73     }
74
75     // Use relative paths to the libraries. Binaries can be moved
76     // as long as they maintain the relative relationship to the
77     // crates they depend on.
78     let rel_rpaths = get_rpaths_relative_to_output(&mut config, libs);
79
80     // And a final backup rpath to the global library location.
81     let fallback_rpaths = vec!(get_install_prefix_rpath(config));
82
83     fn log_rpaths(desc: &str, rpaths: &[String]) {
84         debug!("{} rpaths:", desc);
85         for rpath in rpaths.iter() {
86             debug!("    {}", *rpath);
87         }
88     }
89
90     log_rpaths("relative", rel_rpaths.as_slice());
91     log_rpaths("fallback", fallback_rpaths.as_slice());
92
93     let mut rpaths = rel_rpaths;
94     rpaths.push_all(fallback_rpaths.as_slice());
95
96     // Remove duplicates
97     let rpaths = minimize_rpaths(rpaths.as_slice());
98     return rpaths;
99 }
100
101 fn get_rpaths_relative_to_output(config: &mut RPathConfig,
102                                  libs: &[Path]) -> Vec<String> {
103     libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect()
104 }
105
106 fn get_rpath_relative_to_output(config: &mut RPathConfig,
107                                 lib: &Path) -> String {
108     use std::os;
109
110     assert!(config.os != abi::OsWindows);
111
112     // Mac doesn't appear to support $ORIGIN
113     let prefix = match config.os {
114         abi::OsAndroid | abi::OsLinux | abi::OsFreebsd | abi::OsDragonfly
115                           => "$ORIGIN",
116         abi::OsMacos => "@loader_path",
117         abi::OsWindows | abi::OsiOS => unreachable!()
118     };
119
120     let mut lib = (config.realpath)(&os::make_absolute(lib)).unwrap();
121     lib.pop();
122     let mut output = (config.realpath)(&os::make_absolute(&config.out_filename)).unwrap();
123     output.pop();
124     let relative = lib.path_relative_from(&output);
125     let relative = relative.expect("could not create rpath relative to output");
126     // FIXME (#9639): This needs to handle non-utf8 paths
127     format!("{}/{}",
128             prefix,
129             relative.as_str().expect("non-utf8 component in path"))
130 }
131
132 fn get_install_prefix_rpath(config: RPathConfig) -> String {
133     let path = (config.get_install_prefix_lib_path)();
134     let path = os::make_absolute(&path);
135     // FIXME (#9639): This needs to handle non-utf8 paths
136     path.as_str().expect("non-utf8 component in rpath").to_string()
137 }
138
139 fn minimize_rpaths(rpaths: &[String]) -> Vec<String> {
140     let mut set = HashSet::new();
141     let mut minimized = Vec::new();
142     for rpath in rpaths.iter() {
143         if set.insert(rpath.as_slice()) {
144             minimized.push(rpath.clone());
145         }
146     }
147     minimized
148 }
149
150 #[cfg(unix, test)]
151 mod test {
152     use super::{RPathConfig};
153     use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
154     use syntax::abi;
155
156     #[test]
157     fn test_rpaths_to_flags() {
158         let flags = rpaths_to_flags([
159             "path1".to_string(),
160             "path2".to_string()
161         ]);
162         assert_eq!(flags,
163                    vec!("-Wl,-rpath,path1".to_string(),
164                         "-Wl,-rpath,path2".to_string()));
165     }
166
167     #[test]
168     fn test_minimize1() {
169         let res = minimize_rpaths([
170             "rpath1".to_string(),
171             "rpath2".to_string(),
172             "rpath1".to_string()
173         ]);
174         assert!(res.as_slice() == [
175             "rpath1".to_string(),
176             "rpath2".to_string()
177         ]);
178     }
179
180     #[test]
181     fn test_minimize2() {
182         let res = minimize_rpaths([
183             "1a".to_string(),
184             "2".to_string(),
185             "2".to_string(),
186             "1a".to_string(),
187             "4a".to_string(),
188             "1a".to_string(),
189             "2".to_string(),
190             "3".to_string(),
191             "4a".to_string(),
192             "3".to_string()
193         ]);
194         assert!(res.as_slice() == [
195             "1a".to_string(),
196             "2".to_string(),
197             "4a".to_string(),
198             "3".to_string()
199         ]);
200     }
201
202     #[test]
203     #[cfg(target_os = "linux")]
204     #[cfg(target_os = "android")]
205     fn test_rpath_relative() {
206         let config = &mut RPathConfig {
207             os: abi::OsLinux,
208             used_crates: Vec::new(),
209             out_filename: Path::new("bin/rustc"),
210             get_install_prefix_lib_path: || fail!(),
211             realpath: |p| Ok(p.clone())
212         };
213         let res = get_rpath_relative_to_output(config, &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 config = &mut RPathConfig {
221             os: abi::OsFreebsd,
222             used_crates: Vec::new(),
223             out_filename: Path::new("bin/rustc"),
224             get_install_prefix_lib_path: || fail!(),
225             realpath: |p| Ok(p.clone())
226         };
227         let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
228         assert_eq!(res.as_slice(), "$ORIGIN/../lib");
229     }
230
231     #[test]
232     #[cfg(target_os = "dragonfly")]
233     fn test_rpath_relative() {
234         let config = &mut RPathConfig {
235             os: abi::OsDragonfly,
236             used_crates: Vec::new(),
237             out_filename: Path::new("bin/rustc"),
238             get_install_prefix_lib_path: || fail!(),
239             realpath: |p| Ok(p.clone())
240         };
241         let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
242         assert_eq!(res.as_slice(), "$ORIGIN/../lib");
243     }
244
245     #[test]
246     #[cfg(target_os = "macos")]
247     fn test_rpath_relative() {
248         let config = &mut RPathConfig {
249             os: abi::OsMacos,
250             used_crates: Vec::new(),
251             out_filename: Path::new("bin/rustc"),
252             get_install_prefix_lib_path: || fail!(),
253             realpath: |p| Ok(p.clone())
254         };
255         let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
256         assert_eq!(res.as_slice(), "@loader_path/../lib");
257     }
258 }