1 /// For all the linkers we support, and information they might
2 /// need out of the shared crate context before we get rid of it.
4 use rustc::session::{Session, config};
5 use rustc::session::search_paths::PathKind;
6 use rustc::middle::dependency_format::Linkage;
7 use rustc::middle::cstore::LibSource;
8 use rustc_target::spec::LinkerFlavor;
9 use rustc::hir::def_id::CrateNum;
11 use super::command::Command;
14 use cc::windows_registry;
16 use std::path::{Path, PathBuf};
19 pub fn remove(sess: &Session, path: &Path) {
20 if let Err(e) = fs::remove_file(path) {
21 sess.err(&format!("failed to remove {}: {}",
27 // The third parameter is for env vars, used on windows to set up the
28 // path for MSVC to find its DLLs, and gcc to find its bundled
30 pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathBuf, Command) {
31 let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe");
33 // If our linker looks like a batch script on Windows then to execute this
34 // we'll need to spawn `cmd` explicitly. This is primarily done to handle
35 // emscripten where the linker is `emcc.bat` and needs to be spawned as
36 // `cmd /c emcc.bat ...`.
38 // This worked historically but is needed manually since #42436 (regression
39 // was tagged as #42791) and some more info can be found on #44443 for
41 let mut cmd = match linker.to_str() {
42 Some(linker) if cfg!(windows) && linker.ends_with(".bat") => Command::bat_script(linker),
44 LinkerFlavor::Lld(f) => Command::lld(linker, f),
46 if sess.opts.cg.linker.is_none() && sess.target.target.options.linker.is_none() =>
48 Command::new(msvc_tool.as_ref().map(|t| t.path()).unwrap_or(linker))
50 _ => Command::new(linker),
54 // The compiler's sysroot often has some bundled tools, so add it to the
55 // PATH for the child.
56 let mut new_path = sess.host_filesearch(PathKind::All)
57 .get_tools_search_paths();
58 let mut msvc_changed_path = false;
59 if sess.target.target.options.is_like_msvc {
60 if let Some(ref tool) = msvc_tool {
61 cmd.args(tool.args());
62 for &(ref k, ref v) in tool.env() {
64 new_path.extend(env::split_paths(v));
65 msvc_changed_path = true;
73 if !msvc_changed_path {
74 if let Some(path) = env::var_os("PATH") {
75 new_path.extend(env::split_paths(&path));
78 cmd.env("PATH", env::join_paths(new_path).unwrap());
80 (linker.to_path_buf(), cmd)
83 pub fn each_linked_rlib(sess: &Session,
85 f: &mut dyn FnMut(CrateNum, &Path)) -> Result<(), String> {
86 let crates = info.used_crates_static.iter();
87 let fmts = sess.dependency_formats.borrow();
88 let fmts = fmts.get(&config::CrateType::Executable)
89 .or_else(|| fmts.get(&config::CrateType::Staticlib))
90 .or_else(|| fmts.get(&config::CrateType::Cdylib))
91 .or_else(|| fmts.get(&config::CrateType::ProcMacro));
92 let fmts = match fmts {
94 None => return Err("could not find formats for rlibs".to_string())
96 for &(cnum, ref path) in crates {
97 match fmts.get(cnum.as_usize() - 1) {
98 Some(&Linkage::NotLinked) |
99 Some(&Linkage::IncludedFromDylib) => continue,
101 None => return Err("could not find formats for rlibs".to_string())
103 let name = &info.crate_name[&cnum];
104 let path = match *path {
105 LibSource::Some(ref p) => p,
106 LibSource::MetadataOnly => {
107 return Err(format!("could not find rlib for: `{}`, found rmeta (metadata) file",
111 return Err(format!("could not find rlib for: `{}`", name))
119 /// Returns a boolean indicating whether the specified crate should be ignored
122 /// Crates ignored during LTO are not lumped together in the "massive object
123 /// file" that we create and are linked in their normal rlib states. See
124 /// comments below for what crates do not participate in LTO.
126 /// It's unusual for a crate to not participate in LTO. Typically only
127 /// compiler-specific and unstable crates have a reason to not participate in
129 pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool {
130 // If our target enables builtin function lowering in LLVM then the
131 // crates providing these functions don't participate in LTO (e.g.
132 // no_builtins or compiler builtins crates).
133 !sess.target.target.options.no_builtins &&
134 (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum))
137 pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
140 linker: Option<PathBuf>,
141 flavor: Option<LinkerFlavor>,
142 ) -> Option<(PathBuf, LinkerFlavor)> {
143 match (linker, flavor) {
144 (Some(linker), Some(flavor)) => Some((linker, flavor)),
145 // only the linker flavor is known; use the default linker for the selected flavor
146 (None, Some(flavor)) => Some((PathBuf::from(match flavor {
147 LinkerFlavor::Em => if cfg!(windows) { "emcc.bat" } else { "emcc" },
148 LinkerFlavor::Gcc => "cc",
149 LinkerFlavor::Ld => "ld",
150 LinkerFlavor::Msvc => "link.exe",
151 LinkerFlavor::Lld(_) => "lld",
152 LinkerFlavor::PtxLinker => "rust-ptx-linker",
154 (Some(linker), None) => {
155 let stem = if linker.extension().and_then(|ext| ext.to_str()) == Some("exe") {
156 linker.file_stem().and_then(|stem| stem.to_str())
159 }.unwrap_or_else(|| {
160 sess.fatal("couldn't extract file stem from specified linker");
163 let flavor = if stem == "emcc" {
165 } else if stem == "gcc" || stem.ends_with("-gcc") {
167 } else if stem == "ld" || stem == "ld.lld" || stem.ends_with("-ld") {
169 } else if stem == "link" || stem == "lld-link" {
171 } else if stem == "lld" || stem == "rust-lld" {
172 LinkerFlavor::Lld(sess.target.target.options.lld_flavor)
174 // fall back to the value in the target spec
175 sess.target.target.linker_flavor
178 Some((linker, flavor))
180 (None, None) => None,
184 // linker and linker flavor specified via command line have precedence over what the target
185 // specification specifies
186 if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), sess.opts.cg.linker_flavor) {
190 if let Some(ret) = infer_from(
192 sess.target.target.options.linker.clone().map(PathBuf::from),
193 Some(sess.target.target.linker_flavor),
198 bug!("Not enough information provided to determine how to invoke the linker");