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::OsWindows {
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()]);
40 else if config.os == abi::OsDragonfly {
41 flags.push_all(["-Wl,-rpath,/usr/lib/gcc47".to_string(),
42 "-Wl,-rpath,/usr/lib/gcc44".to_string(),
43 "-Wl,-z,origin".to_string()]);
47 debug!("preparing the RPATH!");
49 let libs = config.used_crates.clone();
50 let libs = libs.into_iter().filter_map(|(_, l)| {
52 }).collect::<Vec<_>>();
54 let rpaths = get_rpaths(config, libs.as_slice());
55 flags.push_all(rpaths_to_flags(rpaths.as_slice()).as_slice());
59 fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> {
60 let mut ret = Vec::new();
61 for rpath in rpaths.iter() {
62 ret.push(format!("-Wl,-rpath,{}", (*rpath).as_slice()));
67 fn get_rpaths(mut config: RPathConfig,
68 libs: &[Path]) -> Vec<String> {
69 debug!("output: {}", config.out_filename.display());
71 for libpath in libs.iter() {
72 debug!(" {}", libpath.display());
75 // Use relative paths to the libraries. Binaries can be moved
76 // as long as they maintain the relative relationship to the
77 // crates they depend on.
78 let rel_rpaths = get_rpaths_relative_to_output(&mut config, libs);
80 // And a final backup rpath to the global library location.
81 let fallback_rpaths = vec!(get_install_prefix_rpath(config));
83 fn log_rpaths(desc: &str, rpaths: &[String]) {
84 debug!("{} rpaths:", desc);
85 for rpath in rpaths.iter() {
86 debug!(" {}", *rpath);
90 log_rpaths("relative", rel_rpaths.as_slice());
91 log_rpaths("fallback", fallback_rpaths.as_slice());
93 let mut rpaths = rel_rpaths;
94 rpaths.push_all(fallback_rpaths.as_slice());
97 let rpaths = minimize_rpaths(rpaths.as_slice());
101 fn get_rpaths_relative_to_output(config: &mut RPathConfig,
102 libs: &[Path]) -> Vec<String> {
103 libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect()
106 fn get_rpath_relative_to_output(config: &mut RPathConfig,
107 lib: &Path) -> String {
110 assert!(config.os != abi::OsWindows);
112 // Mac doesn't appear to support $ORIGIN
113 let prefix = match config.os {
114 abi::OsAndroid | abi::OsLinux | abi::OsFreebsd | abi::OsDragonfly
116 abi::OsMacos => "@loader_path",
117 abi::OsWindows | abi::OsiOS => unreachable!()
120 let mut lib = (config.realpath)(&os::make_absolute(lib)).unwrap();
122 let mut output = (config.realpath)(&os::make_absolute(&config.out_filename)).unwrap();
124 let relative = lib.path_relative_from(&output);
125 let relative = relative.expect("could not create rpath relative to output");
126 // FIXME (#9639): This needs to handle non-utf8 paths
129 relative.as_str().expect("non-utf8 component in path"))
132 fn get_install_prefix_rpath(config: RPathConfig) -> String {
133 let path = (config.get_install_prefix_lib_path)();
134 let path = os::make_absolute(&path);
135 // FIXME (#9639): This needs to handle non-utf8 paths
136 path.as_str().expect("non-utf8 component in rpath").to_string()
139 fn minimize_rpaths(rpaths: &[String]) -> Vec<String> {
140 let mut set = HashSet::new();
141 let mut minimized = Vec::new();
142 for rpath in rpaths.iter() {
143 if set.insert(rpath.as_slice()) {
144 minimized.push(rpath.clone());
152 use super::{RPathConfig};
153 use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
157 fn test_rpaths_to_flags() {
158 let flags = rpaths_to_flags([
163 vec!("-Wl,-rpath,path1".to_string(),
164 "-Wl,-rpath,path2".to_string()));
168 fn test_minimize1() {
169 let res = minimize_rpaths([
170 "rpath1".to_string(),
171 "rpath2".to_string(),
174 assert!(res.as_slice() == [
175 "rpath1".to_string(),
181 fn test_minimize2() {
182 let res = minimize_rpaths([
194 assert!(res.as_slice() == [
203 #[cfg(target_os = "linux")]
204 #[cfg(target_os = "android")]
205 fn test_rpath_relative() {
206 let config = &mut RPathConfig {
208 used_crates: Vec::new(),
209 out_filename: Path::new("bin/rustc"),
210 get_install_prefix_lib_path: || fail!(),
211 realpath: |p| Ok(p.clone())
213 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
214 assert_eq!(res.as_slice(), "$ORIGIN/../lib");
218 #[cfg(target_os = "freebsd")]
219 fn test_rpath_relative() {
220 let config = &mut RPathConfig {
222 used_crates: Vec::new(),
223 out_filename: Path::new("bin/rustc"),
224 get_install_prefix_lib_path: || fail!(),
225 realpath: |p| Ok(p.clone())
227 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
228 assert_eq!(res.as_slice(), "$ORIGIN/../lib");
232 #[cfg(target_os = "dragonfly")]
233 fn test_rpath_relative() {
234 let config = &mut RPathConfig {
235 os: abi::OsDragonfly,
236 used_crates: Vec::new(),
237 out_filename: Path::new("bin/rustc"),
238 get_install_prefix_lib_path: || fail!(),
239 realpath: |p| Ok(p.clone())
241 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
242 assert_eq!(res.as_slice(), "$ORIGIN/../lib");
246 #[cfg(target_os = "macos")]
247 fn test_rpath_relative() {
248 let config = &mut RPathConfig {
250 used_crates: Vec::new(),
251 out_filename: Path::new("bin/rustc"),
252 get_install_prefix_lib_path: || fail!(),
253 realpath: |p| Ok(p.clone())
255 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
256 assert_eq!(res.as_slice(), "@loader_path/../lib");