Skip to content

Simplify implementation of -Z gcc-ld #97375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions compiler/rustc_codegen_ssa/src/back/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,7 @@ impl Command {
}
Program::Lld(ref p, flavor) => {
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.arg("-flavor").arg(flavor.as_str());
if let LldFlavor::Wasm = flavor {
// LLVM expects host-specific formatting for @file
// arguments, but we always generate posix formatted files
Expand Down
45 changes: 14 additions & 31 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2698,37 +2698,20 @@ fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
if let LinkerFlavor::Gcc = flavor {
match ld_impl {
LdImpl::Lld => {
if sess.target.lld_flavor == LldFlavor::Ld64 {
let tools_path = sess.get_tools_search_paths(false);
let ld64_exe = tools_path
.into_iter()
.map(|p| p.join("gcc-ld"))
.map(|p| {
p.join(if sess.host.is_like_windows { "ld64.exe" } else { "ld64" })
})
.find(|p| p.exists())
.unwrap_or_else(|| sess.fatal("rust-lld (as ld64) not found"));
cmd.cmd().arg({
let mut arg = OsString::from("-fuse-ld=");
arg.push(ld64_exe);
arg
});
} else {
let tools_path = sess.get_tools_search_paths(false);
let lld_path = tools_path
.into_iter()
.map(|p| p.join("gcc-ld"))
.find(|p| {
p.join(if sess.host.is_like_windows { "ld.exe" } else { "ld" })
.exists()
})
.unwrap_or_else(|| sess.fatal("rust-lld (as ld) not found"));
cmd.cmd().arg({
let mut arg = OsString::from("-B");
arg.push(lld_path);
arg
});
}
let tools_path = sess.get_tools_search_paths(false);
let gcc_ld_dir = tools_path
.into_iter()
.map(|p| p.join("gcc-ld"))
.find(|p| {
p.join(if sess.host.is_like_windows { "ld.exe" } else { "ld" }).exists()
})
.unwrap_or_else(|| sess.fatal("rust-lld (as ld) not found"));
cmd.arg({
let mut arg = OsString::from("-B");
arg.push(gcc_ld_dir);
arg
});
cmd.arg(format!("-Wl,-rustc-lld-flavor={}", sess.target.lld_flavor.as_str()));
}
}
} else {
Expand Down
17 changes: 10 additions & 7 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ pub enum LldFlavor {
}

impl LldFlavor {
pub fn as_str(&self) -> &'static str {
match self {
LldFlavor::Wasm => "wasm",
LldFlavor::Ld64 => "darwin",
LldFlavor::Ld => "gnu",
LldFlavor::Link => "link",
}
}

fn from_str(s: &str) -> Option<Self> {
Some(match s {
"darwin" => LldFlavor::Ld64,
Expand All @@ -121,13 +130,7 @@ impl LldFlavor {

impl ToJson for LldFlavor {
fn to_json(&self) -> Json {
match *self {
LldFlavor::Ld64 => "darwin",
LldFlavor::Ld => "gnu",
LldFlavor::Link => "link",
LldFlavor::Wasm => "wasm",
}
.to_json()
self.as_str().to_json()
}
}

Expand Down
13 changes: 5 additions & 8 deletions src/bootstrap/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1164,14 +1164,11 @@ impl Step for Assemble {
// for `-Z gcc-ld=lld`
let gcc_ld_dir = libdir_bin.join("gcc-ld");
t!(fs::create_dir(&gcc_ld_dir));
for flavor in ["ld", "ld64"] {
let lld_wrapper_exe = builder.ensure(crate::tool::LldWrapper {
compiler: build_compiler,
target: target_compiler.host,
flavor_feature: flavor,
});
builder.copy(&lld_wrapper_exe, &gcc_ld_dir.join(exe(flavor, target_compiler.host)));
}
let lld_wrapper_exe = builder.ensure(crate::tool::LldWrapper {
compiler: build_compiler,
target: target_compiler.host,
});
builder.copy(&lld_wrapper_exe, &gcc_ld_dir.join(exe("ld", target_compiler.host)));
}

if builder.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) {
Expand Down
7 changes: 2 additions & 5 deletions src/bootstrap/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,11 +407,8 @@ impl Step for Rustc {
let gcc_lld_src_dir = src_dir.join("gcc-ld");
let gcc_lld_dst_dir = dst_dir.join("gcc-ld");
t!(fs::create_dir(&gcc_lld_dst_dir));
for flavor in ["ld", "ld64"] {
let exe_name = exe(flavor, compiler.host);
builder
.copy(&gcc_lld_src_dir.join(&exe_name), &gcc_lld_dst_dir.join(&exe_name));
}
let exe_name = exe("ld", compiler.host);
builder.copy(&gcc_lld_src_dir.join(&exe_name), &gcc_lld_dst_dir.join(&exe_name));
}

// Man pages
Expand Down
3 changes: 1 addition & 2 deletions src/bootstrap/tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,6 @@ impl Step for Cargo {
pub struct LldWrapper {
pub compiler: Compiler,
pub target: TargetSelection,
pub flavor_feature: &'static str,
}

impl Step for LldWrapper {
Expand All @@ -676,7 +675,7 @@ impl Step for LldWrapper {
path: "src/tools/lld-wrapper",
is_optional_tool: false,
source_type: SourceType::InTree,
extra_features: vec![self.flavor_feature.to_owned()],
extra_features: Vec::new(),
})
.expect("expected to build -- essential tool");

Expand Down
6 changes: 0 additions & 6 deletions src/tools/lld-wrapper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,3 @@ name = "lld-wrapper"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"

[dependencies]

[features]
ld = []
ld64 = []
95 changes: 38 additions & 57 deletions src/tools/lld-wrapper/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! Script to invoke the bundled rust-lld with the correct flavor. The flavor is selected by
//! feature.
//! Script to invoke the bundled rust-lld with the correct flavor.
//!
//! lld supports multiple command line interfaces. If `-flavor <flavor>` are passed as the first
//! two arguments the `<flavor>` command line interface is used to process the remaining arguments.
Expand All @@ -8,59 +7,33 @@
//! In Rust with `-Z gcc-ld=lld` we have gcc or clang invoke rust-lld. Since there is no way to
//! make gcc/clang pass `-flavor <flavor>` as the first two arguments in the linker invocation
//! and since Windows does not support symbolic links for files this wrapper is used in place of a
//! symbolic link. It execs `../rust-lld -flavor ld` if the feature `ld` is enabled and
//! `../rust-lld -flavor ld64` if `ld64` is enabled. On Windows it spawns a `..\rust-lld.exe`
//! symbolic link. It execs `../rust-lld -flavor <flavor>` by propagating the flavor argument
//! passed to the wrapper as the first two arguments. On Windows it spawns a `..\rust-lld.exe`
//! child process.

#[cfg(not(any(feature = "ld", feature = "ld64")))]
compile_error!("One of the features ld and ld64 must be enabled.");

#[cfg(all(feature = "ld", feature = "ld64"))]
compile_error!("Only one of the feature ld or ld64 can be enabled.");

#[cfg(feature = "ld")]
const FLAVOR: &str = "ld";

#[cfg(feature = "ld64")]
const FLAVOR: &str = "ld64";

use std::env;
use std::fmt::Display;
use std::path::{Path, PathBuf};
use std::process;
use std::{env, process};

trait ResultExt<T, E> {
trait UnwrapOrExitWith<T> {
fn unwrap_or_exit_with(self, context: &str) -> T;
}

impl<T, E> ResultExt<T, E> for Result<T, E>
where
E: Display,
{
impl<T> UnwrapOrExitWith<T> for Option<T> {
fn unwrap_or_exit_with(self, context: &str) -> T {
match self {
Ok(t) => t,
Err(e) => {
eprintln!("lld-wrapper: {}: {}", context, e);
process::exit(1);
}
}
self.unwrap_or_else(|| {
eprintln!("lld-wrapper: {}", context);
process::exit(1);
})
}
}

trait OptionExt<T> {
fn unwrap_or_exit_with(self, context: &str) -> T;
}

impl<T> OptionExt<T> for Option<T> {
impl<T, E: Display> UnwrapOrExitWith<T> for Result<T, E> {
fn unwrap_or_exit_with(self, context: &str) -> T {
match self {
Some(t) => t,
None => {
eprintln!("lld-wrapper: {}", context);
process::exit(1);
}
}
self.unwrap_or_else(|err| {
eprintln!("lld-wrapper: {}: {}", context, err);
process::exit(1);
})
}
}

Expand All @@ -81,14 +54,28 @@ fn get_rust_lld_path(current_exe_path: &Path) -> PathBuf {
}

/// Returns the command for invoking rust-lld with the correct flavor.
/// LLD only accepts the flavor argument at the first two arguments, so move it there.
///
/// Exits on error.
fn get_rust_lld_command(current_exe_path: &Path) -> process::Command {
let rust_lld_path = get_rust_lld_path(current_exe_path);
let mut command = process::Command::new(rust_lld_path);

let mut flavor = None;
let args = env::args_os()
.skip(1)
.filter(|arg| match arg.to_str().and_then(|s| s.strip_prefix("-rustc-lld-flavor=")) {
Some(suffix) => {
flavor = Some(suffix.to_string());
false
}
None => true,
})
.collect::<Vec<_>>();

command.arg("-flavor");
command.arg(FLAVOR);
command.args(env::args_os().skip(1));
command.arg(flavor.unwrap_or_exit_with("-rustc-lld-flavor=<flavor> is not passed"));
command.args(args);
command
}

Expand All @@ -101,20 +88,14 @@ fn exec_lld(mut command: process::Command) {

#[cfg(not(unix))]
fn exec_lld(mut command: process::Command) {
// Windows has no exec(), spawn a child process and wait for it
// Windows has no exec(), spawn a child process and wait for it.
let exit_status = command.status().unwrap_or_exit_with("error running rust-lld child process");
if !exit_status.success() {
match exit_status.code() {
Some(code) => {
// return the original lld exit code
process::exit(code)
}
None => {
eprintln!("lld-wrapper: rust-lld child process exited with error: {}", exit_status,);
process::exit(1);
}
}
}
let code = exit_status
.code()
.ok_or(exit_status)
.unwrap_or_exit_with("rust-lld child process exited with error");
// Return the original lld exit code.
process::exit(code);
}

fn main() {
Expand Down