]> git.lizzy.rs Git - rust.git/blob - src/librustc_back/rpath.rs
Auto merge of #22517 - brson:relnotes, r=Gankro
[rust.git] / src / librustc_back / rpath.rs
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.
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::env;
14 use std::old_io::IoError;
15 use syntax::ast;
16
17 pub struct RPathConfig<F, G> where
18     F: FnOnce() -> Path,
19     G: FnMut(&Path) -> Result<Path, IoError>,
20 {
21     pub used_crates: Vec<(ast::CrateNum, Option<Path>)>,
22     pub out_filename: Path,
23     pub is_like_osx: bool,
24     pub has_rpath: bool,
25     pub get_install_prefix_lib_path: F,
26     pub realpath: G,
27 }
28
29 pub fn get_rpath_flags<F, G>(config: RPathConfig<F, G>) -> Vec<String> where
30     F: FnOnce() -> Path,
31     G: FnMut(&Path) -> Result<Path, IoError>,
32 {
33     // No rpath on windows
34     if !config.has_rpath {
35         return Vec::new();
36     }
37
38     let mut flags = Vec::new();
39
40     debug!("preparing the RPATH!");
41
42     let libs = config.used_crates.clone();
43     let libs = libs.into_iter().filter_map(|(_, l)| {
44         l.map(|p| p.clone())
45     }).collect::<Vec<_>>();
46
47     let rpaths = get_rpaths(config, &libs[]);
48     flags.push_all(&rpaths_to_flags(&rpaths[])[]);
49     flags
50 }
51
52 fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> {
53     let mut ret = Vec::new();
54     for rpath in rpaths {
55         ret.push(format!("-Wl,-rpath,{}", &(*rpath)[]));
56     }
57     return ret;
58 }
59
60 fn get_rpaths<F, G>(mut config: RPathConfig<F, G>, libs: &[Path]) -> Vec<String> where
61     F: FnOnce() -> Path,
62     G: FnMut(&Path) -> Result<Path, IoError>,
63 {
64     debug!("output: {:?}", config.out_filename.display());
65     debug!("libs:");
66     for libpath in libs {
67         debug!("    {:?}", libpath.display());
68     }
69
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);
74
75     // And a final backup rpath to the global library location.
76     let fallback_rpaths = vec!(get_install_prefix_rpath(config));
77
78     fn log_rpaths(desc: &str, rpaths: &[String]) {
79         debug!("{} rpaths:", desc);
80         for rpath in rpaths {
81             debug!("    {}", *rpath);
82         }
83     }
84
85     log_rpaths("relative", &rel_rpaths[]);
86     log_rpaths("fallback", &fallback_rpaths[]);
87
88     let mut rpaths = rel_rpaths;
89     rpaths.push_all(&fallback_rpaths[]);
90
91     // Remove duplicates
92     let rpaths = minimize_rpaths(&rpaths[]);
93     return rpaths;
94 }
95
96 fn get_rpaths_relative_to_output<F, G>(config: &mut RPathConfig<F, G>,
97                                        libs: &[Path]) -> Vec<String> where
98     F: FnOnce() -> Path,
99     G: FnMut(&Path) -> Result<Path, IoError>,
100 {
101     libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect()
102 }
103
104 fn get_rpath_relative_to_output<F, G>(config: &mut RPathConfig<F, G>, lib: &Path) -> String where
105     F: FnOnce() -> Path,
106     G: FnMut(&Path) -> Result<Path, IoError>,
107 {
108     // Mac doesn't appear to support $ORIGIN
109     let prefix = if config.is_like_osx {
110         "@loader_path"
111     } else {
112         "$ORIGIN"
113     };
114
115     let cwd = env::current_dir().unwrap();
116     let mut lib = (config.realpath)(&cwd.join(lib)).unwrap();
117     lib.pop();
118     let mut output = (config.realpath)(&cwd.join(&config.out_filename)).unwrap();
119     output.pop();
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
123     format!("{}/{}",
124             prefix,
125             relative.as_str().expect("non-utf8 component in path"))
126 }
127
128 fn get_install_prefix_rpath<F, G>(config: RPathConfig<F, G>) -> String where
129     F: FnOnce() -> Path,
130     G: FnMut(&Path) -> Result<Path, IoError>,
131 {
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()
136 }
137
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());
144         }
145     }
146     minimized
147 }
148
149 #[cfg(all(unix, test))]
150 mod test {
151     use super::{RPathConfig};
152     use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
153
154     #[test]
155     fn test_rpaths_to_flags() {
156         let flags = rpaths_to_flags(&[
157             "path1".to_string(),
158             "path2".to_string()
159         ]);
160         assert_eq!(flags,
161                    ["-Wl,-rpath,path1",
162                     "-Wl,-rpath,path2"]);
163     }
164
165     #[test]
166     fn test_minimize1() {
167         let res = minimize_rpaths(&[
168             "rpath1".to_string(),
169             "rpath2".to_string(),
170             "rpath1".to_string()
171         ]);
172         assert!(res == [
173             "rpath1",
174             "rpath2",
175         ]);
176     }
177
178     #[test]
179     fn test_minimize2() {
180         let res = minimize_rpaths(&[
181             "1a".to_string(),
182             "2".to_string(),
183             "2".to_string(),
184             "1a".to_string(),
185             "4a".to_string(),
186             "1a".to_string(),
187             "2".to_string(),
188             "3".to_string(),
189             "4a".to_string(),
190             "3".to_string()
191         ]);
192         assert!(res == [
193             "1a",
194             "2",
195             "4a",
196             "3",
197         ]);
198     }
199
200     #[test]
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!(),
207             has_rpath: true,
208             is_like_osx: false,
209             realpath: |p| Ok(p.clone())
210         };
211         let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
212         assert_eq!(res, "$ORIGIN/../lib");
213     }
214
215     #[test]
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(),
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, "$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, "@loader_path/../lib");
245     }
246 }