@@ -205,15 +205,11 @@ impl ToJson for LldFlavor {
205
205
}
206
206
207
207
impl LinkerFlavor {
208
- pub fn from_cli ( cli : LinkerFlavorCli , target : & TargetOptions ) -> LinkerFlavor {
209
- Self :: from_cli_impl ( cli, target. linker_flavor . lld_flavor ( ) , target. linker_flavor . is_gnu ( ) )
210
- }
211
-
212
- /// The passed CLI flavor is preferred over other args coming from the default target spec,
213
- /// so this function can produce a flavor that is incompatible with the current target.
214
- /// FIXME: Produce errors when `-Clinker-flavor` is set to something incompatible
215
- /// with the current target.
216
- fn from_cli_impl ( cli : LinkerFlavorCli , lld_flavor : LldFlavor , is_gnu : bool ) -> LinkerFlavor {
208
+ /// At this point the target's reference linker flavor doesn't yet exist and we need to infer
209
+ /// it. The inference always succeds and gives some result, and we don't report any flavor
210
+ /// incompatibility errors for json target specs. The CLI flavor is used as the main source
211
+ /// of truth, other flags are used in case of ambiguities.
212
+ fn from_cli_json ( cli : LinkerFlavorCli , lld_flavor : LldFlavor , is_gnu : bool ) -> LinkerFlavor {
217
213
match cli {
218
214
LinkerFlavorCli :: Gcc => match lld_flavor {
219
215
LldFlavor :: Ld if is_gnu => LinkerFlavor :: Gnu ( Cc :: Yes , Lld :: No ) ,
@@ -257,6 +253,85 @@ impl LinkerFlavor {
257
253
}
258
254
}
259
255
256
+ fn infer_cli_hints ( cli : LinkerFlavorCli ) -> ( Option < Cc > , Option < Lld > ) {
257
+ match cli {
258
+ LinkerFlavorCli :: Gcc | LinkerFlavorCli :: Em => ( Some ( Cc :: Yes ) , None ) ,
259
+ LinkerFlavorCli :: Lld ( _) => ( Some ( Cc :: No ) , Some ( Lld :: Yes ) ) ,
260
+ LinkerFlavorCli :: Ld | LinkerFlavorCli :: Msvc => ( Some ( Cc :: No ) , Some ( Lld :: No ) ) ,
261
+ LinkerFlavorCli :: BpfLinker | LinkerFlavorCli :: PtxLinker => ( None , None ) ,
262
+ }
263
+ }
264
+
265
+ fn infer_linker_hints ( linker_stem : & str ) -> ( Option < Cc > , Option < Lld > ) {
266
+ // Remove any version postfix.
267
+ let stem = linker_stem
268
+ . rsplit_once ( '-' )
269
+ . and_then ( |( lhs, rhs) | rhs. chars ( ) . all ( char:: is_numeric) . then_some ( lhs) )
270
+ . unwrap_or ( linker_stem) ;
271
+
272
+ // GCC/Clang can have an optional target prefix.
273
+ if stem == "emcc"
274
+ || stem == "gcc"
275
+ || stem. ends_with ( "-gcc" )
276
+ || stem == "g++"
277
+ || stem. ends_with ( "-g++" )
278
+ || stem == "clang"
279
+ || stem. ends_with ( "-clang" )
280
+ || stem == "clang++"
281
+ || stem. ends_with ( "-clang++" )
282
+ {
283
+ ( Some ( Cc :: Yes ) , None )
284
+ } else if stem == "wasm-ld"
285
+ || stem. ends_with ( "-wasm-ld" )
286
+ || stem == "ld.lld"
287
+ || stem == "lld"
288
+ || stem == "rust-lld"
289
+ || stem == "lld-link"
290
+ {
291
+ ( Some ( Cc :: No ) , Some ( Lld :: Yes ) )
292
+ } else if stem == "ld" || stem. ends_with ( "-ld" ) || stem == "link" {
293
+ ( Some ( Cc :: No ) , Some ( Lld :: No ) )
294
+ } else {
295
+ ( None , None )
296
+ }
297
+ }
298
+
299
+ fn with_hints ( self , ( cc_hint, lld_hint) : ( Option < Cc > , Option < Lld > ) ) -> LinkerFlavor {
300
+ match self {
301
+ LinkerFlavor :: Gnu ( cc, lld) => {
302
+ LinkerFlavor :: Gnu ( cc_hint. unwrap_or ( cc) , lld_hint. unwrap_or ( lld) )
303
+ }
304
+ LinkerFlavor :: Darwin ( cc, lld) => {
305
+ LinkerFlavor :: Darwin ( cc_hint. unwrap_or ( cc) , lld_hint. unwrap_or ( lld) )
306
+ }
307
+ LinkerFlavor :: WasmLld ( cc) => LinkerFlavor :: WasmLld ( cc_hint. unwrap_or ( cc) ) ,
308
+ LinkerFlavor :: Unix ( cc) => LinkerFlavor :: Unix ( cc_hint. unwrap_or ( cc) ) ,
309
+ LinkerFlavor :: Msvc ( lld) => LinkerFlavor :: Msvc ( lld_hint. unwrap_or ( lld) ) ,
310
+ LinkerFlavor :: EmCc | LinkerFlavor :: Bpf | LinkerFlavor :: Ptx => self ,
311
+ }
312
+ }
313
+
314
+ pub fn with_cli_hints ( self , cli : LinkerFlavorCli ) -> LinkerFlavor {
315
+ self . with_hints ( LinkerFlavor :: infer_cli_hints ( cli) )
316
+ }
317
+
318
+ pub fn with_linker_hints ( self , linker_stem : & str ) -> LinkerFlavor {
319
+ self . with_hints ( LinkerFlavor :: infer_linker_hints ( linker_stem) )
320
+ }
321
+
322
+ pub fn check_compatibility ( self , cli : LinkerFlavorCli ) -> Option < String > {
323
+ // The CLI flavor should be compatible with the target if it survives this roundtrip.
324
+ let compatible = |cli| cli == self . with_cli_hints ( cli) . to_cli ( ) ;
325
+ ( !compatible ( cli) ) . then ( || {
326
+ LinkerFlavorCli :: all ( )
327
+ . iter ( )
328
+ . filter ( |cli| compatible ( * * cli) )
329
+ . map ( |cli| cli. desc ( ) )
330
+ . intersperse ( ", " )
331
+ . collect ( )
332
+ } )
333
+ }
334
+
260
335
pub fn lld_flavor ( self ) -> LldFlavor {
261
336
match self {
262
337
LinkerFlavor :: Gnu ( ..)
@@ -278,6 +353,10 @@ impl LinkerFlavor {
278
353
macro_rules! linker_flavor_cli_impls {
279
354
( $( ( $( $flavor: tt) * ) $string: literal) * ) => (
280
355
impl LinkerFlavorCli {
356
+ const fn all( ) -> & ' static [ LinkerFlavorCli ] {
357
+ & [ $( $( $flavor) * , ) * ]
358
+ }
359
+
281
360
pub const fn one_of( ) -> & ' static str {
282
361
concat!( "one of: " , $( $string, " " , ) * )
283
362
}
@@ -289,8 +368,8 @@ macro_rules! linker_flavor_cli_impls {
289
368
} )
290
369
}
291
370
292
- pub fn desc( & self ) -> & str {
293
- match * self {
371
+ pub fn desc( self ) -> & ' static str {
372
+ match self {
294
373
$( $( $flavor) * => $string, ) *
295
374
}
296
375
}
@@ -1801,7 +1880,7 @@ impl TargetOptions {
1801
1880
}
1802
1881
1803
1882
fn update_from_cli ( & mut self ) {
1804
- self . linker_flavor = LinkerFlavor :: from_cli_impl (
1883
+ self . linker_flavor = LinkerFlavor :: from_cli_json (
1805
1884
self . linker_flavor_json ,
1806
1885
self . lld_flavor_json ,
1807
1886
self . linker_is_gnu_json ,
@@ -1815,12 +1894,7 @@ impl TargetOptions {
1815
1894
] {
1816
1895
args. clear ( ) ;
1817
1896
for ( flavor, args_json) in args_json {
1818
- // Cannot use `from_cli` due to borrow checker.
1819
- let linker_flavor = LinkerFlavor :: from_cli_impl (
1820
- * flavor,
1821
- self . lld_flavor_json ,
1822
- self . linker_is_gnu_json ,
1823
- ) ;
1897
+ let linker_flavor = self . linker_flavor . with_cli_hints ( * flavor) ;
1824
1898
// Normalize to no lld to avoid asserts.
1825
1899
let linker_flavor = match linker_flavor {
1826
1900
LinkerFlavor :: Gnu ( cc, _) => LinkerFlavor :: Gnu ( cc, Lld :: No ) ,
0 commit comments