]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/back/rpath.rs
Fix checking for missing stability annotations
[rust.git] / src / librustc_trans / 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 use std::collections::HashSet;
12 use std::env;
13 use std::path::{Path, PathBuf};
14 use std::fs;
15
16 use rustc::hir::def_id::CrateNum;
17 use rustc::middle::cstore::LibSource;
18
19 pub struct RPathConfig<'a> {
20     pub used_crates: Vec<(CrateNum, LibSource)>,
21     pub out_filename: PathBuf,
22     pub is_like_osx: bool,
23     pub has_rpath: bool,
24     pub linker_is_gnu: bool,
25     pub get_install_prefix_lib_path: &'a mut FnMut() -> PathBuf,
26 }
27
28 pub fn get_rpath_flags(config: &mut RPathConfig) -> Vec<String> {
29     // No rpath on windows
30     if !config.has_rpath {
31         return Vec::new();
32     }
33
34     let mut flags = Vec::new();
35
36     debug!("preparing the RPATH!");
37
38     let libs = config.used_crates.clone();
39     let libs = libs.into_iter().filter_map(|(_, l)| l.option()).collect::<Vec<_>>();
40     let rpaths = get_rpaths(config, &libs);
41     flags.extend_from_slice(&rpaths_to_flags(&rpaths));
42
43     // Use DT_RUNPATH instead of DT_RPATH if available
44     if config.linker_is_gnu {
45         flags.push("-Wl,--enable-new-dtags".to_string());
46     }
47
48     flags
49 }
50
51 fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> {
52     let mut ret = Vec::new();
53     for rpath in rpaths {
54         if rpath.contains(',') {
55             ret.push("-Wl,-rpath".into());
56             ret.push("-Xlinker".into());
57             ret.push(rpath.clone());
58         } else {
59             ret.push(format!("-Wl,-rpath,{}", &(*rpath)));
60         }
61     }
62     return ret;
63 }
64
65 fn get_rpaths(config: &mut RPathConfig, libs: &[PathBuf]) -> Vec<String> {
66     debug!("output: {:?}", config.out_filename.display());
67     debug!("libs:");
68     for libpath in libs {
69         debug!("    {:?}", libpath.display());
70     }
71
72     // Use relative paths to the libraries. Binaries can be moved
73     // as long as they maintain the relative relationship to the
74     // crates they depend on.
75     let rel_rpaths = get_rpaths_relative_to_output(config, libs);
76
77     // And a final backup rpath to the global library location.
78     let fallback_rpaths = vec![get_install_prefix_rpath(config)];
79
80     fn log_rpaths(desc: &str, rpaths: &[String]) {
81         debug!("{} rpaths:", desc);
82         for rpath in rpaths {
83             debug!("    {}", *rpath);
84         }
85     }
86
87     log_rpaths("relative", &rel_rpaths);
88     log_rpaths("fallback", &fallback_rpaths);
89
90     let mut rpaths = rel_rpaths;
91     rpaths.extend_from_slice(&fallback_rpaths);
92
93     // Remove duplicates
94     let rpaths = minimize_rpaths(&rpaths);
95     return rpaths;
96 }
97
98 fn get_rpaths_relative_to_output(config: &mut RPathConfig,
99                                  libs: &[PathBuf]) -> Vec<String> {
100     libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect()
101 }
102
103 fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path) -> String {
104     // Mac doesn't appear to support $ORIGIN
105     let prefix = if config.is_like_osx {
106         "@loader_path"
107     } else {
108         "$ORIGIN"
109     };
110
111     let cwd = env::current_dir().unwrap();
112     let mut lib = fs::canonicalize(&cwd.join(lib)).unwrap_or(cwd.join(lib));
113     lib.pop();
114     let mut output = cwd.join(&config.out_filename);
115     output.pop();
116     let output = fs::canonicalize(&output).unwrap_or(output);
117     let relative = path_relative_from(&lib, &output)
118         .expect(&format!("couldn't create relative path from {:?} to {:?}", output, lib));
119     // FIXME (#9639): This needs to handle non-utf8 paths
120     format!("{}/{}", prefix,
121             relative.to_str().expect("non-utf8 component in path"))
122 }
123
124 // This routine is adapted from the *old* Path's `path_relative_from`
125 // function, which works differently from the new `relative_from` function.
126 // In particular, this handles the case on unix where both paths are
127 // absolute but with only the root as the common directory.
128 fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> {
129     use std::path::Component;
130
131     if path.is_absolute() != base.is_absolute() {
132         if path.is_absolute() {
133             Some(PathBuf::from(path))
134         } else {
135             None
136         }
137     } else {
138         let mut ita = path.components();
139         let mut itb = base.components();
140         let mut comps: Vec<Component> = vec![];
141         loop {
142             match (ita.next(), itb.next()) {
143                 (None, None) => break,
144                 (Some(a), None) => {
145                     comps.push(a);
146                     comps.extend(ita.by_ref());
147                     break;
148                 }
149                 (None, _) => comps.push(Component::ParentDir),
150                 (Some(a), Some(b)) if comps.is_empty() && a == b => (),
151                 (Some(a), Some(b)) if b == Component::CurDir => comps.push(a),
152                 (Some(_), Some(b)) if b == Component::ParentDir => return None,
153                 (Some(a), Some(_)) => {
154                     comps.push(Component::ParentDir);
155                     for _ in itb {
156                         comps.push(Component::ParentDir);
157                     }
158                     comps.push(a);
159                     comps.extend(ita.by_ref());
160                     break;
161                 }
162             }
163         }
164         Some(comps.iter().map(|c| c.as_os_str()).collect())
165     }
166 }
167
168
169 fn get_install_prefix_rpath(config: &mut RPathConfig) -> String {
170     let path = (config.get_install_prefix_lib_path)();
171     let path = env::current_dir().unwrap().join(&path);
172     // FIXME (#9639): This needs to handle non-utf8 paths
173     path.to_str().expect("non-utf8 component in rpath").to_string()
174 }
175
176 fn minimize_rpaths(rpaths: &[String]) -> Vec<String> {
177     let mut set = HashSet::new();
178     let mut minimized = Vec::new();
179     for rpath in rpaths {
180         if set.insert(rpath) {
181             minimized.push(rpath.clone());
182         }
183     }
184     minimized
185 }
186
187 #[cfg(all(unix, test))]
188 mod tests {
189     use super::{RPathConfig};
190     use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
191     use std::path::{Path, PathBuf};
192
193     #[test]
194     fn test_rpaths_to_flags() {
195         let flags = rpaths_to_flags(&[
196             "path1".to_string(),
197             "path2".to_string()
198         ]);
199         assert_eq!(flags,
200                    ["-Wl,-rpath,path1",
201                     "-Wl,-rpath,path2"]);
202     }
203
204     #[test]
205     fn test_minimize1() {
206         let res = minimize_rpaths(&[
207             "rpath1".to_string(),
208             "rpath2".to_string(),
209             "rpath1".to_string()
210         ]);
211         assert!(res == [
212             "rpath1",
213             "rpath2",
214         ]);
215     }
216
217     #[test]
218     fn test_minimize2() {
219         let res = minimize_rpaths(&[
220             "1a".to_string(),
221             "2".to_string(),
222             "2".to_string(),
223             "1a".to_string(),
224             "4a".to_string(),
225             "1a".to_string(),
226             "2".to_string(),
227             "3".to_string(),
228             "4a".to_string(),
229             "3".to_string()
230         ]);
231         assert!(res == [
232             "1a",
233             "2",
234             "4a",
235             "3",
236         ]);
237     }
238
239     #[test]
240     fn test_rpath_relative() {
241         if cfg!(target_os = "macos") {
242             let config = &mut RPathConfig {
243                 used_crates: Vec::new(),
244                 has_rpath: true,
245                 is_like_osx: true,
246                 linker_is_gnu: false,
247                 out_filename: PathBuf::from("bin/rustc"),
248                 get_install_prefix_lib_path: &mut || panic!(),
249             };
250             let res = get_rpath_relative_to_output(config,
251                                                    Path::new("lib/libstd.so"));
252             assert_eq!(res, "@loader_path/../lib");
253         } else {
254             let config = &mut RPathConfig {
255                 used_crates: Vec::new(),
256                 out_filename: PathBuf::from("bin/rustc"),
257                 get_install_prefix_lib_path: &mut || panic!(),
258                 has_rpath: true,
259                 is_like_osx: false,
260                 linker_is_gnu: true,
261             };
262             let res = get_rpath_relative_to_output(config,
263                                                    Path::new("lib/libstd.so"));
264             assert_eq!(res, "$ORIGIN/../lib");
265         }
266     }
267
268     #[test]
269     fn test_xlinker() {
270         let args = rpaths_to_flags(&[
271             "a/normal/path".to_string(),
272             "a,comma,path".to_string()
273         ]);
274
275         assert_eq!(args, vec![
276             "-Wl,-rpath,a/normal/path".to_string(),
277             "-Wl,-rpath".to_string(),
278             "-Xlinker".to_string(),
279             "a,comma,path".to_string()
280         ]);
281     }
282 }