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