@@ -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,24 @@ 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+ // Technically, file name can start with @ (e.g. node_modules has folders starting with @).
254+ // But it's very rare in Rust, and it's hard to distinguish from response file so we don't handle them.
255+ if arg. starts_with ( '@' ) {
256+ let path = arg. trim ( ) . trim_start_matches ( '@' ) ;
247257 let file_binary = std:: fs:: read ( path) . unwrap ( ) ;
248258
249259 // 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 ( |_| {
260+ let mut content = String :: from_utf8 ( file_binary. clone ( ) ) . unwrap_or_else ( |_| {
251261 // Convert Vec<u8> to Vec<u16> to convert into a String
252262 let binary_u16le: Vec < u16 > = file_binary
253263 . chunks_exact ( 2 )
@@ -257,15 +267,22 @@ pub fn handle_linker_command_file(args: &mut Vec<String>) {
257267 String :: from_utf16_lossy ( & binary_u16le)
258268 } ) ;
259269
270+ // Remove byte order mark in the beginning
271+ if content. starts_with ( '\u{FEFF}' ) {
272+ content. remove ( 0 ) ;
273+ }
274+
260275 // Gather linker args, and reset the args to be just the linker args
261- * args = content
276+ content
262277 . lines ( )
263278 . map ( |line| {
264279 let line_parsed = line. trim ( ) . to_string ( ) ;
265280 let line_parsed = line_parsed. trim_end_matches ( '"' ) . to_string ( ) ;
266281 let line_parsed = line_parsed. trim_start_matches ( '"' ) . to_string ( ) ;
267282 line_parsed
268283 } )
269- . collect ( ) ;
284+ . collect ( )
285+ } else {
286+ vec ! [ arg]
270287 }
271288}
0 commit comments