1 use rustc_data_structures
::fx
::FxHashSet
;
3 use std
::path
::{Path, PathBuf}
;
6 use rustc
::hir
::def_id
::CrateNum
;
7 use rustc
::middle
::cstore
::LibSource
;
9 pub struct RPathConfig
<'a
> {
10 pub used_crates
: &'a
[(CrateNum
, LibSource
)],
11 pub out_filename
: PathBuf
,
12 pub is_like_osx
: bool
,
14 pub linker_is_gnu
: bool
,
15 pub get_install_prefix_lib_path
: &'a
mut dyn FnMut() -> PathBuf
,
18 pub fn get_rpath_flags(config
: &mut RPathConfig
<'_
>) -> Vec
<String
> {
19 // No rpath on windows
20 if !config
.has_rpath
{
24 debug
!("preparing the RPATH!");
26 let libs
= config
.used_crates
.clone();
27 let libs
= libs
.iter().filter_map(|&(_
, ref l
)| l
.option()).collect
::<Vec
<_
>>();
28 let rpaths
= get_rpaths(config
, &libs
);
29 let mut flags
= rpaths_to_flags(&rpaths
);
31 // Use DT_RUNPATH instead of DT_RPATH if available
32 if config
.linker_is_gnu
{
33 flags
.push("-Wl,--enable-new-dtags".to_owned());
39 fn rpaths_to_flags(rpaths
: &[String
]) -> Vec
<String
> {
40 let mut ret
= Vec
::with_capacity(rpaths
.len()); // the minimum needed capacity
43 if rpath
.contains('
,'
) {
44 ret
.push("-Wl,-rpath".into());
45 ret
.push("-Xlinker".into());
46 ret
.push(rpath
.clone());
48 ret
.push(format
!("-Wl,-rpath,{}", &(*rpath
)));
55 fn get_rpaths(config
: &mut RPathConfig
<'_
>, libs
: &[PathBuf
]) -> Vec
<String
> {
56 debug
!("output: {:?}", config
.out_filename
.display());
59 debug
!(" {:?}", libpath
.display());
62 // Use relative paths to the libraries. Binaries can be moved
63 // as long as they maintain the relative relationship to the
64 // crates they depend on.
65 let rel_rpaths
= get_rpaths_relative_to_output(config
, libs
);
67 // And a final backup rpath to the global library location.
68 let fallback_rpaths
= vec
![get_install_prefix_rpath(config
)];
70 fn log_rpaths(desc
: &str, rpaths
: &[String
]) {
71 debug
!("{} rpaths:", desc
);
73 debug
!(" {}", *rpath
);
77 log_rpaths("relative", &rel_rpaths
);
78 log_rpaths("fallback", &fallback_rpaths
);
80 let mut rpaths
= rel_rpaths
;
81 rpaths
.extend_from_slice(&fallback_rpaths
);
84 let rpaths
= minimize_rpaths(&rpaths
);
89 fn get_rpaths_relative_to_output(config
: &mut RPathConfig
<'_
>,
90 libs
: &[PathBuf
]) -> 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
<'_
>, lib
: &Path
) -> String
{
95 // Mac doesn't appear to support $ORIGIN
96 let prefix
= if config
.is_like_osx
{
102 let cwd
= env
::current_dir().unwrap();
103 let mut lib
= fs
::canonicalize(&cwd
.join(lib
)).unwrap_or_else(|_
| cwd
.join(lib
));
104 lib
.pop(); // strip filename
105 let mut output
= cwd
.join(&config
.out_filename
);
106 output
.pop(); // strip filename
107 let output
= fs
::canonicalize(&output
).unwrap_or(output
);
108 let relative
= path_relative_from(&lib
, &output
).unwrap_or_else(||
109 panic
!("couldn't create relative path from {:?} to {:?}", output
, lib
));
110 // FIXME (#9639): This needs to handle non-utf8 paths
111 format
!("{}/{}", prefix
, relative
.to_str().expect("non-utf8 component in path"))
114 // This routine is adapted from the *old* Path's `path_relative_from`
115 // function, which works differently from the new `relative_from` function.
116 // In particular, this handles the case on unix where both paths are
117 // absolute but with only the root as the common directory.
118 fn path_relative_from(path
: &Path
, base
: &Path
) -> Option
<PathBuf
> {
119 use std
::path
::Component
;
121 if path
.is_absolute() != base
.is_absolute() {
122 path
.is_absolute().then(|| PathBuf
::from(path
))
124 let mut ita
= path
.components();
125 let mut itb
= base
.components();
126 let mut comps
: Vec
<Component
<'_
>> = vec
![];
128 match (ita
.next(), itb
.next()) {
129 (None
, None
) => break,
132 comps
.extend(ita
.by_ref());
135 (None
, _
) => comps
.push(Component
::ParentDir
),
136 (Some(a
), Some(b
)) if comps
.is_empty() && a
== b
=> (),
137 (Some(a
), Some(b
)) if b
== Component
::CurDir
=> comps
.push(a
),
138 (Some(_
), Some(b
)) if b
== Component
::ParentDir
=> return None
,
139 (Some(a
), Some(_
)) => {
140 comps
.push(Component
::ParentDir
);
141 comps
.extend(itb
.map(|_
| Component
::ParentDir
));
143 comps
.extend(ita
.by_ref());
148 Some(comps
.iter().map(|c
| c
.as_os_str()).collect())
153 fn get_install_prefix_rpath(config
: &mut RPathConfig
<'_
>) -> String
{
154 let path
= (config
.get_install_prefix_lib_path
)();
155 let path
= env
::current_dir().unwrap().join(&path
);
156 // FIXME (#9639): This needs to handle non-utf8 paths
157 path
.to_str().expect("non-utf8 component in rpath").to_owned()
160 fn minimize_rpaths(rpaths
: &[String
]) -> Vec
<String
> {
161 let mut set
= FxHashSet
::default();
162 let mut minimized
= Vec
::new();
163 for rpath
in rpaths
{
164 if set
.insert(rpath
) {
165 minimized
.push(rpath
.clone());
171 #[cfg(all(unix, test))]