@@ -60,7 +60,7 @@ pub use rustc_codegen_utils::link::{find_crate_name, filename_for_input, default
6060// The third parameter is for env vars, used on windows to set up the
6161// path for MSVC to find its DLLs, and gcc to find its bundled
6262// toolchain
63- pub fn get_linker ( sess : & Session ) -> ( PathBuf , Command ) {
63+ pub fn get_linker ( sess : & Session , linker : & Path , flavor : LinkerFlavor ) -> ( PathBuf , Command ) {
6464 // If our linker looks like a batch script on Windows then to execute this
6565 // we'll need to spawn `cmd` explicitly. This is primarily done to handle
6666 // emscripten where the linker is `emcc.bat` and needs to be spawned as
@@ -69,36 +69,21 @@ pub fn get_linker(sess: &Session) -> (PathBuf, Command) {
6969 // This worked historically but is needed manually since #42436 (regression
7070 // was tagged as #42791) and some more info can be found on #44443 for
7171 // emscripten itself.
72- let cmd = | linker : & Path | {
72+ let mut cmd = ( | | {
7373 if let Some ( linker) = linker. to_str ( ) {
7474 if cfg ! ( windows) && linker. ends_with ( ".bat" ) {
7575 return Command :: bat_script ( linker)
7676 }
7777 }
78- match sess . linker_flavor ( ) {
78+ match flavor {
7979 LinkerFlavor :: Lld ( f) => Command :: lld ( linker, f) ,
8080 _ => Command :: new ( linker) ,
8181
8282 }
83- } ;
83+ } ) ( ) ;
8484
8585 let msvc_tool = windows_registry:: find_tool ( & sess. opts . target_triple . triple ( ) , "link.exe" ) ;
8686
87- let linker_path = sess. opts . cg . linker . as_ref ( ) . map ( |s| & * * s)
88- . or ( sess. target . target . options . linker . as_ref ( ) . map ( |s| s. as_ref ( ) ) )
89- . unwrap_or ( match sess. linker_flavor ( ) {
90- LinkerFlavor :: Msvc => {
91- msvc_tool. as_ref ( ) . map ( |t| t. path ( ) ) . unwrap_or ( "link.exe" . as_ref ( ) )
92- }
93- LinkerFlavor :: Em if cfg ! ( windows) => "emcc.bat" . as_ref ( ) ,
94- LinkerFlavor :: Em => "emcc" . as_ref ( ) ,
95- LinkerFlavor :: Gcc => "cc" . as_ref ( ) ,
96- LinkerFlavor :: Ld => "ld" . as_ref ( ) ,
97- LinkerFlavor :: Lld ( _) => "lld" . as_ref ( ) ,
98- } ) ;
99-
100- let mut cmd = cmd ( linker_path) ;
101-
10287 // The compiler's sysroot often has some bundled tools, so add it to the
10388 // PATH for the child.
10489 let mut new_path = sess. host_filesearch ( PathKind :: All )
@@ -125,7 +110,7 @@ pub fn get_linker(sess: &Session) -> (PathBuf, Command) {
125110 }
126111 cmd. env ( "PATH" , env:: join_paths ( new_path) . unwrap ( ) ) ;
127112
128- ( linker_path . to_path_buf ( ) , cmd)
113+ ( linker . to_path_buf ( ) , cmd)
129114}
130115
131116pub fn remove ( sess : & Session , path : & Path ) {
@@ -615,6 +600,71 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) {
615600 }
616601}
617602
603+ pub fn linker_and_flavor ( sess : & Session ) -> Result < ( PathBuf , LinkerFlavor ) , ( ) > {
604+ fn from < F > (
605+ sess : & Session ,
606+ linker : Option < PathBuf > ,
607+ flavor : Option < LinkerFlavor > ,
608+ otherwise : F ,
609+ ) -> Result < ( PathBuf , LinkerFlavor ) , ( ) >
610+ where
611+ F : FnOnce ( ) -> Result < ( PathBuf , LinkerFlavor ) , ( ) >
612+ {
613+ match ( linker, flavor) {
614+ ( Some ( linker) , Some ( flavor) ) => Ok ( ( linker, flavor) ) ,
615+ // only the linker flavor is known; use the default linker for the selected flavor
616+ ( None , Some ( flavor) ) => Ok ( ( PathBuf :: from ( match flavor {
617+ LinkerFlavor :: Em => "emcc" ,
618+ LinkerFlavor :: Gcc => "gcc" ,
619+ LinkerFlavor :: Ld => "ld" ,
620+ LinkerFlavor :: Msvc => "link.exe" ,
621+ LinkerFlavor :: Lld ( _) => "lld" ,
622+ } ) , flavor) ) ,
623+ // infer the linker flavor from the linker name
624+ ( Some ( linker) , None ) => {
625+ let stem = linker. file_stem ( ) . and_then ( |stem| stem. to_str ( ) ) . ok_or_else ( || {
626+ sess
627+ . struct_err ( & format ! ( "couldn't extract file stem from specified linker" ) )
628+ . emit ( ) ;
629+ } ) ?. to_owned ( ) ;
630+
631+ let flavor = if stem == "emcc" {
632+ LinkerFlavor :: Em
633+ } else if stem == "gcc" || stem. ends_with ( "-gcc" ) {
634+ LinkerFlavor :: Gcc
635+ } else if stem == "ld" || stem == "ld.lld" || stem. ends_with ( "-ld" ) {
636+ LinkerFlavor :: Ld
637+ } else if stem == "link" || stem == "lld-link" {
638+ LinkerFlavor :: Msvc
639+ } else {
640+ sess
641+ . struct_err ( & format ! ( "couldn't infer linker flavor from specified linker" ) )
642+ . emit ( ) ;
643+ return Err ( ( ) ) ;
644+ } ;
645+
646+ Ok ( ( linker, flavor) )
647+ } ,
648+ ( None , None ) => otherwise ( ) ,
649+ }
650+ }
651+
652+ // linker and linker flavor specified via command line have precedence over what the target
653+ // specification specifies
654+ from ( sess, sess. opts . cg . linker . clone ( ) , sess. opts . debugging_opts . linker_flavor , || {
655+ from (
656+ sess,
657+ sess. target . target . options . linker . clone ( ) . map ( PathBuf :: from) ,
658+ Some ( sess. target . target . linker_flavor ) ,
659+ || {
660+ sess
661+ . struct_err ( & format ! ( "no linker or linker flavor information provided" ) )
662+ . emit ( ) ;
663+ Err ( ( ) )
664+ } )
665+ } )
666+ }
667+
618668// Create a dynamic library or executable
619669//
620670// This will invoke the system linker/cc to create the resulting file. This
@@ -625,10 +675,15 @@ fn link_natively(sess: &Session,
625675 codegen_results : & CodegenResults ,
626676 tmpdir : & Path ) {
627677 info ! ( "preparing {:?} to {:?}" , crate_type, out_filename) ;
628- let flavor = sess. linker_flavor ( ) ;
678+ let ( linker, flavor) = if let Ok ( ( linker, flavor) ) = linker_and_flavor ( sess) {
679+ ( linker, flavor)
680+ } else {
681+ sess. abort_if_errors ( ) ;
682+ return ;
683+ } ;
629684
630685 // The invocations of cc share some flags across platforms
631- let ( pname, mut cmd) = get_linker ( sess) ;
686+ let ( pname, mut cmd) = get_linker ( sess, & linker , flavor ) ;
632687
633688 let root = sess. target_filesearch ( PathKind :: Native ) . get_lib_path ( ) ;
634689 if let Some ( args) = sess. target . target . options . pre_link_args . get ( & flavor) {
@@ -669,8 +724,8 @@ fn link_natively(sess: &Session,
669724 }
670725
671726 {
672- let mut linker = codegen_results. linker_info . to_linker ( cmd, & sess) ;
673- link_args ( & mut * linker, sess, crate_type, tmpdir,
727+ let mut linker = codegen_results. linker_info . to_linker ( cmd, & sess, flavor ) ;
728+ link_args ( & mut * linker, flavor , sess, crate_type, tmpdir,
674729 out_filename, codegen_results) ;
675730 cmd = linker. finalize ( ) ;
676731 }
@@ -742,7 +797,7 @@ fn link_natively(sess: &Session,
742797 // linking executables as pie. Different versions of gcc seem to use
743798 // different quotes in the error message so don't check for them.
744799 if sess. target . target . options . linker_is_gnu &&
745- sess . linker_flavor ( ) != LinkerFlavor :: Ld &&
800+ flavor != LinkerFlavor :: Ld &&
746801 ( out. contains ( "unrecognized command line option" ) ||
747802 out. contains ( "unknown argument" ) ) &&
748803 out. contains ( "-no-pie" ) &&
@@ -991,6 +1046,7 @@ fn exec_linker(sess: &Session, cmd: &mut Command, out_filename: &Path, tmpdir: &
9911046}
9921047
9931048fn link_args ( cmd : & mut dyn Linker ,
1049+ flavor : LinkerFlavor ,
9941050 sess : & Session ,
9951051 crate_type : config:: CrateType ,
9961052 tmpdir : & Path ,
@@ -1075,7 +1131,7 @@ fn link_args(cmd: &mut dyn Linker,
10751131 // independent executables by default. We have to pass -no-pie to
10761132 // explicitly turn that off. Not applicable to ld.
10771133 if sess. target . target . options . linker_is_gnu
1078- && sess . linker_flavor ( ) != LinkerFlavor :: Ld {
1134+ && flavor != LinkerFlavor :: Ld {
10791135 cmd. no_position_independent_executable ( ) ;
10801136 }
10811137 }
0 commit comments