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}
;
16 use rustc
::hir
::def_id
::CrateNum
;
18 pub struct RPathConfig
<'a
> {
19 pub used_crates
: Vec
<(CrateNum
, Option
<PathBuf
>)>,
20 pub out_filename
: PathBuf
,
21 pub is_like_osx
: bool
,
23 pub linker_is_gnu
: bool
,
24 pub get_install_prefix_lib_path
: &'a
mut FnMut() -> PathBuf
,
27 pub fn get_rpath_flags(config
: &mut 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
)| l
).collect
::<Vec
<_
>>();
39 let rpaths
= get_rpaths(config
, &libs
[..]);
40 flags
.extend_from_slice(&rpaths_to_flags(&rpaths
[..]));
42 // Use DT_RUNPATH instead of DT_RPATH if available
43 if config
.linker_is_gnu
{
44 flags
.push("-Wl,--enable-new-dtags".to_string());
50 fn rpaths_to_flags(rpaths
: &[String
]) -> Vec
<String
> {
51 let mut ret
= Vec
::new();
53 ret
.push(format
!("-Wl,-rpath,{}", &(*rpath
)));
58 fn get_rpaths(config
: &mut RPathConfig
, libs
: &[PathBuf
]) -> Vec
<String
> {
59 debug
!("output: {:?}", config
.out_filename
.display());
62 debug
!(" {:?}", libpath
.display());
65 // Use relative paths to the libraries. Binaries can be moved
66 // as long as they maintain the relative relationship to the
67 // crates they depend on.
68 let rel_rpaths
= get_rpaths_relative_to_output(config
, libs
);
70 // And a final backup rpath to the global library location.
71 let fallback_rpaths
= vec
![get_install_prefix_rpath(config
)];
73 fn log_rpaths(desc
: &str, rpaths
: &[String
]) {
74 debug
!("{} rpaths:", desc
);
76 debug
!(" {}", *rpath
);
80 log_rpaths("relative", &rel_rpaths
[..]);
81 log_rpaths("fallback", &fallback_rpaths
[..]);
83 let mut rpaths
= rel_rpaths
;
84 rpaths
.extend_from_slice(&fallback_rpaths
[..]);
87 let rpaths
= minimize_rpaths(&rpaths
[..]);
91 fn get_rpaths_relative_to_output(config
: &mut RPathConfig
,
92 libs
: &[PathBuf
]) -> Vec
<String
> {
93 libs
.iter().map(|a
| get_rpath_relative_to_output(config
, a
)).collect()
96 fn get_rpath_relative_to_output(config
: &mut RPathConfig
, lib
: &Path
) -> String
{
97 // Mac doesn't appear to support $ORIGIN
98 let prefix
= if config
.is_like_osx
{
104 let cwd
= env
::current_dir().unwrap();
105 let mut lib
= fs
::canonicalize(&cwd
.join(lib
)).unwrap_or(cwd
.join(lib
));
107 let mut output
= cwd
.join(&config
.out_filename
);
109 let output
= fs
::canonicalize(&output
).unwrap_or(output
);
110 let relative
= path_relative_from(&lib
, &output
)
111 .expect(&format
!("couldn't create relative path from {:?} to {:?}", output
, lib
));
112 // FIXME (#9639): This needs to handle non-utf8 paths
113 format
!("{}/{}", prefix
,
114 relative
.to_str().expect("non-utf8 component in path"))
117 // This routine is adapted from the *old* Path's `path_relative_from`
118 // function, which works differently from the new `relative_from` function.
119 // In particular, this handles the case on unix where both paths are
120 // absolute but with only the root as the common directory.
121 fn path_relative_from(path
: &Path
, base
: &Path
) -> Option
<PathBuf
> {
122 use std
::path
::Component
;
124 if path
.is_absolute() != base
.is_absolute() {
125 if path
.is_absolute() {
126 Some(PathBuf
::from(path
))
131 let mut ita
= path
.components();
132 let mut itb
= base
.components();
133 let mut comps
: Vec
<Component
> = vec
![];
135 match (ita
.next(), itb
.next()) {
136 (None
, None
) => break,
139 comps
.extend(ita
.by_ref());
142 (None
, _
) => comps
.push(Component
::ParentDir
),
143 (Some(a
), Some(b
)) if comps
.is_empty() && a
== b
=> (),
144 (Some(a
), Some(b
)) if b
== Component
::CurDir
=> comps
.push(a
),
145 (Some(_
), Some(b
)) if b
== Component
::ParentDir
=> return None
,
146 (Some(a
), Some(_
)) => {
147 comps
.push(Component
::ParentDir
);
149 comps
.push(Component
::ParentDir
);
152 comps
.extend(ita
.by_ref());
157 Some(comps
.iter().map(|c
| c
.as_os_str()).collect())
162 fn get_install_prefix_rpath(config
: &mut RPathConfig
) -> String
{
163 let path
= (config
.get_install_prefix_lib_path
)();
164 let path
= env
::current_dir().unwrap().join(&path
);
165 // FIXME (#9639): This needs to handle non-utf8 paths
166 path
.to_str().expect("non-utf8 component in rpath").to_string()
169 fn minimize_rpaths(rpaths
: &[String
]) -> Vec
<String
> {
170 let mut set
= HashSet
::new();
171 let mut minimized
= Vec
::new();
172 for rpath
in rpaths
{
173 if set
.insert(&rpath
[..]) {
174 minimized
.push(rpath
.clone());
180 #[cfg(all(unix, test))]
182 use super::{RPathConfig}
;
183 use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output}
;
184 use std
::path
::{Path, PathBuf}
;
187 fn test_rpaths_to_flags() {
188 let flags
= rpaths_to_flags(&[
194 "-Wl,-rpath,path2"]);
198 fn test_minimize1() {
199 let res
= minimize_rpaths(&[
200 "rpath1".to_string(),
201 "rpath2".to_string(),
211 fn test_minimize2() {
212 let res
= minimize_rpaths(&[
233 fn test_rpath_relative() {
234 if cfg
!(target_os
= "macos") {
235 let config
= &mut RPathConfig
{
236 used_crates
: Vec
::new(),
239 linker_is_gnu
: false,
240 out_filename
: PathBuf
::from("bin/rustc"),
241 get_install_prefix_lib_path
: &mut || panic
!(),
243 let res
= get_rpath_relative_to_output(config
,
244 Path
::new("lib/libstd.so"));
245 assert_eq
!(res
, "@loader_path/../lib");
247 let config
= &mut RPathConfig
{
248 used_crates
: Vec
::new(),
249 out_filename
: PathBuf
::from("bin/rustc"),
250 get_install_prefix_lib_path
: &mut || panic
!(),
255 let res
= get_rpath_relative_to_output(config
,
256 Path
::new("lib/libstd.so"));
257 assert_eq
!(res
, "$ORIGIN/../lib");