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 linker_is_gnu
: bool
,
23 pub get_install_prefix_lib_path
: &'a
mut FnMut() -> 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
.extend_from_slice(&rpaths_to_flags(&rpaths
[..]));
41 // Use DT_RUNPATH instead of DT_RPATH if available
42 if config
.linker_is_gnu
{
43 flags
.push("-Wl,--enable-new-dtags".to_string());
49 fn rpaths_to_flags(rpaths
: &[String
]) -> Vec
<String
> {
50 let mut ret
= Vec
::new();
52 ret
.push(format
!("-Wl,-rpath,{}", &(*rpath
)));
57 fn get_rpaths(config
: &mut RPathConfig
, libs
: &[PathBuf
]) -> Vec
<String
> {
58 debug
!("output: {:?}", config
.out_filename
.display());
61 debug
!(" {:?}", libpath
.display());
64 // Use relative paths to the libraries. Binaries can be moved
65 // as long as they maintain the relative relationship to the
66 // crates they depend on.
67 let rel_rpaths
= get_rpaths_relative_to_output(config
, libs
);
69 // And a final backup rpath to the global library location.
70 let fallback_rpaths
= vec
!(get_install_prefix_rpath(config
));
72 fn log_rpaths(desc
: &str, rpaths
: &[String
]) {
73 debug
!("{} rpaths:", desc
);
75 debug
!(" {}", *rpath
);
79 log_rpaths("relative", &rel_rpaths
[..]);
80 log_rpaths("fallback", &fallback_rpaths
[..]);
82 let mut rpaths
= rel_rpaths
;
83 rpaths
.extend_from_slice(&fallback_rpaths
[..]);
86 let rpaths
= minimize_rpaths(&rpaths
[..]);
90 fn get_rpaths_relative_to_output(config
: &mut RPathConfig
,
91 libs
: &[PathBuf
]) -> Vec
<String
> {
92 libs
.iter().map(|a
| get_rpath_relative_to_output(config
, a
)).collect()
95 fn get_rpath_relative_to_output(config
: &mut RPathConfig
, lib
: &Path
) -> String
{
96 // Mac doesn't appear to support $ORIGIN
97 let prefix
= if config
.is_like_osx
{
103 let cwd
= env
::current_dir().unwrap();
104 let mut lib
= fs
::canonicalize(&cwd
.join(lib
)).unwrap_or(cwd
.join(lib
));
106 let mut output
= cwd
.join(&config
.out_filename
);
108 let output
= fs
::canonicalize(&output
).unwrap_or(output
);
109 let relative
= path_relative_from(&lib
, &output
)
110 .expect(&format
!("couldn't create relative path from {:?} to {:?}", output
, lib
));
111 // FIXME (#9639): This needs to handle non-utf8 paths
112 format
!("{}/{}", prefix
,
113 relative
.to_str().expect("non-utf8 component in path"))
116 // This routine is adapted from the *old* Path's `path_relative_from`
117 // function, which works differently from the new `relative_from` function.
118 // In particular, this handles the case on unix where both paths are
119 // absolute but with only the root as the common directory.
120 fn path_relative_from(path
: &Path
, base
: &Path
) -> Option
<PathBuf
> {
121 use std
::path
::Component
;
123 if path
.is_absolute() != base
.is_absolute() {
124 if path
.is_absolute() {
125 Some(PathBuf
::from(path
))
130 let mut ita
= path
.components();
131 let mut itb
= base
.components();
132 let mut comps
: Vec
<Component
> = vec
![];
134 match (ita
.next(), itb
.next()) {
135 (None
, None
) => break,
138 comps
.extend(ita
.by_ref());
141 (None
, _
) => comps
.push(Component
::ParentDir
),
142 (Some(a
), Some(b
)) if comps
.is_empty() && a
== b
=> (),
143 (Some(a
), Some(b
)) if b
== Component
::CurDir
=> comps
.push(a
),
144 (Some(_
), Some(b
)) if b
== Component
::ParentDir
=> return None
,
145 (Some(a
), Some(_
)) => {
146 comps
.push(Component
::ParentDir
);
148 comps
.push(Component
::ParentDir
);
151 comps
.extend(ita
.by_ref());
156 Some(comps
.iter().map(|c
| c
.as_os_str()).collect())
161 fn get_install_prefix_rpath(config
: &mut RPathConfig
) -> String
{
162 let path
= (config
.get_install_prefix_lib_path
)();
163 let path
= env
::current_dir().unwrap().join(&path
);
164 // FIXME (#9639): This needs to handle non-utf8 paths
165 path
.to_str().expect("non-utf8 component in rpath").to_string()
168 fn minimize_rpaths(rpaths
: &[String
]) -> Vec
<String
> {
169 let mut set
= HashSet
::new();
170 let mut minimized
= Vec
::new();
171 for rpath
in rpaths
{
172 if set
.insert(&rpath
[..]) {
173 minimized
.push(rpath
.clone());
179 #[cfg(all(unix, test))]
181 use super::{RPathConfig}
;
182 use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output}
;
183 use std
::path
::{Path, PathBuf}
;
186 fn test_rpaths_to_flags() {
187 let flags
= rpaths_to_flags(&[
193 "-Wl,-rpath,path2"]);
197 fn test_minimize1() {
198 let res
= minimize_rpaths(&[
199 "rpath1".to_string(),
200 "rpath2".to_string(),
210 fn test_minimize2() {
211 let res
= minimize_rpaths(&[
232 fn test_rpath_relative() {
233 if cfg
!(target_os
= "macos") {
234 let config
= &mut RPathConfig
{
235 used_crates
: Vec
::new(),
238 linker_is_gnu
: false,
239 out_filename
: PathBuf
::from("bin/rustc"),
240 get_install_prefix_lib_path
: &mut || panic
!(),
242 let res
= get_rpath_relative_to_output(config
,
243 Path
::new("lib/libstd.so"));
244 assert_eq
!(res
, "@loader_path/../lib");
246 let config
= &mut RPathConfig
{
247 used_crates
: Vec
::new(),
248 out_filename
: PathBuf
::from("bin/rustc"),
249 get_install_prefix_lib_path
: &mut || panic
!(),
254 let res
= get_rpath_relative_to_output(config
,
255 Path
::new("lib/libstd.so"));
256 assert_eq
!(res
, "$ORIGIN/../lib");