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