rustc: Tweak default linker selection

This commit refactors how the path to the linker that we're going to invoke is
selected. Previously all targets listed *both* a `LinkerFlavor` and a `linker`
(path) option, but this meant that whenever you changed one you had to change
the other. The purpose of this commit is to avoid coupling these where possible.

Target specifications now only unconditionally define the *flavor* of the linker
that they're using by default. If not otherwise specified each flavor now
implies a particular default linker to run. As a result, this means that if
you'd like to test out `ld` for example you should be able to do:

    rustc -Z linker-flavor=ld foo.rs

whereas previously you had to do

    rustc -Z linker-flavor=ld -C linker=ld foo.rs

This will hopefully make it a bit easier to tinker around with variants that
should otherwise be well known to work, for example with LLD, `ld` on OSX, etc.
This commit is contained in:
Alex Crichton 2018-02-10 12:09:25 -08:00
parent d69b24805b
commit 0129b01a41
26 changed files with 83 additions and 118 deletions

View File

@ -606,8 +606,10 @@ impl Step for Std {
let mut src = builder.sysroot_libdir(compiler, target).to_path_buf();
src.pop(); // Remove the trailing /lib folder from the sysroot_libdir
cp_filtered(&src, &dst, &|path| {
path.file_name().and_then(|s| s.to_str()) !=
Some(build.config.rust_codegen_backends_dir.as_str())
let name = path.file_name().and_then(|s| s.to_str());
name != Some(build.config.rust_codegen_backends_dir.as_str()) &&
name != Some("bin")
});
let mut cmd = rust_installer(builder);

View File

@ -208,7 +208,7 @@ impl Step for Llvm {
cfg.define("LLVM_NATIVE_BUILD", build.llvm_out(build.build).join("build"));
}
configure_cmake(build, target, &mut cfg);
configure_cmake(build, target, &mut cfg, false);
// FIXME: we don't actually need to build all LLVM tools and all LLVM
// libraries here, e.g. we just want a few components and a few
@ -241,7 +241,8 @@ fn check_llvm_version(build: &Build, llvm_config: &Path) {
fn configure_cmake(build: &Build,
target: Interned<String>,
cfg: &mut cmake::Config) {
cfg: &mut cmake::Config,
building_dist_binaries: bool) {
if build.config.ninja {
cfg.generator("Ninja");
}
@ -294,8 +295,10 @@ fn configure_cmake(build: &Build,
cfg.build_arg("-j").build_arg(build.jobs().to_string());
cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" "));
let mut cxxflags = build.cflags(target).join(" ");
if build.config.llvm_static_stdcpp && !target.contains("windows") {
cxxflags.push_str(" -static-libstdc++");
if building_dist_binaries {
if build.config.llvm_static_stdcpp && !target.contains("windows") {
cxxflags.push_str(" -static-libstdc++");
}
}
cfg.define("CMAKE_CXX_FLAGS", cxxflags);
if let Some(ar) = build.ar(target) {
@ -350,7 +353,7 @@ impl Step for Lld {
t!(fs::create_dir_all(&out_dir));
let mut cfg = cmake::Config::new(build.src.join("src/tools/lld"));
configure_cmake(build, target, &mut cfg);
configure_cmake(build, target, &mut cfg, true);
cfg.out_dir(&out_dir)
.profile("Release")

View File

@ -57,6 +57,9 @@ pub enum LinkerFlavor {
RustcEncodable, RustcDecodable)]
pub enum LldFlavor {
Wasm,
Ld64,
Ld,
Link,
}
impl ToJson for LinkerFlavor {
@ -94,6 +97,9 @@ flavor_mappings! {
((LinkerFlavor::Ld), "ld"),
((LinkerFlavor::Msvc), "msvc"),
((LinkerFlavor::Lld(LldFlavor::Wasm)), "wasm-ld"),
((LinkerFlavor::Lld(LldFlavor::Ld64)), "ld64.lld"),
((LinkerFlavor::Lld(LldFlavor::Ld)), "ld.lld"),
((LinkerFlavor::Lld(LldFlavor::Link)), "lld-link"),
}
#[derive(Clone, Copy, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)]

View File

@ -15,7 +15,7 @@ pub fn target() -> TargetResult {
let mut base = super::cloudabi_base::opts();
base.max_atomic_width = Some(128);
base.abi_blacklist = super::arm_base::abi_blacklist();
base.linker = "aarch64-unknown-cloudabi-cc".to_string();
base.linker = Some("aarch64-unknown-cloudabi-cc".to_string());
Ok(Target {
llvm_target: "aarch64-unknown-cloudabi".to_string(),

View File

@ -17,7 +17,7 @@ pub fn target() -> TargetResult {
base.max_atomic_width = Some(64);
base.features = "+v7,+vfp3,+neon".to_string();
base.abi_blacklist = super::arm_base::abi_blacklist();
base.linker = "armv7-unknown-cloudabi-eabihf-cc".to_string();
base.linker = Some("armv7-unknown-cloudabi-eabihf-cc".to_string());
Ok(Target {
llvm_target: "armv7-unknown-cloudabi-eabihf".to_string(),

View File

@ -10,7 +10,6 @@
use LinkerFlavor;
use super::{LinkArgs, Target, TargetOptions};
use super::emscripten_base::{cmd};
pub fn target() -> Result<Target, String> {
let mut args = LinkArgs::new();
@ -19,8 +18,6 @@ pub fn target() -> Result<Target, String> {
"ERROR_ON_UNDEFINED_SYMBOLS=1".to_string()]);
let opts = TargetOptions {
linker: cmd("emcc"),
dynamic_linking: false,
executables: true,
exe_suffix: ".js".to_string(),

View File

@ -1,17 +0,0 @@
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub fn cmd(name: &str) -> String {
if cfg!(windows) {
format!("{}.bat", name)
} else {
name.to_string()
}
}

View File

@ -13,7 +13,6 @@ use std::default::Default;
pub fn opts() -> TargetOptions {
TargetOptions {
linker: "cc".to_string(),
dynamic_linking: true,
executables: true,
has_rpath: false,

View File

@ -15,7 +15,7 @@ pub fn target() -> TargetResult {
let mut base = super::cloudabi_base::opts();
base.cpu = "pentium4".to_string();
base.max_atomic_width = Some(64);
base.linker = "i686-unknown-cloudabi-cc".to_string();
base.linker = Some("i686-unknown-cloudabi-cc".to_string());
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
base.stack_probes = true;

View File

@ -73,7 +73,6 @@ pub fn opts() -> Result<TargetOptions, String> {
has_elf_tls: false,
exe_allocation_crate: None,
panic_strategy: PanicStrategy::Abort,
linker: "ld".to_string(),
pre_link_args,
post_link_args,
target_family: Some("unix".to_string()),

View File

@ -58,7 +58,6 @@ mod arm_base;
mod bitrig_base;
mod cloudabi_base;
mod dragonfly_base;
mod emscripten_base;
mod freebsd_base;
mod haiku_base;
mod linux_base;
@ -279,8 +278,8 @@ pub struct TargetOptions {
/// Whether the target is built-in or loaded from a custom target specification.
pub is_builtin: bool,
/// Linker to invoke. Defaults to "cc".
pub linker: String,
/// Linker to invoke
pub linker: Option<String>,
/// Linker arguments that are unconditionally passed *before* any
/// user-defined libraries.
@ -482,7 +481,7 @@ impl Default for TargetOptions {
fn default() -> TargetOptions {
TargetOptions {
is_builtin: false,
linker: option_env!("CFG_DEFAULT_LINKER").unwrap_or("cc").to_string(),
linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.to_string()),
pre_link_args: LinkArgs::new(),
post_link_args: LinkArgs::new(),
asm_args: Vec::new(),
@ -732,7 +731,7 @@ impl Target {
}
key!(is_builtin, bool);
key!(linker);
key!(linker, optional);
key!(pre_link_args, link_args);
key!(pre_link_objects_exe, list);
key!(pre_link_objects_dll, list);

View File

@ -32,7 +32,7 @@ pub fn target() -> TargetResult {
// to gcc to get object files. For this reason we have a hard
// dependency on this specific gcc.
asm_args: vec!["-mcpu=msp430".to_string()],
linker: "msp430-elf-gcc".to_string(),
linker: Some("msp430-elf-gcc".to_string()),
no_integrated_as: true,
// There are no atomic instructions available in the MSP430

View File

@ -45,7 +45,7 @@ pub fn opts() -> TargetOptions {
executables: true,
// In 99%+ of cases, we want to use the `arm-none-eabi-gcc` compiler (there aren't many
// options around)
linker: "arm-none-eabi-gcc".to_string(),
linker: Some("arm-none-eabi-gcc".to_string()),
// Because these devices have very little resources having an unwinder is too onerous so we
// default to "abort" because the "unwind" strategy is very rare.
panic_strategy: PanicStrategy::Abort,

View File

@ -10,7 +10,6 @@
use LinkerFlavor;
use super::{LinkArgs, Target, TargetOptions};
use super::emscripten_base::{cmd};
pub fn target() -> Result<Target, String> {
let mut post_link_args = LinkArgs::new();
@ -24,8 +23,6 @@ pub fn target() -> Result<Target, String> {
"-g3".to_string()]);
let opts = TargetOptions {
linker: cmd("emcc"),
dynamic_linking: false,
executables: true,
// Today emcc emits two files - a .js file to bootstrap and

View File

@ -10,7 +10,6 @@
use LinkerFlavor;
use super::{LinkArgs, Target, TargetOptions};
use super::emscripten_base::{cmd};
pub fn target() -> Result<Target, String> {
let mut post_link_args = LinkArgs::new();
@ -21,8 +20,6 @@ pub fn target() -> Result<Target, String> {
"ERROR_ON_UNDEFINED_SYMBOLS=1".to_string()]);
let opts = TargetOptions {
linker: cmd("emcc"),
dynamic_linking: false,
executables: true,
// Today emcc emits two files - a .js file to bootstrap and

View File

@ -22,8 +22,6 @@ use super::{Target, TargetOptions, PanicStrategy};
pub fn target() -> Result<Target, String> {
let opts = TargetOptions {
linker: "lld".to_string(),
// we allow dynamic linking, but only cdylibs. Basically we allow a
// final library artifact that exports some symbols (a wasm module) but
// we don't allow intermediate `dylib` crate types

View File

@ -75,7 +75,7 @@ pub fn opts() -> TargetOptions {
TargetOptions {
// FIXME(#13846) this should be enabled for windows
function_sections: false,
linker: "gcc".to_string(),
linker: Some("gcc".to_string()),
dynamic_linking: true,
executables: true,
dll_prefix: "".to_string(),

View File

@ -20,7 +20,6 @@ pub fn opts() -> TargetOptions {
TargetOptions {
function_sections: true,
linker: "link.exe".to_string(),
dynamic_linking: true,
executables: true,
dll_prefix: "".to_string(),

View File

@ -15,7 +15,7 @@ pub fn target() -> TargetResult {
let mut base = super::netbsd_base::opts();
base.cpu = "x86-64".to_string();
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string());
base.linker = "x86_64-rumprun-netbsd-gcc".to_string();
base.linker = Some("x86_64-rumprun-netbsd-gcc".to_string());
base.max_atomic_width = Some(64);
base.dynamic_linking = false;

View File

@ -15,7 +15,7 @@ pub fn target() -> TargetResult {
let mut base = super::cloudabi_base::opts();
base.cpu = "x86-64".to_string();
base.max_atomic_width = Some(64);
base.linker = "x86_64-unknown-cloudabi-cc".to_string();
base.linker = Some("x86_64-unknown-cloudabi-cc".to_string());
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string());
base.stack_probes = true;

View File

@ -11,6 +11,7 @@ test = false
[dependencies]
bitflags = "1.0"
cc = "1.0.1"
flate2 = "1.0"
jobserver = "0.1.5"
libc = "0.2"
@ -34,9 +35,6 @@ syntax = { path = "../libsyntax" }
syntax_pos = { path = "../libsyntax_pos" }
tempdir = "0.3"
[target."cfg(windows)".dependencies]
cc = "1.0.1"
[features]
# Used to communicate the feature to `rustc_back` in the same manner that the
# `rustc` driver script communicate this.

View File

@ -81,17 +81,6 @@ impl Command {
self
}
pub fn envs<I, K, V>(&mut self, envs: I) -> &mut Command
where I: IntoIterator<Item=(K, V)>,
K: AsRef<OsStr>,
V: AsRef<OsStr>
{
for (key, value) in envs {
self._env(key.as_ref(), value.as_ref());
}
self
}
fn _env(&mut self, key: &OsStr, value: &OsStr) {
self.env.push((key.to_owned(), value.to_owned()));
}
@ -112,6 +101,9 @@ impl Command {
let mut c = process::Command::new(p);
c.arg("-flavor").arg(match flavor {
LldFlavor::Wasm => "wasm",
LldFlavor::Ld => "gnu",
LldFlavor::Link => "link",
LldFlavor::Ld64 => "darwin",
});
c
}

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use cc::windows_registry;
use super::archive::{ArchiveBuilder, ArchiveConfig};
use super::bytecode::RLIB_BYTECODE_EXTENSION;
use super::linker::Linker;
@ -35,7 +36,6 @@ use llvm;
use std::ascii;
use std::char;
use std::env;
use std::ffi::OsString;
use std::fmt;
use std::fs;
use std::io;
@ -58,9 +58,7 @@ pub use rustc_trans_utils::link::{find_crate_name, filename_for_input, default_o
// The third parameter is for env vars, used on windows to set up the
// path for MSVC to find its DLLs, and gcc to find its bundled
// toolchain
pub fn get_linker(sess: &Session) -> (PathBuf, Command, Vec<(OsString, OsString)>) {
let envs = vec![("PATH".into(), command_path(sess))];
pub fn get_linker(sess: &Session) -> (PathBuf, Command) {
// If our linker looks like a batch script on Windows then to execute this
// we'll need to spawn `cmd` explicitly. This is primarily done to handle
// emscripten where the linker is `emcc.bat` and needs to be spawned as
@ -75,60 +73,57 @@ pub fn get_linker(sess: &Session) -> (PathBuf, Command, Vec<(OsString, OsString)
return Command::bat_script(linker)
}
}
Command::new(linker)
match sess.linker_flavor() {
LinkerFlavor::Lld(f) => Command::lld(linker, f),
_ => Command::new(linker),
}
};
if let Some(ref linker) = sess.opts.cg.linker {
(linker.clone(), cmd(linker), envs)
} else if sess.target.target.options.is_like_msvc {
let (cmd, envs) = msvc_link_exe_cmd(sess);
(PathBuf::from("link.exe"), cmd, envs)
} else if let LinkerFlavor::Lld(f) = sess.linker_flavor() {
let linker = PathBuf::from(&sess.target.target.options.linker);
let cmd = Command::lld(&linker, f);
(linker, cmd, envs)
} else {
let linker = PathBuf::from(&sess.target.target.options.linker);
let cmd = cmd(&linker);
(linker, cmd, envs)
}
}
let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple, "link.exe");
#[cfg(windows)]
pub fn msvc_link_exe_cmd(sess: &Session) -> (Command, Vec<(OsString, OsString)>) {
use cc::windows_registry;
let linker_path = sess.opts.cg.linker.as_ref().map(|s| &**s)
.or(sess.target.target.options.linker.as_ref().map(|s| s.as_ref()))
.unwrap_or(match sess.linker_flavor() {
LinkerFlavor::Msvc => {
msvc_tool.as_ref().map(|t| t.path()).unwrap_or("link.exe".as_ref())
}
LinkerFlavor::Em if cfg!(windows) => "emcc.bat".as_ref(),
LinkerFlavor::Em => "emcc".as_ref(),
LinkerFlavor::Gcc => "cc".as_ref(),
LinkerFlavor::Ld => "ld".as_ref(),
LinkerFlavor::Lld(_) => "lld".as_ref(),
});
let target = &sess.opts.target_triple;
let tool = windows_registry::find_tool(target, "link.exe");
let mut cmd = cmd(linker_path);
if let Some(tool) = tool {
let mut cmd = Command::new(tool.path());
cmd.args(tool.args());
for &(ref k, ref v) in tool.env() {
cmd.env(k, v);
}
let envs = tool.env().to_vec();
(cmd, envs)
} else {
debug!("Failed to locate linker.");
(Command::new("link.exe"), vec![])
}
}
#[cfg(not(windows))]
pub fn msvc_link_exe_cmd(_sess: &Session) -> (Command, Vec<(OsString, OsString)>) {
(Command::new("link.exe"), vec![])
}
fn command_path(sess: &Session) -> OsString {
// The compiler's sysroot often has some bundled tools, so add it to the
// PATH for the child.
let mut new_path = sess.host_filesearch(PathKind::All)
.get_tools_search_paths();
if let Some(path) = env::var_os("PATH") {
new_path.extend(env::split_paths(&path));
let mut msvc_changed_path = false;
if sess.target.target.options.is_like_msvc {
if let Some(ref tool) = msvc_tool {
cmd.args(tool.args());
for &(ref k, ref v) in tool.env() {
if k == "PATH" {
new_path.extend(env::split_paths(v));
msvc_changed_path = true;
} else {
cmd.env(k, v);
}
}
}
}
env::join_paths(new_path).unwrap()
if !msvc_changed_path {
if let Some(path) = env::var_os("PATH") {
new_path.extend(env::split_paths(&path));
}
}
cmd.env("PATH", env::join_paths(new_path).unwrap());
(linker_path.to_path_buf(), cmd)
}
pub fn remove(sess: &Session, path: &Path) {
@ -618,9 +613,7 @@ fn link_natively(sess: &Session,
let flavor = sess.linker_flavor();
// The invocations of cc share some flags across platforms
let (pname, mut cmd, envs) = get_linker(sess);
// This will set PATH on windows
cmd.envs(envs);
let (pname, mut cmd) = get_linker(sess);
let root = sess.target_filesearch(PathKind::Native).get_lib_path();
if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) {

View File

@ -45,6 +45,7 @@ impl LinkerInfo {
cmd: Command,
sess: &'a Session) -> Box<Linker+'a> {
match sess.linker_flavor() {
LinkerFlavor::Lld(LldFlavor::Link) |
LinkerFlavor::Msvc => {
Box::new(MsvcLinker {
cmd,
@ -68,6 +69,9 @@ impl LinkerInfo {
is_ld: false,
}) as Box<Linker>
}
LinkerFlavor::Lld(LldFlavor::Ld) |
LinkerFlavor::Lld(LldFlavor::Ld64) |
LinkerFlavor::Ld => {
Box::new(GccLinker {
cmd,

View File

@ -1369,7 +1369,7 @@ fn start_executing_work(tcx: TyCtxt,
let assembler_cmd = if modules_config.no_integrated_as {
// HACK: currently we use linker (gcc) as our assembler
let (name, mut cmd, _) = get_linker(sess);
let (name, mut cmd) = get_linker(sess);
cmd.args(&sess.target.target.options.asm_args);
Some(Arc::new(AssemblerCommand {
name,

View File

@ -62,7 +62,6 @@ extern crate rustc_trans_utils;
extern crate syntax_pos;
extern crate rustc_errors as errors;
extern crate serialize;
#[cfg(windows)]
extern crate cc; // Used to locate MSVC
extern crate tempdir;