Skip to content
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

Wrapper for -Z gcc-ld=lld to invoke rust-lld with the correct flavor #89288

Merged
merged 1 commit into from
Oct 8, 2021
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
4 changes: 4 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1965,6 +1965,10 @@ dependencies = [
"walkdir",
]

[[package]]
name = "lld-wrapper"
version = "0.1.0"

[[package]]
name = "lock_api"
version = "0.4.1"
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ members = [
"src/tools/jsondocck",
"src/tools/html-checker",
"src/tools/bump-stage0",
"src/tools/lld-wrapper",
]

exclude = [
Expand Down
16 changes: 8 additions & 8 deletions src/bootstrap/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1136,14 +1136,14 @@ 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));
builder.copy(
&lld_install.join("bin").join(&src_exe),
&gcc_ld_dir.join(exe("ld", target_compiler.host)),
);
builder.copy(
&lld_install.join("bin").join(&src_exe),
&gcc_ld_dir.join(exe("ld64", target_compiler.host)),
);
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)));
}
}

// Similarly, copy `llvm-dwp` into libdir for Split DWARF. Only copy it when the LLVM
Expand Down
13 changes: 8 additions & 5 deletions src/bootstrap/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,11 +409,14 @@ impl Step for Rustc {
let rust_lld = exe("rust-lld", compiler.host);
builder.copy(&src_dir.join(&rust_lld), &dst_dir.join(&rust_lld));
// for `-Z gcc-ld=lld`
let gcc_lld_dir = dst_dir.join("gcc-ld");
t!(fs::create_dir(&gcc_lld_dir));
builder.copy(&src_dir.join(&rust_lld), &gcc_lld_dir.join(exe("ld", compiler.host)));
builder
.copy(&src_dir.join(&rust_lld), &gcc_lld_dir.join(exe("ld64", compiler.host)));
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));
}
}

// Copy over llvm-dwp if it's there
Expand Down
32 changes: 32 additions & 0 deletions src/bootstrap/tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,38 @@ impl Step for Cargo {
}
}

#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct LldWrapper {
pub compiler: Compiler,
pub target: TargetSelection,
pub flavor_feature: &'static str,
}

impl Step for LldWrapper {
type Output = PathBuf;

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.never()
}

fn run(self, builder: &Builder<'_>) -> PathBuf {
let src_exe = builder
.ensure(ToolBuild {
compiler: self.compiler,
target: self.target,
tool: "lld-wrapper",
mode: Mode::ToolStd,
path: "src/tools/lld-wrapper",
is_optional_tool: false,
source_type: SourceType::InTree,
extra_features: vec![self.flavor_feature.to_owned()],
})
.expect("expected to build -- essential tool");

src_exe
}
}

macro_rules! tool_extended {
(($sel:ident, $builder:ident),
$($name:ident,
Expand Down
11 changes: 11 additions & 0 deletions src/tools/lld-wrapper/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "lld-wrapper"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"

[dependencies]

[features]
ld = []
ld64 = []
125 changes: 125 additions & 0 deletions src/tools/lld-wrapper/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//! Script to invoke the bundled rust-lld with the correct flavor. The flavor is selected by
//! feature.
//!
//! 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.
//! If no `-flavor` argument is present the flavor is determined by the executable name.
//!
//! 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
//! symblic 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`
//! 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;

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

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

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

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

/// Returns the path to rust-lld in the parent directory.
///
/// Exits if the parent directory cannot be determined.
fn get_rust_lld_path(current_exe_path: &Path) -> PathBuf {
let mut rust_lld_exe_name = "rust-lld".to_owned();
rust_lld_exe_name.push_str(env::consts::EXE_SUFFIX);
let mut rust_lld_path = current_exe_path
.parent()
.unwrap_or_exit_with("directory containing current executable could not be determined")
.parent()
.unwrap_or_exit_with("parent directory could not be determined")
.to_owned();
rust_lld_path.push(rust_lld_exe_name);
rust_lld_path
}

/// Returns the command for invoking rust-lld with the correct flavor.
///
/// 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);
command.arg("-flavor");
command.arg(FLAVOR);
command.args(env::args_os().skip(1));
command
}

#[cfg(unix)]
fn exec_lld(mut command: process::Command) {
use std::os::unix::prelude::CommandExt;
Result::<(), _>::Err(command.exec()).unwrap_or_exit_with("could not exec rust-lld");
unreachable!("lld-wrapper: after exec without error");
}

#[cfg(not(unix))]
fn exec_lld(mut command: process::Command) {
// 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);
}
}
}
}

fn main() {
let current_exe_path =
env::current_exe().unwrap_or_exit_with("could not get the path of the current executable");

exec_lld(get_rust_lld_command(current_exe_path.as_ref()));
}