Skip to main content

rustc_codegen_ssa/back/
rpath.rs

1use std::ffi::OsString;
2use std::path::{Path, PathBuf};
3
4use pathdiff::diff_paths;
5use rustc_data_structures::fx::FxHashSet;
6use rustc_fs_util::try_canonicalize;
7use tracing::debug;
8
9pub(super) struct RPathConfig<'a> {
10    pub libs: &'a [&'a Path],
11    pub out_filename: PathBuf,
12    pub is_like_darwin: bool,
13    pub linker_is_gnu: bool,
14}
15
16pub(super) fn get_rpath_linker_args(config: &RPathConfig<'_>) -> Vec<OsString> {
17    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/rpath.rs:17",
                        "rustc_codegen_ssa::back::rpath", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/rpath.rs"),
                        ::tracing_core::__macro_support::Option::Some(17u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::rpath"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("preparing the RPATH!")
                                            as &dyn Value))])
            });
    } else { ; }
};debug!("preparing the RPATH!");
18
19    let rpaths = get_rpaths(config);
20    let mut args = Vec::with_capacity(rpaths.len() * 2); // the minimum needed capacity
21
22    for rpath in rpaths {
23        args.push("-rpath".into());
24        args.push(rpath);
25    }
26
27    if config.linker_is_gnu {
28        // Use DT_RUNPATH instead of DT_RPATH if available
29        args.push("--enable-new-dtags".into());
30
31        // Set DF_ORIGIN for substitute $ORIGIN
32        args.push("-z".into());
33        args.push("origin".into());
34    }
35
36    args
37}
38
39fn get_rpaths(config: &RPathConfig<'_>) -> Vec<OsString> {
40    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/rpath.rs:40",
                        "rustc_codegen_ssa::back::rpath", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/rpath.rs"),
                        ::tracing_core::__macro_support::Option::Some(40u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::rpath"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("output: {0:?}",
                                                    config.out_filename.display()) as &dyn Value))])
            });
    } else { ; }
};debug!("output: {:?}", config.out_filename.display());
41    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/rpath.rs:41",
                        "rustc_codegen_ssa::back::rpath", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/rpath.rs"),
                        ::tracing_core::__macro_support::Option::Some(41u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::rpath"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("libs:")
                                            as &dyn Value))])
            });
    } else { ; }
};debug!("libs:");
42    for libpath in config.libs {
43        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/rpath.rs:43",
                        "rustc_codegen_ssa::back::rpath", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/rpath.rs"),
                        ::tracing_core::__macro_support::Option::Some(43u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::rpath"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("    {0:?}",
                                                    libpath.display()) as &dyn Value))])
            });
    } else { ; }
};debug!("    {:?}", libpath.display());
44    }
45
46    // Use relative paths to the libraries. Binaries can be moved
47    // as long as they maintain the relative relationship to the
48    // crates they depend on.
49    let rpaths = get_rpaths_relative_to_output(config);
50
51    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/rpath.rs:51",
                        "rustc_codegen_ssa::back::rpath", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/rpath.rs"),
                        ::tracing_core::__macro_support::Option::Some(51u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::rpath"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("rpaths:")
                                            as &dyn Value))])
            });
    } else { ; }
};debug!("rpaths:");
52    for rpath in &rpaths {
53        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_ssa/src/back/rpath.rs:53",
                        "rustc_codegen_ssa::back::rpath", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_ssa/src/back/rpath.rs"),
                        ::tracing_core::__macro_support::Option::Some(53u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_ssa::back::rpath"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("    {0:?}",
                                                    rpath) as &dyn Value))])
            });
    } else { ; }
};debug!("    {:?}", rpath);
54    }
55
56    // Remove duplicates
57    minimize_rpaths(&rpaths)
58}
59
60fn get_rpaths_relative_to_output(config: &RPathConfig<'_>) -> Vec<OsString> {
61    config.libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect()
62}
63
64fn get_rpath_relative_to_output(config: &RPathConfig<'_>, lib: &Path) -> OsString {
65    // Mac doesn't appear to support $ORIGIN
66    let prefix = if config.is_like_darwin { "@loader_path" } else { "$ORIGIN" };
67
68    // Strip filenames
69    let lib = lib.parent().unwrap();
70    let output = config.out_filename.parent().unwrap();
71
72    // If output or lib is empty, just assume it locates in current path
73    let lib = if lib == Path::new("") { Path::new(".") } else { lib };
74    let output = if output == Path::new("") { Path::new(".") } else { output };
75
76    let lib = try_canonicalize(lib).unwrap();
77    let output = try_canonicalize(output).unwrap();
78    let relative = path_relative_from(&lib, &output)
79        .unwrap_or_else(|| {
    ::core::panicking::panic_fmt(format_args!("couldn\'t create relative path from {0:?} to {1:?}",
            output, lib));
}panic!("couldn't create relative path from {output:?} to {lib:?}"));
80
81    let mut rpath = OsString::from(prefix);
82    rpath.push("/");
83    rpath.push(relative);
84    rpath
85}
86
87// This routine is adapted from the *old* Path's `path_relative_from`
88// function, which works differently from the new `relative_from` function.
89// In particular, this handles the case on unix where both paths are
90// absolute but with only the root as the common directory.
91fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> {
92    diff_paths(path, base)
93}
94
95fn minimize_rpaths(rpaths: &[OsString]) -> Vec<OsString> {
96    let mut set = FxHashSet::default();
97    let mut minimized = Vec::new();
98    for rpath in rpaths {
99        if set.insert(rpath) {
100            minimized.push(rpath.clone());
101        }
102    }
103    minimized
104}
105
106#[cfg(all(unix, test))]
107mod tests;