Skip to content

Commit da72b63

Browse files
qouteallealmloff
andauthored
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 <[email protected]>
1 parent 5929c09 commit da72b63

File tree

3 files changed

+28
-12
lines changed

3 files changed

+28
-12
lines changed

packages/cli/src/build/request.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2194,7 +2194,7 @@ impl BuildRequest {
21942194
// And then remove the rest of the rlibs
21952195
//
21962196
// We also need to insert the -force_load flag to force the linker to load the archive
2197-
let mut args: Vec<_> = rustc_args.link_args.iter().skip(1).cloned().collect();
2197+
let mut args: Vec<_> = rustc_args.link_args.clone();
21982198
if let Some(last_object) = args.iter().rposition(|arg| arg.ends_with(".o")) {
21992199
if archive_has_contents {
22002200
match self.linker_flavor() {

packages/cli/src/cli/link.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,12 @@ impl LinkAction {
127127
/// The file will be given by the dx-magic-link-arg env var itself, so we use
128128
/// it both for determining if we should act as a linker and the for the file name itself.
129129
fn run_link_inner(self) -> Result<()> {
130-
let mut args: Vec<_> = std::env::args().collect();
130+
let args: Vec<_> = std::env::args().collect();
131131
if args.is_empty() {
132132
return Ok(());
133133
}
134134

135-
handle_linker_command_file(&mut args);
135+
let mut args = get_actual_linker_args_excluding_program_name(args);
136136

137137
if self.triple.environment == target_lexicon::Environment::Android {
138138
args.retain(|arg| !arg.ends_with(".lib"));
@@ -149,7 +149,7 @@ impl LinkAction {
149149
let mut cmd = std::process::Command::new(linker);
150150
match cfg!(target_os = "windows") {
151151
true => cmd.arg(format!("@{}", &self.link_args_file.display())),
152-
false => cmd.args(args.iter().skip(1)),
152+
false => cmd.args(args),
153153
};
154154
let res = cmd.output().expect("Failed to run linker");
155155

@@ -240,14 +240,22 @@ impl LinkAction {
240240
}
241241
}
242242

243-
pub fn handle_linker_command_file(args: &mut Vec<String>) {
244-
// Handle command files, usually a windows thing.
245-
if let Some(command) = args.iter().find(|arg| arg.starts_with('@')).cloned() {
246-
let path = command.trim().trim_start_matches('@');
243+
pub fn get_actual_linker_args_excluding_program_name(args: Vec<String>) -> Vec<String> {
244+
args.into_iter()
245+
.skip(1) // the first arg is program name
246+
.flat_map(|arg| handle_linker_arg_response_file(arg).into_iter())
247+
.collect()
248+
}
249+
250+
// handle Windows linker response file. It's designed to workaround Windows command length limit.
251+
// https://learn.microsoft.com/en-us/cpp/build/reference/at-specify-a-linker-response-file?view=msvc-170
252+
pub fn handle_linker_arg_response_file(arg: String) -> Vec<String> {
253+
if arg.starts_with('@') {
254+
let path = arg.trim().trim_start_matches('@');
247255
let file_binary = std::fs::read(path).unwrap();
248256

249257
// This may be a utf-16le file. Let's try utf-8 first.
250-
let content = String::from_utf8(file_binary.clone()).unwrap_or_else(|_| {
258+
let mut content = String::from_utf8(file_binary.clone()).unwrap_or_else(|_| {
251259
// Convert Vec<u8> to Vec<u16> to convert into a String
252260
let binary_u16le: Vec<u16> = file_binary
253261
.chunks_exact(2)
@@ -257,15 +265,22 @@ pub fn handle_linker_command_file(args: &mut Vec<String>) {
257265
String::from_utf16_lossy(&binary_u16le)
258266
});
259267

268+
// Remove byte order mark in the beginning
269+
if content.starts_with('\u{FEFF}') {
270+
content.remove(0);
271+
}
272+
260273
// Gather linker args, and reset the args to be just the linker args
261-
*args = content
274+
content
262275
.lines()
263276
.map(|line| {
264277
let line_parsed = line.trim().to_string();
265278
let line_parsed = line_parsed.trim_end_matches('"').to_string();
266279
let line_parsed = line_parsed.trim_start_matches('"').to_string();
267280
line_parsed
268281
})
269-
.collect();
282+
.collect()
283+
} else {
284+
vec![arg]
270285
}
271286
}

packages/cli/src/rustcwrapper.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ pub fn is_wrapping_rustc() -> bool {
2626
pub struct RustcArgs {
2727
pub args: Vec<String>,
2828
pub envs: Vec<(String, String)>,
29-
pub link_args: Vec<String>, // I don't believe this is used anymore
29+
/// it doesn't include first program name argument
30+
pub link_args: Vec<String>,
3031
}
3132

3233
/// Check if the arguments indicate a linking step, including those in command files.

0 commit comments

Comments
 (0)