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