|  | 
|  | 1 | +//! Script to invoke the bundled rust-lld with the correct flavor. The flavor is selected by | 
|  | 2 | +//! feature. | 
|  | 3 | +//! | 
|  | 4 | +//! lld supports multiple command line interfaces. If `-flavor <flavor>` are passed as the first | 
|  | 5 | +//! two arguments the `<flavor>` command line interface is used to process the remaining arguments. | 
|  | 6 | +//! If no `-flavor` argument is present the flavor is determined by the executable name. | 
|  | 7 | +//! | 
|  | 8 | +//! In Rust with `-Z gcc-ld=lld` we have gcc or clang invoke rust-lld. Since there is no way to | 
|  | 9 | +//! make gcc/clang pass `-flavor <flavor>` as the first two arguments in the linker invocation | 
|  | 10 | +//! and since Windows does not support symbolic links for files this wrapper is used in place of a | 
|  | 11 | +//! symblic link. It execs `../rust-lld -flavor ld` if the feature `ld` is enabled and | 
|  | 12 | +//! `../rust-lld -flavor ld64` if `ld64` is enabled. On Windows it spawns a `..\rust-lld.exe` | 
|  | 13 | +//! child process. | 
|  | 14 | +
 | 
|  | 15 | +#[cfg(not(any(feature = "ld", feature = "ld64")))] | 
|  | 16 | +compile_error!("One of the features ld and ld64 must be enabled."); | 
|  | 17 | + | 
|  | 18 | +#[cfg(all(feature = "ld", feature = "ld64"))] | 
|  | 19 | +compile_error!("Only one of the feature ld or ld64 can be enabled."); | 
|  | 20 | + | 
|  | 21 | +#[cfg(feature = "ld")] | 
|  | 22 | +const FLAVOR: &str = "ld"; | 
|  | 23 | + | 
|  | 24 | +#[cfg(feature = "ld64")] | 
|  | 25 | +const FLAVOR: &str = "ld64"; | 
|  | 26 | + | 
|  | 27 | +use std::env; | 
|  | 28 | +use std::fmt::Display; | 
|  | 29 | +use std::path::{Path, PathBuf}; | 
|  | 30 | +use std::process; | 
|  | 31 | + | 
|  | 32 | +trait ResultExt<T, E> { | 
|  | 33 | +    fn unwrap_or_exit_with(self, context: &str) -> T; | 
|  | 34 | +} | 
|  | 35 | + | 
|  | 36 | +impl<T, E> ResultExt<T, E> for Result<T, E> | 
|  | 37 | +where | 
|  | 38 | +    E: Display, | 
|  | 39 | +{ | 
|  | 40 | +    fn unwrap_or_exit_with(self, context: &str) -> T { | 
|  | 41 | +        match self { | 
|  | 42 | +            Ok(t) => t, | 
|  | 43 | +            Err(e) => { | 
|  | 44 | +                eprintln!("lld-wrapper: {}: {}", context, e); | 
|  | 45 | +                process::exit(1); | 
|  | 46 | +            } | 
|  | 47 | +        } | 
|  | 48 | +    } | 
|  | 49 | +} | 
|  | 50 | + | 
|  | 51 | +trait OptionExt<T> { | 
|  | 52 | +    fn unwrap_or_exit_with(self, context: &str) -> T; | 
|  | 53 | +} | 
|  | 54 | + | 
|  | 55 | +impl<T> OptionExt<T> for Option<T> { | 
|  | 56 | +    fn unwrap_or_exit_with(self, context: &str) -> T { | 
|  | 57 | +        match self { | 
|  | 58 | +            Some(t) => t, | 
|  | 59 | +            None => { | 
|  | 60 | +                eprintln!("lld-wrapper: {}", context); | 
|  | 61 | +                process::exit(1); | 
|  | 62 | +            } | 
|  | 63 | +        } | 
|  | 64 | +    } | 
|  | 65 | +} | 
|  | 66 | + | 
|  | 67 | +/// Returns the path to rust-lld in the parent directory. | 
|  | 68 | +/// | 
|  | 69 | +/// Exits if the parent directory cannot be determined. | 
|  | 70 | +fn get_rust_lld_path(current_exe_path: &Path) -> PathBuf { | 
|  | 71 | +    let mut rust_lld_exe_name = "rust-lld".to_owned(); | 
|  | 72 | +    rust_lld_exe_name.push_str(env::consts::EXE_SUFFIX); | 
|  | 73 | +    let mut rust_lld_path = current_exe_path | 
|  | 74 | +        .parent() | 
|  | 75 | +        .unwrap_or_exit_with("directory containing current executable could not be determined") | 
|  | 76 | +        .parent() | 
|  | 77 | +        .unwrap_or_exit_with("parent directory could not be determined") | 
|  | 78 | +        .to_owned(); | 
|  | 79 | +    rust_lld_path.push(rust_lld_exe_name); | 
|  | 80 | +    rust_lld_path | 
|  | 81 | +} | 
|  | 82 | + | 
|  | 83 | +/// Returns the command for invoking rust-lld with the correct flavor. | 
|  | 84 | +/// | 
|  | 85 | +/// Exits on error. | 
|  | 86 | +fn get_rust_lld_command(current_exe_path: &Path) -> process::Command { | 
|  | 87 | +    let rust_lld_path = get_rust_lld_path(current_exe_path); | 
|  | 88 | +    let mut command = process::Command::new(rust_lld_path); | 
|  | 89 | +    command.arg("-flavor"); | 
|  | 90 | +    command.arg(FLAVOR); | 
|  | 91 | +    command.args(env::args_os().skip(1)); | 
|  | 92 | +    command | 
|  | 93 | +} | 
|  | 94 | + | 
|  | 95 | +#[cfg(unix)] | 
|  | 96 | +fn exec_lld(mut command: process::Command) { | 
|  | 97 | +    use std::os::unix::prelude::CommandExt; | 
|  | 98 | +    Result::<(), _>::Err(command.exec()).unwrap_or_exit_with("could not exec rust-lld"); | 
|  | 99 | +    unreachable!("lld-wrapper: after exec without error"); | 
|  | 100 | +} | 
|  | 101 | + | 
|  | 102 | +#[cfg(not(unix))] | 
|  | 103 | +fn exec_lld(mut command: process::Command) { | 
|  | 104 | +    // Windows has no exec(), spawn a child process and wait for it | 
|  | 105 | +    let exit_status = command.status().unwrap_or_exit_with("error running rust-lld child process"); | 
|  | 106 | +    if !exit_status.success() { | 
|  | 107 | +        match exit_status.code() { | 
|  | 108 | +            Some(code) => { | 
|  | 109 | +                // return the original lld exit code | 
|  | 110 | +                process::exit(code) | 
|  | 111 | +            } | 
|  | 112 | +            None => { | 
|  | 113 | +                eprintln!("lld-wrapper: rust-lld child process exited with error: {}", exit_status,); | 
|  | 114 | +                process::exit(1); | 
|  | 115 | +            } | 
|  | 116 | +        } | 
|  | 117 | +    } | 
|  | 118 | +} | 
|  | 119 | + | 
|  | 120 | +fn main() { | 
|  | 121 | +    let current_exe_path = | 
|  | 122 | +        env::current_exe().unwrap_or_exit_with("could not get the path of the current executable"); | 
|  | 123 | + | 
|  | 124 | +    exec_lld(get_rust_lld_command(current_exe_path.as_ref())); | 
|  | 125 | +} | 
0 commit comments