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;
17 pub struct RPathConfig<'a> {
18 pub used_crates: Vec<(ast::CrateNum, Option<Path>)>,
19 pub out_filename: Path,
20 pub is_like_osx: bool,
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.has_rpath {
33 let mut flags = Vec::new();
35 debug!("preparing the RPATH!");
37 let libs = config.used_crates.clone();
38 let libs = libs.into_iter().filter_map(|(_, l)| {
40 }).collect::<Vec<_>>();
42 let rpaths = get_rpaths(config, libs.as_slice());
43 flags.push_all(rpaths_to_flags(rpaths.as_slice()).as_slice());
47 fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> {
48 let mut ret = Vec::new();
49 for rpath in rpaths.iter() {
50 ret.push(format!("-Wl,-rpath,{}", (*rpath).as_slice()));
55 fn get_rpaths(mut config: RPathConfig,
56 libs: &[Path]) -> Vec<String> {
57 debug!("output: {}", config.out_filename.display());
59 for libpath in libs.iter() {
60 debug!(" {}", libpath.display());
63 // Use relative paths to the libraries. Binaries can be moved
64 // as long as they maintain the relative relationship to the
65 // crates they depend on.
66 let rel_rpaths = get_rpaths_relative_to_output(&mut config, libs);
68 // And a final backup rpath to the global library location.
69 let fallback_rpaths = vec!(get_install_prefix_rpath(config));
71 fn log_rpaths(desc: &str, rpaths: &[String]) {
72 debug!("{} rpaths:", desc);
73 for rpath in rpaths.iter() {
74 debug!(" {}", *rpath);
78 log_rpaths("relative", rel_rpaths.as_slice());
79 log_rpaths("fallback", fallback_rpaths.as_slice());
81 let mut rpaths = rel_rpaths;
82 rpaths.push_all(fallback_rpaths.as_slice());
85 let rpaths = minimize_rpaths(rpaths.as_slice());
89 fn get_rpaths_relative_to_output(config: &mut RPathConfig,
90 libs: &[Path]) -> Vec<String> {
91 libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect()
94 fn get_rpath_relative_to_output(config: &mut RPathConfig,
95 lib: &Path) -> String {
98 // Mac doesn't appear to support $ORIGIN
99 let prefix = if config.is_like_osx {
105 let mut lib = (config.realpath)(&os::make_absolute(lib).unwrap()).unwrap();
107 let mut output = (config.realpath)(&os::make_absolute(&config.out_filename).unwrap()).unwrap();
109 let relative = lib.path_relative_from(&output);
110 let relative = relative.expect("could not create rpath relative to output");
111 // FIXME (#9639): This needs to handle non-utf8 paths
114 relative.as_str().expect("non-utf8 component in path"))
117 fn get_install_prefix_rpath(config: RPathConfig) -> String {
118 let path = (config.get_install_prefix_lib_path)();
119 let path = os::make_absolute(&path).unwrap();
120 // FIXME (#9639): This needs to handle non-utf8 paths
121 path.as_str().expect("non-utf8 component in rpath").to_string()
124 fn minimize_rpaths(rpaths: &[String]) -> Vec<String> {
125 let mut set = HashSet::new();
126 let mut minimized = Vec::new();
127 for rpath in rpaths.iter() {
128 if set.insert(rpath.as_slice()) {
129 minimized.push(rpath.clone());
135 #[cfg(all(unix, test))]
137 use super::{RPathConfig};
138 use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
142 fn test_rpaths_to_flags() {
143 let flags = rpaths_to_flags(&[
148 vec!("-Wl,-rpath,path1".to_string(),
149 "-Wl,-rpath,path2".to_string()));
153 fn test_minimize1() {
154 let res = minimize_rpaths(&[
155 "rpath1".to_string(),
156 "rpath2".to_string(),
159 assert!(res.as_slice() == &[
160 "rpath1".to_string(),
166 fn test_minimize2() {
167 let res = minimize_rpaths(&[
179 assert!(res.as_slice() == &[
188 #[cfg(any(target_os = "linux", target_os = "android"))]
189 fn test_rpath_relative() {
190 let config = &mut RPathConfig {
191 used_crates: Vec::new(),
192 out_filename: Path::new("bin/rustc"),
193 get_install_prefix_lib_path: || panic!(),
196 realpath: |p| Ok(p.clone())
198 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
199 assert_eq!(res.as_slice(), "$ORIGIN/../lib");
203 #[cfg(target_os = "freebsd")]
204 fn test_rpath_relative() {
205 let config = &mut RPathConfig {
206 used_crates: Vec::new(),
209 out_filename: Path::new("bin/rustc"),
210 get_install_prefix_lib_path: || panic!(),
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 = "dragonfly")]
219 fn test_rpath_relative() {
220 let config = &mut RPathConfig {
221 used_crates: Vec::new(),
224 out_filename: Path::new("bin/rustc"),
225 get_install_prefix_lib_path: || panic!(),
226 realpath: |p| Ok(p.clone())
228 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
229 assert_eq!(res.as_slice(), "$ORIGIN/../lib");
233 #[cfg(target_os = "macos")]
234 fn test_rpath_relative() {
235 let config = &mut RPathConfig {
236 used_crates: Vec::new(),
239 out_filename: Path::new("bin/rustc"),
240 get_install_prefix_lib_path: || panic!(),
241 realpath: |p| Ok(p.clone())
243 let res = get_rpath_relative_to_output(config, &Path::new("lib/libstd.so"));
244 assert_eq!(res.as_slice(), "@loader_path/../lib");