8 import metadata::cstore;
9 import driver::session;
10 import util::filesearch;
12 export get_rpath_flags;
14 fn get_rpath_flags(sess: session::session, out_filename: str) -> [str] {
15 let os = sess.get_targ_cfg().os;
17 // No rpath on windows
18 if os == session::os_win32 {
22 #debug("preparing the RPATH!");
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)];
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)
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)
46 fn rpaths_to_flags(rpaths: [str]) -> [str] {
47 vec::map(rpaths, { |rpath| #fmt("-Wl,-rpath,%s",rpath)})
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);
58 #debug(" %s", libpath);
60 #debug("target_triple: %s", target_triple);
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);
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);
71 // And a final backup rpath to the global library location.
72 let fallback_rpaths = [get_install_prefix_rpath(cwd, target_triple)];
74 fn log_rpaths(desc: str, rpaths: [str]) {
75 #debug("%s rpaths:", desc);
81 log_rpaths("relative", rel_rpaths);
82 log_rpaths("absolute", abs_rpaths);
83 log_rpaths("fallback", fallback_rpaths);
85 let rpaths = rel_rpaths + abs_rpaths + fallback_rpaths;
88 let rpaths = minimize_rpaths(rpaths);
92 fn get_rpaths_relative_to_output(os: session::os,
95 libs: [fs::path]) -> [str] {
96 vec::map(libs, bind get_rpath_relative_to_output(os, cwd, output, _))
99 fn get_rpath_relative_to_output(os: session::os,
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() }
110 prefix + get_relative_to(
111 get_absolute(cwd, output),
112 get_absolute(cwd, lib))
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",
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);
130 let max_common_path = math::min(len1, len2) - 1u;
132 while start_idx < max_common_path
133 && split1[start_idx] == split2[start_idx] {
139 uint::range(start_idx, len1 - 1u) {|_i| path += [".."]; };
141 path += vec::slice(split2, start_idx, len2 - 1u);
143 if check vec::is_not_empty(path) {
144 ret fs::connect_many(path);
150 fn get_absolute_rpaths(cwd: fs::path, libs: [fs::path]) -> [str] {
151 vec::map(libs, bind get_absolute_rpath(cwd, _))
154 fn get_absolute_rpath(cwd: fs::path, &&lib: fs::path) -> str {
155 fs::dirname(get_absolute(cwd, lib))
158 fn get_absolute(cwd: fs::path, lib: fs::path) -> fs::path {
159 if fs::path_is_absolute(lib) {
162 fs::connect(cwd, lib)
166 fn get_install_prefix_rpath(cwd: fs::path, target_triple: str) -> str {
167 let install_prefix = #env("CFG_PREFIX");
169 if install_prefix == "" {
170 fail "rustc compiled without CFG_PREFIX environment variable";
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))
179 fn minimize_rpaths(rpaths: [str]) -> [str] {
180 let set = map::new_str_hash::<()>();
182 for rpath in rpaths {
183 if !set.contains_key(rpath) {
184 minimized += [rpath];
185 set.insert(rpath, ());
191 #[cfg(target_os = "linux")]
192 #[cfg(target_os = "macos")]
193 #[cfg(target_os = "freebsd")]
197 fn test_rpaths_to_flags() {
198 let flags = rpaths_to_flags(["path1", "path2"]);
199 assert flags == ["-Wl,-rpath,path1", "-Wl,-rpath,path2"];
203 fn test_get_absolute1() {
205 let lib = "some/path/lib";
206 let res = get_absolute(cwd, lib);
207 assert res == "/dir/some/path/lib";
211 fn test_get_absolute2() {
213 let lib = "/some/path/lib";
214 let res = get_absolute(cwd, lib);
215 assert res == "/some/path/lib";
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");
226 fn test_prefix_rpath_abs() {
227 let res = get_install_prefix_rpath("/usr/lib", "triple");
228 assert fs::path_is_absolute(res);
232 fn test_minimize1() {
233 let res = minimize_rpaths(["rpath1", "rpath2", "rpath1"]);
234 assert res == ["rpath1", "rpath2"];
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"];
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";
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";
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";
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";
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";
285 fn test_relative_to6() {
288 let res = get_relative_to(p1, p2);
293 fn test_relative_to7() {
296 let res = get_relative_to(p1, p2);
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);
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";
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";
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";
335 fn test_get_absolute_rpath() {
336 let res = get_absolute_rpath("/usr", "lib/libstd.so");
337 assert res == "/usr/lib";