]> git.lizzy.rs Git - rust.git/blob - src/comp/back/rpath.rs
06c866fc926c9f3c3b8fe0b6aa09858fb5ddc4ff
[rust.git] / src / comp / back / rpath.rs
1 import std::os;
2 import std::fs;
3 import std::os_fs;
4 import vec;
5 import std::map;
6 import str;
7 import uint;
8 import metadata::cstore;
9 import driver::session;
10 import util::filesearch;
11
12 export get_rpath_flags;
13
14 fn get_rpath_flags(sess: session::session, out_filename: str) -> [str] {
15     let os = sess.get_targ_cfg().os;
16
17     // No rpath on windows
18     if os == session::os_win32 {
19         ret [];
20     }
21
22     #debug("preparing the RPATH!");
23
24     let cwd = os::getcwd();
25     let sysroot = sess.filesearch().sysroot();
26     let output = out_filename;
27     let libs = cstore::get_used_crate_files(sess.get_cstore());
28     // We don't currently rpath native libraries, but we know
29     // where rustrt is and we know every rust program needs it
30     let libs = libs + [get_sysroot_absolute_rt_lib(sess)];
31
32     let target_triple = sess.get_opts().target_triple;
33     let rpaths = get_rpaths(os, cwd, sysroot, output, libs, target_triple);
34     rpaths_to_flags(rpaths)
35 }
36
37 fn get_sysroot_absolute_rt_lib(sess: session::session) -> fs::path {
38     let path = [sess.filesearch().sysroot()]
39         + filesearch::relative_target_lib_path(
40             sess.get_opts().target_triple)
41         + [os::dylib_filename("rustrt")];
42     check vec::is_not_empty(path);
43     fs::connect_many(path)
44 }
45
46 fn rpaths_to_flags(rpaths: [str]) -> [str] {
47     vec::map(rpaths, { |rpath| #fmt("-Wl,-rpath,%s",rpath)})
48 }
49
50 fn get_rpaths(os: session::os, cwd: fs::path, sysroot: fs::path,
51               output: fs::path, libs: [fs::path],
52               target_triple: str) -> [str] {
53     #debug("cwd: %s", cwd);
54     #debug("sysroot: %s", sysroot);
55     #debug("output: %s", output);
56     #debug("libs:");
57     for libpath in libs {
58         #debug("    %s", libpath);
59     }
60     #debug("target_triple: %s", target_triple);
61
62     // Use relative paths to the libraries. Binaries can be moved
63     // as long as they maintain the relative relationship to the
64     // crates they depend on.
65     let rel_rpaths = get_rpaths_relative_to_output(os, cwd, output, libs);
66
67     // Make backup absolute paths to the libraries. Binaries can
68     // be moved as long as the crates they link against don't move.
69     let abs_rpaths = get_absolute_rpaths(cwd, libs);
70
71     // And a final backup rpath to the global library location.
72     let fallback_rpaths = [get_install_prefix_rpath(cwd, target_triple)];
73
74     fn log_rpaths(desc: str, rpaths: [str]) {
75         #debug("%s rpaths:", desc);
76         for rpath in rpaths {
77             #debug("    %s", rpath);
78         }
79     }
80
81     log_rpaths("relative", rel_rpaths);
82     log_rpaths("absolute", abs_rpaths);
83     log_rpaths("fallback", fallback_rpaths);
84
85     let rpaths = rel_rpaths + abs_rpaths + fallback_rpaths;
86
87     // Remove duplicates
88     let rpaths = minimize_rpaths(rpaths);
89     ret rpaths;
90 }
91
92 fn get_rpaths_relative_to_output(os: session::os,
93                                  cwd: fs::path,
94                                  output: fs::path,
95                                  libs: [fs::path]) -> [str] {
96     vec::map(libs, bind get_rpath_relative_to_output(os, cwd, output, _))
97 }
98
99 fn get_rpath_relative_to_output(os: session::os,
100                                 cwd: fs::path,
101                                 output: fs::path,
102                                 &&lib: fs::path) -> str {
103     // Mac doesn't appear to support $ORIGIN
104     let prefix = alt os {
105         session::os_linux. { "$ORIGIN" + fs::path_sep() }
106         session::os_freebsd. { "$ORIGIN" + fs::path_sep() }
107         session::os_macos. { "@executable_path" + fs::path_sep() }
108     };
109
110     prefix + get_relative_to(
111         get_absolute(cwd, output),
112         get_absolute(cwd, lib))
113 }
114
115 // Find the relative path from one file to another
116 fn get_relative_to(abs1: fs::path, abs2: fs::path) -> fs::path {
117     assert fs::path_is_absolute(abs1);
118     assert fs::path_is_absolute(abs2);
119     #debug("finding relative path from %s to %s",
120            abs1, abs2);
121     let normal1 = fs::normalize(abs1);
122     let normal2 = fs::normalize(abs2);
123     let split1 = str::split(normal1, os_fs::path_sep as u8);
124     let split2 = str::split(normal2, os_fs::path_sep as u8);
125     let len1 = vec::len(split1);
126     let len2 = vec::len(split2);
127     assert len1 > 0u;
128     assert len2 > 0u;
129
130     let max_common_path = math::min(len1, len2) - 1u;
131     let start_idx = 0u;
132     while start_idx < max_common_path
133         && split1[start_idx] == split2[start_idx] {
134         start_idx += 1u;
135     }
136
137     let path = [];
138
139     uint::range(start_idx, len1 - 1u) {|_i| path += [".."]; };
140
141     path += vec::slice(split2, start_idx, len2 - 1u);
142
143     if check vec::is_not_empty(path) {
144         ret fs::connect_many(path);
145     } else {
146         ret ".";
147     }
148 }
149
150 fn get_absolute_rpaths(cwd: fs::path, libs: [fs::path]) -> [str] {
151     vec::map(libs, bind get_absolute_rpath(cwd, _))
152 }
153
154 fn get_absolute_rpath(cwd: fs::path, &&lib: fs::path) -> str {
155     fs::dirname(get_absolute(cwd, lib))
156 }
157
158 fn get_absolute(cwd: fs::path, lib: fs::path) -> fs::path {
159     if fs::path_is_absolute(lib) {
160         lib
161     } else {
162         fs::connect(cwd, lib)
163     }
164 }
165
166 fn get_install_prefix_rpath(cwd: fs::path, target_triple: str) -> str {
167     let install_prefix = #env("CFG_PREFIX");
168
169     if install_prefix == "" {
170         fail "rustc compiled without CFG_PREFIX environment variable";
171     }
172
173     let path = [install_prefix]
174         + filesearch::relative_target_lib_path(target_triple);
175     check vec::is_not_empty(path);
176     get_absolute(cwd, fs::connect_many(path))
177 }
178
179 fn minimize_rpaths(rpaths: [str]) -> [str] {
180     let set = map::new_str_hash::<()>();
181     let minimized = [];
182     for rpath in rpaths {
183         if !set.contains_key(rpath) {
184             minimized += [rpath];
185             set.insert(rpath, ());
186         }
187     }
188     ret minimized;
189 }
190
191 #[cfg(target_os = "linux")]
192 #[cfg(target_os = "macos")]
193 #[cfg(target_os = "freebsd")]
194 #[cfg(test)]
195 mod test {
196     #[test]
197     fn test_rpaths_to_flags() {
198         let flags = rpaths_to_flags(["path1", "path2"]);
199         assert flags == ["-Wl,-rpath,path1", "-Wl,-rpath,path2"];
200     }
201
202     #[test]
203     fn test_get_absolute1() {
204         let cwd = "/dir";
205         let lib = "some/path/lib";
206         let res = get_absolute(cwd, lib);
207         assert res == "/dir/some/path/lib";
208     }
209
210     #[test]
211     fn test_get_absolute2() {
212         let cwd = "/dir";
213         let lib = "/some/path/lib";
214         let res = get_absolute(cwd, lib);
215         assert res == "/some/path/lib";
216     }
217
218     #[test]
219     fn test_prefix_rpath() {
220         let res = get_install_prefix_rpath("/usr/lib", "triple");
221         assert str::ends_with(res, #env("CFG_PREFIX")
222                               + "/lib/rustc/triple/lib");
223     }
224
225     #[test]
226     fn test_prefix_rpath_abs() {
227         let res = get_install_prefix_rpath("/usr/lib", "triple");
228         assert fs::path_is_absolute(res);
229     }
230
231     #[test]
232     fn test_minimize1() {
233         let res = minimize_rpaths(["rpath1", "rpath2", "rpath1"]);
234         assert res == ["rpath1", "rpath2"];
235     }
236
237     #[test]
238     fn test_minimize2() {
239         let res = minimize_rpaths(["1a", "2", "2", "1a", "4a",
240                                    "1a", "2", "3", "4a", "3"]);
241         assert res == ["1a", "2", "4a", "3"];
242     }
243
244     #[test]
245     fn test_relative_to1() {
246         let p1 = "/usr/bin/rustc";
247         let p2 = "/usr/lib/mylib";
248         let res = get_relative_to(p1, p2);
249         assert res == "../lib";
250     }
251
252     #[test]
253     fn test_relative_to2() {
254         let p1 = "/usr/bin/rustc";
255         let p2 = "/usr/bin/../lib/mylib";
256         let res = get_relative_to(p1, p2);
257         assert res == "../lib";
258     }
259
260     #[test]
261     fn test_relative_to3() {
262         let p1 = "/usr/bin/whatever/rustc";
263         let p2 = "/usr/lib/whatever/mylib";
264         let res = get_relative_to(p1, p2);
265         assert res == "../../lib/whatever";
266     }
267
268     #[test]
269     fn test_relative_to4() {
270         let p1 = "/usr/bin/whatever/../rustc";
271         let p2 = "/usr/lib/whatever/mylib";
272         let res = get_relative_to(p1, p2);
273         assert res == "../lib/whatever";
274     }
275
276     #[test]
277     fn test_relative_to5() {
278         let p1 = "/usr/bin/whatever/../rustc";
279         let p2 = "/usr/lib/whatever/../mylib";
280         let res = get_relative_to(p1, p2);
281         assert res == "../lib";
282     }
283
284     #[test]
285     fn test_relative_to6() {
286         let p1 = "/1";
287         let p2 = "/2/3";
288         let res = get_relative_to(p1, p2);
289         assert res == "2";
290     }
291
292     #[test]
293     fn test_relative_to7() {
294         let p1 = "/1/2";
295         let p2 = "/3";
296         let res = get_relative_to(p1, p2);
297         assert res == "..";
298     }
299
300     #[test]
301     fn test_relative_to8() {
302         let p1 = "/home/brian/Dev/rust/build/"
303             + "stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so";
304         let p2 = "/home/brian/Dev/rust/build/stage2/bin/.."
305             + "/lib/rustc/i686-unknown-linux-gnu/lib/libstd.so";
306         let res = get_relative_to(p1, p2);
307         assert res == ".";
308     }
309
310     #[test]
311     #[cfg(target_os = "linux")]
312     fn test_rpath_relative() {
313         let res = get_rpath_relative_to_output(session::os_linux,
314             "/usr", "bin/rustc", "lib/libstd.so");
315         assert res == "$ORIGIN/../lib";
316     }
317
318     #[test]
319     #[cfg(target_os = "freebsd")]
320     fn test_rpath_relative() {
321         let res = get_rpath_relative_to_output(session::os_freebsd,
322             "/usr", "bin/rustc", "lib/libstd.so");
323         assert res == "$ORIGIN/../lib";
324     }
325
326     #[test]
327     #[cfg(target_os = "macos")]
328     fn test_rpath_relative() {
329         let res = get_rpath_relative_to_output(session::os_macos,
330             "/usr", "bin/rustc", "lib/libstd.so");
331         assert res == "@executable_path/../lib";
332     }
333
334     #[test]
335     fn test_get_absolute_rpath() {
336         let res = get_absolute_rpath("/usr", "lib/libstd.so");
337         assert res == "/usr/lib";
338     }
339 }