rust/src/rustc/back/rpath.rs
2012-06-07 22:28:00 -07:00

344 lines
9.7 KiB
Rust

import std::map;
import std::map::hashmap;
import metadata::cstore;
import driver::session;
import metadata::filesearch;
export get_rpath_flags;
pure fn not_win32(os: session::os) -> bool {
alt os {
session::os_win32 { false }
_ { true }
}
}
fn get_rpath_flags(sess: session::session, out_filename: str) -> [str] {
let os = sess.targ_cfg.os;
// No rpath on windows
if os == session::os_win32 {
ret [];
}
#debug("preparing the RPATH!");
let cwd = os::getcwd();
let sysroot = sess.filesearch.sysroot();
let output = out_filename;
let libs = cstore::get_used_crate_files(sess.cstore);
// We don't currently rpath native libraries, but we know
// where rustrt is and we know every rust program needs it
let libs = libs + [get_sysroot_absolute_rt_lib(sess)];
let target_triple = sess.opts.target_triple;
let rpaths = get_rpaths(os, cwd, sysroot, output, libs, target_triple);
rpaths_to_flags(rpaths)
}
fn get_sysroot_absolute_rt_lib(sess: session::session) -> path::path {
let path = [sess.filesearch.sysroot()]
+ filesearch::relative_target_lib_path(
sess.opts.target_triple)
+ [os::dll_filename("rustrt")];
path::connect_many(path)
}
fn rpaths_to_flags(rpaths: [str]) -> [str] {
vec::map(rpaths, { |rpath| #fmt("-Wl,-rpath,%s",rpath)})
}
fn get_rpaths(os: session::os, cwd: path::path, sysroot: path::path,
output: path::path, libs: [path::path],
target_triple: str) -> [str] {
#debug("cwd: %s", cwd);
#debug("sysroot: %s", sysroot);
#debug("output: %s", output);
#debug("libs:");
for libs.each {|libpath|
#debug(" %s", libpath);
}
#debug("target_triple: %s", target_triple);
// Use relative paths to the libraries. Binaries can be moved
// as long as they maintain the relative relationship to the
// crates they depend on.
let rel_rpaths = get_rpaths_relative_to_output(os, cwd, output, libs);
// Make backup absolute paths to the libraries. Binaries can
// be moved as long as the crates they link against don't move.
let abs_rpaths = get_absolute_rpaths(cwd, libs);
// And a final backup rpath to the global library location.
let fallback_rpaths = [get_install_prefix_rpath(cwd, target_triple)];
fn log_rpaths(desc: str, rpaths: [str]) {
#debug("%s rpaths:", desc);
for rpaths.each {|rpath|
#debug(" %s", rpath);
}
}
log_rpaths("relative", rel_rpaths);
log_rpaths("absolute", abs_rpaths);
log_rpaths("fallback", fallback_rpaths);
let rpaths = rel_rpaths + abs_rpaths + fallback_rpaths;
// Remove duplicates
let rpaths = minimize_rpaths(rpaths);
ret rpaths;
}
fn get_rpaths_relative_to_output(os: session::os,
cwd: path::path,
output: path::path,
libs: [path::path]) -> [str] {
vec::map(libs, bind get_rpath_relative_to_output(os, cwd, output, _))
}
fn get_rpath_relative_to_output(os: session::os,
cwd: path::path,
output: path::path,
&&lib: path::path) : not_win32(os) -> str {
// Mac doesn't appear to support $ORIGIN
let prefix = alt os {
session::os_linux { "$ORIGIN" + path::path_sep() }
session::os_freebsd { "$ORIGIN" + path::path_sep() }
session::os_macos { "@executable_path" + path::path_sep() }
session::os_win32 { core::unreachable(); }
};
prefix + get_relative_to(
get_absolute(cwd, output),
get_absolute(cwd, lib))
}
// Find the relative path from one file to another
fn get_relative_to(abs1: path::path, abs2: path::path) -> path::path {
assert path::path_is_absolute(abs1);
assert path::path_is_absolute(abs2);
#debug("finding relative path from %s to %s",
abs1, abs2);
let normal1 = path::normalize(abs1);
let normal2 = path::normalize(abs2);
let split1 = path::split(normal1);
let split2 = path::split(normal2);
let len1 = vec::len(split1);
let len2 = vec::len(split2);
assert len1 > 0u;
assert len2 > 0u;
let max_common_path = uint::min(len1, len2) - 1u;
let mut start_idx = 0u;
while start_idx < max_common_path
&& split1[start_idx] == split2[start_idx] {
start_idx += 1u;
}
let mut path = [];
for uint::range(start_idx, len1 - 1u) {|_i| path += [".."]; };
path += vec::slice(split2, start_idx, len2 - 1u);
if check vec::is_not_empty(path) {
ret path::connect_many(path);
} else {
ret ".";
}
}
fn get_absolute_rpaths(cwd: path::path, libs: [path::path]) -> [str] {
vec::map(libs, bind get_absolute_rpath(cwd, _))
}
fn get_absolute_rpath(cwd: path::path, &&lib: path::path) -> str {
path::dirname(get_absolute(cwd, lib))
}
fn get_absolute(cwd: path::path, lib: path::path) -> path::path {
if path::path_is_absolute(lib) {
lib
} else {
path::connect(cwd, lib)
}
}
fn get_install_prefix_rpath(cwd: path::path, target_triple: str) -> str {
let install_prefix = #env("CFG_PREFIX");
if install_prefix == "" {
fail "rustc compiled without CFG_PREFIX environment variable";
}
let path = [install_prefix]
+ filesearch::relative_target_lib_path(target_triple);
get_absolute(cwd, path::connect_many(path))
}
fn minimize_rpaths(rpaths: [str]) -> [str] {
let set = map::str_hash::<()>();
let mut minimized = [];
for rpaths.each {|rpath|
if !set.contains_key(rpath) {
minimized += [rpath];
set.insert(rpath, ());
}
}
ret minimized;
}
#[cfg(unix)]
mod test {
#[test]
fn test_rpaths_to_flags() {
let flags = rpaths_to_flags(["path1", "path2"]);
assert flags == ["-Wl,-rpath,path1", "-Wl,-rpath,path2"];
}
#[test]
fn test_get_absolute1() {
let cwd = "/dir";
let lib = "some/path/lib";
let res = get_absolute(cwd, lib);
assert res == "/dir/some/path/lib";
}
#[test]
fn test_get_absolute2() {
let cwd = "/dir";
let lib = "/some/path/lib";
let res = get_absolute(cwd, lib);
assert res == "/some/path/lib";
}
#[test]
fn test_prefix_rpath() {
let res = get_install_prefix_rpath("/usr/lib", "triple");
let d = path::connect(#env("CFG_PREFIX"), "/lib/rustc/triple/lib");
assert str::ends_with(res, d);
}
#[test]
fn test_prefix_rpath_abs() {
let res = get_install_prefix_rpath("/usr/lib", "triple");
assert path::path_is_absolute(res);
}
#[test]
fn test_minimize1() {
let res = minimize_rpaths(["rpath1", "rpath2", "rpath1"]);
assert res == ["rpath1", "rpath2"];
}
#[test]
fn test_minimize2() {
let res = minimize_rpaths(["1a", "2", "2", "1a", "4a",
"1a", "2", "3", "4a", "3"]);
assert res == ["1a", "2", "4a", "3"];
}
#[test]
fn test_relative_to1() {
let p1 = "/usr/bin/rustc";
let p2 = "/usr/lib/mylib";
let res = get_relative_to(p1, p2);
assert res == "../lib";
}
#[test]
fn test_relative_to2() {
let p1 = "/usr/bin/rustc";
let p2 = "/usr/bin/../lib/mylib";
let res = get_relative_to(p1, p2);
assert res == "../lib";
}
#[test]
fn test_relative_to3() {
let p1 = "/usr/bin/whatever/rustc";
let p2 = "/usr/lib/whatever/mylib";
let res = get_relative_to(p1, p2);
assert res == "../../lib/whatever";
}
#[test]
fn test_relative_to4() {
let p1 = "/usr/bin/whatever/../rustc";
let p2 = "/usr/lib/whatever/mylib";
let res = get_relative_to(p1, p2);
assert res == "../lib/whatever";
}
#[test]
fn test_relative_to5() {
let p1 = "/usr/bin/whatever/../rustc";
let p2 = "/usr/lib/whatever/../mylib";
let res = get_relative_to(p1, p2);
assert res == "../lib";
}
#[test]
fn test_relative_to6() {
let p1 = "/1";
let p2 = "/2/3";
let res = get_relative_to(p1, p2);
assert res == "2";
}
#[test]
fn test_relative_to7() {
let p1 = "/1/2";
let p2 = "/3";
let res = get_relative_to(p1, p2);
assert res == "..";
}
#[test]
fn test_relative_to8() {
let p1 = "/home/brian/Dev/rust/build/"
+ "stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so";
let p2 = "/home/brian/Dev/rust/build/stage2/bin/.."
+ "/lib/rustc/i686-unknown-linux-gnu/lib/libstd.so";
let res = get_relative_to(p1, p2);
assert res == ".";
}
#[test]
#[cfg(target_os = "linux")]
fn test_rpath_relative() {
let o = session::os_linux;
check not_win32(o);
let res = get_rpath_relative_to_output(o,
"/usr", "bin/rustc", "lib/libstd.so");
assert res == "$ORIGIN/../lib";
}
#[test]
#[cfg(target_os = "freebsd")]
fn test_rpath_relative() {
let o = session::os_freebsd;
check not_win32(o);
let res = get_rpath_relative_to_output(o,
"/usr", "bin/rustc", "lib/libstd.so");
assert res == "$ORIGIN/../lib";
}
#[test]
#[cfg(target_os = "macos")]
fn test_rpath_relative() {
// this is why refinements would be nice
let o = session::os_macos;
check not_win32(o);
let res = get_rpath_relative_to_output(o, "/usr", "bin/rustc",
"lib/libstd.so");
assert res == "@executable_path/../lib";
}
#[test]
fn test_get_absolute_rpath() {
let res = get_absolute_rpath("/usr", "lib/libstd.so");
assert res == "/usr/lib";
}
}