From dd840d982b270b8c09ee21361ec71a927855d4d8 Mon Sep 17 00:00:00 2001 From: qouteall Date: Sat, 22 Nov 2025 18:21:58 +0800 Subject: [PATCH] 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. --- packages/cli/src/build/request.rs | 2 +- packages/cli/src/cli/link.rs | 35 ++++++++++++++++++++++--------- packages/cli/src/rustcwrapper.rs | 3 ++- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index aece7f767c..517b36e898 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -2201,7 +2201,7 @@ impl BuildRequest { // 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 - 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 archive_has_contents { match self.linker_flavor() { diff --git a/packages/cli/src/cli/link.rs b/packages/cli/src/cli/link.rs index 005aad3d44..c77890dde3 100644 --- a/packages/cli/src/cli/link.rs +++ b/packages/cli/src/cli/link.rs @@ -127,12 +127,12 @@ impl LinkAction { /// 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. 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() { 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 { args.retain(|arg| !arg.ends_with(".lib")); @@ -149,7 +149,7 @@ impl LinkAction { let mut cmd = std::process::Command::new(linker); match cfg!(target_os = "windows") { 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"); @@ -240,14 +240,22 @@ impl LinkAction { } } -pub fn handle_linker_command_file(args: &mut Vec) { - // Handle command files, usually a windows thing. - if let Some(command) = args.iter().find(|arg| arg.starts_with('@')).cloned() { - let path = command.trim().trim_start_matches('@'); +pub fn get_actual_linker_args_excluding_program_name(args: Vec) -> Vec { + args.into_iter() + .skip(1) // the first arg is program name + .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 { + if arg.starts_with('@') { + let path = arg.trim().trim_start_matches('@'); let file_binary = std::fs::read(path).unwrap(); // 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 to Vec to convert into a String let binary_u16le: Vec = file_binary .chunks_exact(2) @@ -257,8 +265,13 @@ pub fn handle_linker_command_file(args: &mut Vec) { 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 - *args = content + content .lines() .map(|line| { let line_parsed = line.trim().to_string(); @@ -266,6 +279,8 @@ pub fn handle_linker_command_file(args: &mut Vec) { let line_parsed = line_parsed.trim_start_matches('"').to_string(); line_parsed }) - .collect(); + .collect() + } else { + vec![arg] } } diff --git a/packages/cli/src/rustcwrapper.rs b/packages/cli/src/rustcwrapper.rs index 41252481fe..aa30a312b2 100644 --- a/packages/cli/src/rustcwrapper.rs +++ b/packages/cli/src/rustcwrapper.rs @@ -26,7 +26,8 @@ pub fn is_wrapping_rustc() -> bool { pub struct RustcArgs { pub args: Vec, pub envs: Vec<(String, String)>, - pub link_args: Vec, // I don't believe this is used anymore + /// it doesn't include first program name argument + pub link_args: Vec, } /// Check if the arguments indicate a linking step, including those in command files.