1 // Copyright 2012-2015 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.
11 use std
::collections
::HashSet
;
14 use std
::path
::{Path, PathBuf}
;
17 pub struct RPathConfig
<'a
> {
18 pub used_crates
: Vec
<(ast
::CrateNum
, Option
<PathBuf
>)>,
19 pub out_filename
: PathBuf
,
20 pub is_like_osx
: bool
,
22 pub get_install_prefix_lib_path
: &'a
mut FnMut() -> PathBuf
,
23 pub realpath
: &'a
mut FnMut(&Path
) -> io
::Result
<PathBuf
>,
26 pub fn get_rpath_flags(config
: &mut RPathConfig
) -> Vec
<String
> {
27 // No rpath on windows
28 if !config
.has_rpath
{
32 let mut flags
= Vec
::new();
34 debug
!("preparing the RPATH!");
36 let libs
= config
.used_crates
.clone();
37 let libs
= libs
.into_iter().filter_map(|(_
, l
)| l
).collect
::<Vec
<_
>>();
38 let rpaths
= get_rpaths(config
, &libs
[..]);
39 flags
.push_all(&rpaths_to_flags(&rpaths
[..]));
43 fn rpaths_to_flags(rpaths
: &[String
]) -> Vec
<String
> {
44 let mut ret
= Vec
::new();
46 ret
.push(format
!("-Wl,-rpath,{}", &(*rpath
)));
51 fn get_rpaths(config
: &mut RPathConfig
, libs
: &[PathBuf
]) -> Vec
<String
> {
52 debug
!("output: {:?}", config
.out_filename
.display());
55 debug
!(" {:?}", libpath
.display());
58 // Use relative paths to the libraries. Binaries can be moved
59 // as long as they maintain the relative relationship to the
60 // crates they depend on.
61 let rel_rpaths
= get_rpaths_relative_to_output(config
, libs
);
63 // And a final backup rpath to the global library location.
64 let fallback_rpaths
= vec
!(get_install_prefix_rpath(config
));
66 fn log_rpaths(desc
: &str, rpaths
: &[String
]) {
67 debug
!("{} rpaths:", desc
);
69 debug
!(" {}", *rpath
);
73 log_rpaths("relative", &rel_rpaths
[..]);
74 log_rpaths("fallback", &fallback_rpaths
[..]);
76 let mut rpaths
= rel_rpaths
;
77 rpaths
.push_all(&fallback_rpaths
[..]);
80 let rpaths
= minimize_rpaths(&rpaths
[..]);
84 fn get_rpaths_relative_to_output(config
: &mut RPathConfig
,
85 libs
: &[PathBuf
]) -> Vec
<String
> {
86 libs
.iter().map(|a
| get_rpath_relative_to_output(config
, a
)).collect()
89 fn get_rpath_relative_to_output(config
: &mut RPathConfig
, lib
: &Path
) -> String
{
90 // Mac doesn't appear to support $ORIGIN
91 let prefix
= if config
.is_like_osx
{
97 let cwd
= env
::current_dir().unwrap();
98 let mut lib
= (config
.realpath
)(&cwd
.join(lib
)).unwrap();
100 let mut output
= (config
.realpath
)(&cwd
.join(&config
.out_filename
)).unwrap();
102 let relative
= path_relative_from(&lib
, &output
)
103 .expect(&format
!("couldn't create relative path from {:?} to {:?}", output
, lib
));
104 // FIXME (#9639): This needs to handle non-utf8 paths
105 format
!("{}/{}", prefix
,
106 relative
.to_str().expect("non-utf8 component in path"))
109 // This routine is adapted from the *old* Path's `path_relative_from`
110 // function, which works differently from the new `relative_from` function.
111 // In particular, this handles the case on unix where both paths are
112 // absolute but with only the root as the common directory.
113 fn path_relative_from(path
: &Path
, base
: &Path
) -> Option
<PathBuf
> {
114 use std
::path
::Component
;
116 if path
.is_absolute() != base
.is_absolute() {
117 if path
.is_absolute() {
118 Some(PathBuf
::from(path
))
123 let mut ita
= path
.components();
124 let mut itb
= base
.components();
125 let mut comps
: Vec
<Component
> = vec
![];
127 match (ita
.next(), itb
.next()) {
128 (None
, None
) => break,
131 comps
.extend(ita
.by_ref());
134 (None
, _
) => comps
.push(Component
::ParentDir
),
135 (Some(a
), Some(b
)) if comps
.is_empty() && a
== b
=> (),
136 (Some(a
), Some(b
)) if b
== Component
::CurDir
=> comps
.push(a
),
137 (Some(_
), Some(b
)) if b
== Component
::ParentDir
=> return None
,
138 (Some(a
), Some(_
)) => {
139 comps
.push(Component
::ParentDir
);
141 comps
.push(Component
::ParentDir
);
144 comps
.extend(ita
.by_ref());
149 Some(comps
.iter().map(|c
| c
.as_os_str()).collect())
154 fn get_install_prefix_rpath(config
: &mut RPathConfig
) -> String
{
155 let path
= (config
.get_install_prefix_lib_path
)();
156 let path
= env
::current_dir().unwrap().join(&path
);
157 // FIXME (#9639): This needs to handle non-utf8 paths
158 path
.to_str().expect("non-utf8 component in rpath").to_string()
161 fn minimize_rpaths(rpaths
: &[String
]) -> Vec
<String
> {
162 let mut set
= HashSet
::new();
163 let mut minimized
= Vec
::new();
164 for rpath
in rpaths
{
165 if set
.insert(&rpath
[..]) {
166 minimized
.push(rpath
.clone());
172 #[cfg(all(unix, test))]
174 use super::{RPathConfig}
;
175 use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output}
;
176 use std
::path
::{Path, PathBuf}
;
179 fn test_rpaths_to_flags() {
180 let flags
= rpaths_to_flags(&[
186 "-Wl,-rpath,path2"]);
190 fn test_minimize1() {
191 let res
= minimize_rpaths(&[
192 "rpath1".to_string(),
193 "rpath2".to_string(),
203 fn test_minimize2() {
204 let res
= minimize_rpaths(&[
225 fn test_rpath_relative() {
226 if cfg
!(target_os
= "macos") {
227 let config
= &mut RPathConfig
{
228 used_crates
: Vec
::new(),
231 out_filename
: PathBuf
::from("bin/rustc"),
232 get_install_prefix_lib_path
: &mut || panic
!(),
233 realpath
: &mut |p
| Ok(p
.to_path_buf()),
235 let res
= get_rpath_relative_to_output(config
,
236 Path
::new("lib/libstd.so"));
237 assert_eq
!(res
, "@loader_path/../lib");
239 let config
= &mut RPathConfig
{
240 used_crates
: Vec
::new(),
241 out_filename
: PathBuf
::from("bin/rustc"),
242 get_install_prefix_lib_path
: &mut || panic
!(),
245 realpath
: &mut |p
| Ok(p
.to_path_buf()),
247 let res
= get_rpath_relative_to_output(config
,
248 Path
::new("lib/libstd.so"));
249 assert_eq
!(res
, "$ORIGIN/../lib");