1 // Copyright 2012-2013 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.
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.
12 use std::collections::HashSet;
18 pub struct RPathConfig<'a> {
20 pub used_crates: Vec<(ast::CrateNum, Option<Path>)>,
21 pub out_filename: Path,
22 pub get_install_prefix_lib_path: ||:'a -> Path,
23 pub realpath: |&Path|:'a -> Result<Path, IoError>
26 pub fn get_rpath_flags(config: RPathConfig) -> Vec<String> {
28 // No rpath on windows
29 if config.os == abi::OsWin32 {
33 let mut flags = Vec::new();
35 if config.os == abi::OsFreebsd {
36 flags.push_all(["-Wl,-rpath,/usr/local/lib/gcc46".to_string(),
37 "-Wl,-rpath,/usr/local/lib/gcc44".to_string(),
38 "-Wl,-z,origin".to_string()]);
41 debug!("preparing the RPATH!");
43 let libs = config.used_crates.clone();
44 let libs = libs.move_iter().filter_map(|(_, l)| {
46 }).collect::<Vec<_>>();
48 let rpaths = get_rpaths(config, libs.as_slice());
49 flags.push_all(rpaths_to_flags(rpaths.as_slice()).as_slice());
53 fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> {
54 let mut ret = Vec::new();
55 for rpath in rpaths.iter() {
56 ret.push(format!("-Wl,-rpath,{}", (*rpath).as_slice()));
61 fn get_rpaths(mut config: RPathConfig,
62 libs: &[Path]) -> Vec<String> {
63 debug!("output: {}", config.out_filename.display());
65 for libpath in libs.iter() {
66 debug!(" {}", libpath.display());
69 // Use relative paths to the libraries. Binaries can be moved
70 // as long as they maintain the relative relationship to the
71 // crates they depend on.
72 let rel_rpaths = get_rpaths_relative_to_output(&mut config, libs);
74 // And a final backup rpath to the global library location.
75 let fallback_rpaths = vec!(get_install_prefix_rpath(config));
77 fn log_rpaths(desc: &str, rpaths: &[String]) {
78 debug!("{} rpaths:", desc);
79 for rpath in rpaths.iter() {
80 debug!(" {}", *rpath);
84 log_rpaths("relative", rel_rpaths.as_slice());
85 log_rpaths("fallback", fallback_rpaths.as_slice());
87 let mut rpaths = rel_rpaths;
88 rpaths.push_all(fallback_rpaths.as_slice());
91 let rpaths = minimize_rpaths(rpaths.as_slice());
95 fn get_rpaths_relative_to_output(config: &mut RPathConfig,
96 libs: &[Path]) -> Vec<String> {
97 libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect()
100 fn get_rpath_relative_to_output(config: &mut RPathConfig,
101 lib: &Path) -> String {
104 assert!(config.os != abi::OsWin32);
106 // Mac doesn't appear to support $ORIGIN
107 let prefix = match config.os {
108 abi::OsAndroid | abi::OsLinux | abi::OsFreebsd
110 abi::OsMacos => "@loader_path",
111 abi::OsWin32 | abi::OsiOS => unreachable!()
114 let mut lib = (config.realpath)(&os::make_absolute(lib)).unwrap();
116 let mut output = (config.realpath)(&os::make_absolute(&config.out_filename)).unwrap();
118 let relative = lib.path_relative_from(&output);
119 let relative = relative.expect("could not create rpath relative to output");
120 // FIXME (#9639): This needs to handle non-utf8 paths
123 relative.as_str().expect("non-utf8 component in path"))
126 fn get_install_prefix_rpath(config: RPathConfig) -> String {
127 let path = (config.get_install_prefix_lib_path)();
128 let path = os::make_absolute(&path);
129 // FIXME (#9639): This needs to handle non-utf8 paths
130 path.as_str().expect("non-utf8 component in rpath").to_string()
133 fn minimize_rpaths(rpaths: &[String]) -> Vec<String> {
134 let mut set = HashSet::new();
135 let mut minimized = Vec::new();
136 for rpath in rpaths.iter() {
137 if set.insert(rpath.as_slice()) {
138 minimized.push(rpath.clone());
146 use super::{RPathConfig};
147 use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
151 fn test_rpaths_to_flags() {
152 let flags = rpaths_to_flags([
157 vec!("-Wl,-rpath,path1".to_string(),
158 "-Wl,-rpath,path2".to_string()));
162 fn test_minimize1() {
163 let res = minimize_rpaths([
164 "rpath1".to_string(),
165 "rpath2".to_string(),
168 assert!(res.as_slice() == [
169 "rpath1".to_string(),
175 fn test_minimize2() {
176 let res = minimize_rpaths([
188 assert!(res.as_slice() == [
197 #[cfg(target_os = "linux")]
198 #[cfg(target_os = "android")]
199 fn test_rpath_relative() {
200 let config = &mut RPathConfig {
202 used_crates: Vec::new(),
203 out_filename: Path::new("bin/rustc"),
204 get_install_prefix_lib_path: || fail!(),
205 realpath: |p| Ok(p.clone())
207 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
208 assert_eq!(res.as_slice(), "$ORIGIN/../lib");
212 #[cfg(target_os = "freebsd")]
213 fn test_rpath_relative() {
214 let config = &mut RPathConfig {
216 used_crates: Vec::new(),
217 out_filename: Path::new("bin/rustc"),
218 get_install_prefix_lib_path: || fail!(),
219 realpath: |p| Ok(p.clone())
221 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
222 assert_eq!(res.as_slice(), "$ORIGIN/../lib");
226 #[cfg(target_os = "macos")]
227 fn test_rpath_relative() {
228 let config = &mut RPathConfig {
230 used_crates: Vec::new(),
231 out_filename: Path::new("bin/rustc"),
232 get_install_prefix_lib_path: || fail!(),
233 realpath: |p| Ok(p.clone())
235 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
236 assert_eq!(res.as_slice(), "@loader_path/../lib");