Fix the wrong removal of first linker arg in Windows. Linker argument file no longer include first program name argument. Small refactor of handling of linker response file. (#5010)

Co-authored-by: Evan Almloff <evanalmloff@gmail.com>
This commit is contained in:
qouteallandEvan Almloff authored and GitHub committed 2025-12-15 10:14:22 -06:00
1 parent 5929c09cec
commit da72b63b6f
3 files changed
+28 -12

No files matched your search

+1 -1
View File
@@ -2194,7 +2194,7 @@ impl BuildRequest {
// And then remove the rest of the rlibs // And then remove the rest of the rlibs
// //
// We also need to insert the -force_load flag to force the linker to load the archive // We also need to insert the -force_load flag to force the linker to load the archive
let mut args: Vec<_> = rustc_args.link_args.iter().skip(1).cloned().collect(); let mut args: Vec<_> = rustc_args.link_args.clone();
if let Some(last_object) = args.iter().rposition(|arg| arg.ends_with(".o")) { if let Some(last_object) = args.iter().rposition(|arg| arg.ends_with(".o")) {
if archive_has_contents { if archive_has_contents {
match self.linker_flavor() { match self.linker_flavor() {
+25 -10
View File
@@ -127,12 +127,12 @@ impl LinkAction {
/// The file will be given by the dx-magic-link-arg env var itself, so we use /// The file will be given by the dx-magic-link-arg env var itself, so we use
/// it both for determining if we should act as a linker and the for the file name itself. /// it both for determining if we should act as a linker and the for the file name itself.
fn run_link_inner(self) -> Result<()> { fn run_link_inner(self) -> Result<()> {
let mut args: Vec<_> = std::env::args().collect(); let args: Vec<_> = std::env::args().collect();
if args.is_empty() { if args.is_empty() {
return Ok(()); return Ok(());
} }
handle_linker_command_file(&mut args); let mut args = get_actual_linker_args_excluding_program_name(args);
if self.triple.environment == target_lexicon::Environment::Android { if self.triple.environment == target_lexicon::Environment::Android {
args.retain(|arg| !arg.ends_with(".lib")); args.retain(|arg| !arg.ends_with(".lib"));
@@ -149,7 +149,7 @@ impl LinkAction {
let mut cmd = std::process::Command::new(linker); let mut cmd = std::process::Command::new(linker);
match cfg!(target_os = "windows") { match cfg!(target_os = "windows") {
true => cmd.arg(format!("@{}", &self.link_args_file.display())), true => cmd.arg(format!("@{}", &self.link_args_file.display())),
false => cmd.args(args.iter().skip(1)), false => cmd.args(args),
}; };
let res = cmd.output().expect("Failed to run linker"); let res = cmd.output().expect("Failed to run linker");
@@ -240,14 +240,22 @@ impl LinkAction {
} }
} }
pub fn handle_linker_command_file(args: &mut Vec<String>) { pub fn get_actual_linker_args_excluding_program_name(args: Vec<String>) -> Vec<String> {
// Handle command files, usually a windows thing. args.into_iter()
if let Some(command) = args.iter().find(|arg| arg.starts_with('@')).cloned() { .skip(1) // the first arg is program name
let path = command.trim().trim_start_matches('@'); .flat_map(|arg| handle_linker_arg_response_file(arg).into_iter())
.collect()
}
// handle Windows linker response file. It's designed to workaround Windows command length limit.
// https://learn.microsoft.com/en-us/cpp/build/reference/at-specify-a-linker-response-file?view=msvc-170
pub fn handle_linker_arg_response_file(arg: String) -> Vec<String> {
if arg.starts_with('@') {
let path = arg.trim().trim_start_matches('@');
let file_binary = std::fs::read(path).unwrap(); let file_binary = std::fs::read(path).unwrap();
// This may be a utf-16le file. Let's try utf-8 first. // This may be a utf-16le file. Let's try utf-8 first.
let content = String::from_utf8(file_binary.clone()).unwrap_or_else(|_| { let mut content = String::from_utf8(file_binary.clone()).unwrap_or_else(|_| {
// Convert Vec<u8> to Vec<u16> to convert into a String // Convert Vec<u8> to Vec<u16> to convert into a String
let binary_u16le: Vec<u16> = file_binary let binary_u16le: Vec<u16> = file_binary
.chunks_exact(2) .chunks_exact(2)
@@ -257,8 +265,13 @@ pub fn handle_linker_command_file(args: &mut Vec<String>) {
String::from_utf16_lossy(&binary_u16le) String::from_utf16_lossy(&binary_u16le)
}); });
// Remove byte order mark in the beginning
if content.starts_with('\u{FEFF}') {
content.remove(0);
}
// Gather linker args, and reset the args to be just the linker args // Gather linker args, and reset the args to be just the linker args
*args = content content
.lines() .lines()
.map(|line| { .map(|line| {
let line_parsed = line.trim().to_string(); let line_parsed = line.trim().to_string();
@@ -266,6 +279,8 @@ pub fn handle_linker_command_file(args: &mut Vec<String>) {
let line_parsed = line_parsed.trim_start_matches('"').to_string(); let line_parsed = line_parsed.trim_start_matches('"').to_string();
line_parsed line_parsed
}) })
.collect(); .collect()
} else {
vec![arg]
} }
} }
+2 -1
View File
@@ -26,7 +26,8 @@ pub fn is_wrapping_rustc() -> bool {
pub struct RustcArgs { pub struct RustcArgs {
pub args: Vec<String>, pub args: Vec<String>,
pub envs: Vec<(String, String)>, pub envs: Vec<(String, String)>,
pub link_args: Vec<String>, // I don't believe this is used anymore /// it doesn't include first program name argument
pub link_args: Vec<String>,
} }
/// Check if the arguments indicate a linking step, including those in command files. /// Check if the arguments indicate a linking step, including those in command files.