@@ -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,25 @@ 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
245+ . into_iter ( )
246+ . skip ( 1 ) // the first arg is program name
247+ . flat_map ( |arg| handle_linker_arg_response_file ( arg) . into_iter ( ) )
248+ . collect ( )
249+ }
250+
251+ // handle Windows linker response file. It's designed to workaround Windows command length limit.
252+ // https://learn.microsoft.com/en-us/cpp/build/reference/at-specify-a-linker-response-file?view=msvc-170
253+ pub fn handle_linker_arg_response_file ( arg : String ) -> Vec < String > {
254+ // Technically, file name can start with @ (e.g. node_modules has folders starting with @).
255+ // But it's very rare in Rust, and it's hard to distinguish from response file so we don't handle them.
256+ if arg. starts_with ( '@' ) {
257+ let path = arg. trim ( ) . trim_start_matches ( '@' ) ;
247258 let file_binary = std:: fs:: read ( path) . unwrap ( ) ;
248259
249260 // 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 ( |_| {
261+ let mut content = String :: from_utf8 ( file_binary. clone ( ) ) . unwrap_or_else ( |_| {
251262 // Convert Vec<u8> to Vec<u16> to convert into a String
252263 let binary_u16le: Vec < u16 > = file_binary
253264 . chunks_exact ( 2 )
@@ -257,15 +268,22 @@ pub fn handle_linker_command_file(args: &mut Vec<String>) {
257268 String :: from_utf16_lossy ( & binary_u16le)
258269 } ) ;
259270
271+ // Remove byte order mark in the beginning
272+ if content. starts_with ( '\u{FEFF}' ) {
273+ content. remove ( 0 ) ;
274+ }
275+
260276 // Gather linker args, and reset the args to be just the linker args
261- * args = content
277+ content
262278 . lines ( )
263279 . map ( |line| {
264280 let line_parsed = line. trim ( ) . to_string ( ) ;
265281 let line_parsed = line_parsed. trim_end_matches ( '"' ) . to_string ( ) ;
266282 let line_parsed = line_parsed. trim_start_matches ( '"' ) . to_string ( ) ;
267283 line_parsed
268284 } )
269- . collect ( ) ;
285+ . collect ( )
286+ } else {
287+ vec ! [ arg]
270288 }
271289}
0 commit comments