]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_ssa/back/link.rs
2a5ecf9a0593ff400a301a73c6e15371b8b1ee32
[rust.git] / src / librustc_codegen_ssa / back / link.rs
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.
3
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;
10
11 use super::command::Command;
12 use CrateInfo;
13
14 use cc::windows_registry;
15 use std::fs;
16 use std::path::{Path, PathBuf};
17 use std::env;
18
19 pub fn remove(sess: &Session, path: &Path) {
20     if let Err(e) = fs::remove_file(path) {
21         sess.err(&format!("failed to remove {}: {}",
22                           path.display(),
23                           e));
24     }
25 }
26
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
29 // toolchain
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");
32
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 ...`.
37     //
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
40     // emscripten itself.
41     let mut cmd = match linker.to_str() {
42         Some(linker) if cfg!(windows) && linker.ends_with(".bat") => Command::bat_script(linker),
43         _ => match flavor {
44             LinkerFlavor::Lld(f) => Command::lld(linker, f),
45             LinkerFlavor::Msvc
46                 if sess.opts.cg.linker.is_none() && sess.target.target.options.linker.is_none() =>
47             {
48                 Command::new(msvc_tool.as_ref().map(|t| t.path()).unwrap_or(linker))
49             },
50             _ => Command::new(linker),
51         }
52     };
53
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() {
63                 if k == "PATH" {
64                     new_path.extend(env::split_paths(v));
65                     msvc_changed_path = true;
66                 } else {
67                     cmd.env(k, v);
68                 }
69             }
70         }
71     }
72
73     if !msvc_changed_path {
74         if let Some(path) = env::var_os("PATH") {
75             new_path.extend(env::split_paths(&path));
76         }
77     }
78     cmd.env("PATH", env::join_paths(new_path).unwrap());
79
80     (linker.to_path_buf(), cmd)
81 }
82
83 pub fn each_linked_rlib(sess: &Session,
84                                info: &CrateInfo,
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 {
93         Some(f) => f,
94         None => return Err("could not find formats for rlibs".to_string())
95     };
96     for &(cnum, ref path) in crates {
97         match fmts.get(cnum.as_usize() - 1) {
98             Some(&Linkage::NotLinked) |
99             Some(&Linkage::IncludedFromDylib) => continue,
100             Some(_) => {}
101             None => return Err("could not find formats for rlibs".to_string())
102         }
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",
108                                    name))
109             }
110             LibSource::None => {
111                 return Err(format!("could not find rlib for: `{}`", name))
112             }
113         };
114         f(cnum, &path);
115     }
116     Ok(())
117 }
118
119 /// Returns a boolean indicating whether the specified crate should be ignored
120 /// during LTO.
121 ///
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.
125 ///
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
128 /// LTO.
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))
135 }
136
137 pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
138     fn infer_from(
139         sess: &Session,
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",
153             }), flavor)),
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())
157                 } else {
158                     linker.to_str()
159                 }.unwrap_or_else(|| {
160                     sess.fatal("couldn't extract file stem from specified linker");
161                 }).to_owned();
162
163                 let flavor = if stem == "emcc" {
164                     LinkerFlavor::Em
165                 } else if stem == "gcc" || stem.ends_with("-gcc") {
166                     LinkerFlavor::Gcc
167                 } else if stem == "ld" || stem == "ld.lld" || stem.ends_with("-ld") {
168                     LinkerFlavor::Ld
169                 } else if stem == "link" || stem == "lld-link" {
170                     LinkerFlavor::Msvc
171                 } else if stem == "lld" || stem == "rust-lld" {
172                     LinkerFlavor::Lld(sess.target.target.options.lld_flavor)
173                 } else {
174                     // fall back to the value in the target spec
175                     sess.target.target.linker_flavor
176                 };
177
178                 Some((linker, flavor))
179             },
180             (None, None) => None,
181         }
182     }
183
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) {
187         return ret;
188     }
189
190     if let Some(ret) = infer_from(
191         sess,
192         sess.target.target.options.linker.clone().map(PathBuf::from),
193         Some(sess.target.target.linker_flavor),
194     ) {
195         return ret;
196     }
197
198     bug!("Not enough information provided to determine how to invoke the linker");
199 }