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
;
13 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
,
25 pub fn get_rpath_flags(config
: &mut RPathConfig
) -> Vec
<String
> {
26 // No rpath on windows
27 if !config
.has_rpath
{
31 let mut flags
= Vec
::new();
33 debug
!("preparing the RPATH!");
35 let libs
= config
.used_crates
.clone();
36 let libs
= libs
.into_iter().filter_map(|(_
, l
)| l
).collect
::<Vec
<_
>>();
37 let rpaths
= get_rpaths(config
, &libs
[..]);
38 flags
.extend_from_slice(&rpaths_to_flags(&rpaths
[..]));
42 fn rpaths_to_flags(rpaths
: &[String
]) -> Vec
<String
> {
43 let mut ret
= Vec
::new();
45 ret
.push(format
!("-Wl,-rpath,{}", &(*rpath
)));
50 fn get_rpaths(config
: &mut RPathConfig
, libs
: &[PathBuf
]) -> Vec
<String
> {
51 debug
!("output: {:?}", config
.out_filename
.display());
54 debug
!(" {:?}", libpath
.display());
57 // Use relative paths to the libraries. Binaries can be moved
58 // as long as they maintain the relative relationship to the
59 // crates they depend on.
60 let rel_rpaths
= get_rpaths_relative_to_output(config
, libs
);
62 // And a final backup rpath to the global library location.
63 let fallback_rpaths
= vec
!(get_install_prefix_rpath(config
));
65 fn log_rpaths(desc
: &str, rpaths
: &[String
]) {
66 debug
!("{} rpaths:", desc
);
68 debug
!(" {}", *rpath
);
72 log_rpaths("relative", &rel_rpaths
[..]);
73 log_rpaths("fallback", &fallback_rpaths
[..]);
75 let mut rpaths
= rel_rpaths
;
76 rpaths
.extend_from_slice(&fallback_rpaths
[..]);
79 let rpaths
= minimize_rpaths(&rpaths
[..]);
83 fn get_rpaths_relative_to_output(config
: &mut RPathConfig
,
84 libs
: &[PathBuf
]) -> Vec
<String
> {
85 libs
.iter().map(|a
| get_rpath_relative_to_output(config
, a
)).collect()
88 fn get_rpath_relative_to_output(config
: &mut RPathConfig
, lib
: &Path
) -> String
{
89 // Mac doesn't appear to support $ORIGIN
90 let prefix
= if config
.is_like_osx
{
96 let cwd
= env
::current_dir().unwrap();
97 let mut lib
= fs
::canonicalize(&cwd
.join(lib
)).unwrap_or(cwd
.join(lib
));
99 let mut output
= cwd
.join(&config
.out_filename
);
101 let output
= fs
::canonicalize(&output
).unwrap_or(output
);
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
!(),
234 let res
= get_rpath_relative_to_output(config
,
235 Path
::new("lib/libstd.so"));
236 assert_eq
!(res
, "@loader_path/../lib");
238 let config
= &mut RPathConfig
{
239 used_crates
: Vec
::new(),
240 out_filename
: PathBuf
::from("bin/rustc"),
241 get_install_prefix_lib_path
: &mut || panic
!(),
245 let res
= get_rpath_relative_to_output(config
,
246 Path
::new("lib/libstd.so"));
247 assert_eq
!(res
, "$ORIGIN/../lib");