@@ -53,7 +53,9 @@ pub use rustc_codegen_utils::link::{find_crate_name, filename_for_input, default
5353// The third parameter is for env vars, used on windows to set up the
5454// path for MSVC to find its DLLs, and gcc to find its bundled
5555// toolchain
56- pub fn get_linker ( sess : & Session ) -> ( PathBuf , Command ) {
56+ pub fn get_linker ( sess : & Session , linker : & Path , flavor : LinkerFlavor ) -> ( PathBuf , Command ) {
57+ let msvc_tool = windows_registry:: find_tool ( & sess. opts . target_triple . triple ( ) , "link.exe" ) ;
58+
5759 // If our linker looks like a batch script on Windows then to execute this
5860 // we'll need to spawn `cmd` explicitly. This is primarily done to handle
5961 // emscripten where the linker is `emcc.bat` and needs to be spawned as
@@ -62,36 +64,19 @@ pub fn get_linker(sess: &Session) -> (PathBuf, Command) {
6264 // This worked historically but is needed manually since #42436 (regression
6365 // was tagged as #42791) and some more info can be found on #44443 for
6466 // emscripten itself.
65- let cmd = |linker : & Path | {
66- if let Some ( linker) = linker. to_str ( ) {
67- if cfg ! ( windows) && linker. ends_with ( ".bat" ) {
68- return Command :: bat_script ( linker)
69- }
70- }
71- match sess. linker_flavor ( ) {
67+ let mut cmd = match linker. to_str ( ) {
68+ Some ( linker) if cfg ! ( windows) && linker. ends_with ( ".bat" ) => Command :: bat_script ( linker) ,
69+ _ => match flavor {
7270 LinkerFlavor :: Lld ( f) => Command :: lld ( linker, f) ,
71+ LinkerFlavor :: Msvc
72+ if sess. opts . cg . linker . is_none ( ) && sess. target . target . options . linker . is_none ( ) =>
73+ {
74+ Command :: new ( msvc_tool. as_ref ( ) . map ( |t| t. path ( ) ) . unwrap_or ( linker) )
75+ } ,
7376 _ => Command :: new ( linker) ,
74-
7577 }
7678 } ;
7779
78- let msvc_tool = windows_registry:: find_tool ( & sess. opts . target_triple . triple ( ) , "link.exe" ) ;
79-
80- let linker_path = sess. opts . cg . linker . as_ref ( ) . map ( |s| & * * s)
81- . or ( sess. target . target . options . linker . as_ref ( ) . map ( |s| s. as_ref ( ) ) )
82- . unwrap_or ( match sess. linker_flavor ( ) {
83- LinkerFlavor :: Msvc => {
84- msvc_tool. as_ref ( ) . map ( |t| t. path ( ) ) . unwrap_or ( "link.exe" . as_ref ( ) )
85- }
86- LinkerFlavor :: Em if cfg ! ( windows) => "emcc.bat" . as_ref ( ) ,
87- LinkerFlavor :: Em => "emcc" . as_ref ( ) ,
88- LinkerFlavor :: Gcc => "cc" . as_ref ( ) ,
89- LinkerFlavor :: Ld => "ld" . as_ref ( ) ,
90- LinkerFlavor :: Lld ( _) => "lld" . as_ref ( ) ,
91- } ) ;
92-
93- let mut cmd = cmd ( linker_path) ;
94-
9580 // The compiler's sysroot often has some bundled tools, so add it to the
9681 // PATH for the child.
9782 let mut new_path = sess. host_filesearch ( PathKind :: All )
@@ -118,7 +103,7 @@ pub fn get_linker(sess: &Session) -> (PathBuf, Command) {
118103 }
119104 cmd. env ( "PATH" , env:: join_paths ( new_path) . unwrap ( ) ) ;
120105
121- ( linker_path . to_path_buf ( ) , cmd)
106+ ( linker . to_path_buf ( ) , cmd)
122107}
123108
124109pub fn remove ( sess : & Session , path : & Path ) {
@@ -608,6 +593,69 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) {
608593 }
609594}
610595
596+ pub fn linker_and_flavor ( sess : & Session ) -> ( PathBuf , LinkerFlavor ) {
597+ fn infer_from (
598+ sess : & Session ,
599+ linker : Option < PathBuf > ,
600+ flavor : Option < LinkerFlavor > ,
601+ ) -> Option < ( PathBuf , LinkerFlavor ) > {
602+ match ( linker, flavor) {
603+ ( Some ( linker) , Some ( flavor) ) => Some ( ( linker, flavor) ) ,
604+ // only the linker flavor is known; use the default linker for the selected flavor
605+ ( None , Some ( flavor) ) => Some ( ( PathBuf :: from ( match flavor {
606+ LinkerFlavor :: Em => if cfg ! ( windows) { "emcc.bat" } else { "emcc" } ,
607+ LinkerFlavor :: Gcc => "cc" ,
608+ LinkerFlavor :: Ld => "ld" ,
609+ LinkerFlavor :: Msvc => "link.exe" ,
610+ LinkerFlavor :: Lld ( _) => "lld" ,
611+ } ) , flavor) ) ,
612+ ( Some ( linker) , None ) => {
613+ let stem = linker. file_stem ( ) . and_then ( |stem| stem. to_str ( ) ) . unwrap_or_else ( || {
614+ sess. fatal ( "couldn't extract file stem from specified linker" ) ;
615+ } ) . to_owned ( ) ;
616+
617+ let flavor = if stem == "emcc" {
618+ LinkerFlavor :: Em
619+ } else if stem == "gcc" || stem. ends_with ( "-gcc" ) {
620+ LinkerFlavor :: Gcc
621+ } else if stem == "ld" || stem == "ld.lld" || stem. ends_with ( "-ld" ) {
622+ LinkerFlavor :: Ld
623+ } else if stem == "link" || stem == "lld-link" {
624+ LinkerFlavor :: Msvc
625+ } else if stem == "lld" || stem == "rust-lld" {
626+ LinkerFlavor :: Lld ( sess. target . target . options . lld_flavor )
627+ } else {
628+ // fall back to the value in the target spec
629+ sess. target . target . linker_flavor
630+ } ;
631+
632+ Some ( ( linker, flavor) )
633+ } ,
634+ ( None , None ) => None ,
635+ }
636+ }
637+
638+ // linker and linker flavor specified via command line have precedence over what the target
639+ // specification specifies
640+ if let Some ( ret) = infer_from (
641+ sess,
642+ sess. opts . cg . linker . clone ( ) ,
643+ sess. opts . debugging_opts . linker_flavor ,
644+ ) {
645+ return ret;
646+ }
647+
648+ if let Some ( ret) = infer_from (
649+ sess,
650+ sess. target . target . options . linker . clone ( ) . map ( PathBuf :: from) ,
651+ Some ( sess. target . target . linker_flavor ) ,
652+ ) {
653+ return ret;
654+ }
655+
656+ bug ! ( "Not enough information provided to determine how to invoke the linker" ) ;
657+ }
658+
611659// Create a dynamic library or executable
612660//
613661// This will invoke the system linker/cc to create the resulting file. This
@@ -618,10 +666,10 @@ fn link_natively(sess: &Session,
618666 codegen_results : & CodegenResults ,
619667 tmpdir : & Path ) {
620668 info ! ( "preparing {:?} to {:?}" , crate_type, out_filename) ;
621- let flavor = sess . linker_flavor ( ) ;
669+ let ( linker , flavor) = linker_and_flavor ( sess ) ;
622670
623671 // The invocations of cc share some flags across platforms
624- let ( pname, mut cmd) = get_linker ( sess) ;
672+ let ( pname, mut cmd) = get_linker ( sess, & linker , flavor ) ;
625673
626674 let root = sess. target_filesearch ( PathKind :: Native ) . get_lib_path ( ) ;
627675 if let Some ( args) = sess. target . target . options . pre_link_args . get ( & flavor) {
@@ -662,8 +710,8 @@ fn link_natively(sess: &Session,
662710 }
663711
664712 {
665- let mut linker = codegen_results. linker_info . to_linker ( cmd, & sess) ;
666- link_args ( & mut * linker, sess, crate_type, tmpdir,
713+ let mut linker = codegen_results. linker_info . to_linker ( cmd, & sess, flavor ) ;
714+ link_args ( & mut * linker, flavor , sess, crate_type, tmpdir,
667715 out_filename, codegen_results) ;
668716 cmd = linker. finalize ( ) ;
669717 }
@@ -735,7 +783,7 @@ fn link_natively(sess: &Session,
735783 // linking executables as pie. Different versions of gcc seem to use
736784 // different quotes in the error message so don't check for them.
737785 if sess. target . target . options . linker_is_gnu &&
738- sess . linker_flavor ( ) != LinkerFlavor :: Ld &&
786+ flavor != LinkerFlavor :: Ld &&
739787 ( out. contains ( "unrecognized command line option" ) ||
740788 out. contains ( "unknown argument" ) ) &&
741789 out. contains ( "-no-pie" ) &&
@@ -984,6 +1032,7 @@ fn exec_linker(sess: &Session, cmd: &mut Command, out_filename: &Path, tmpdir: &
9841032}
9851033
9861034fn link_args ( cmd : & mut dyn Linker ,
1035+ flavor : LinkerFlavor ,
9871036 sess : & Session ,
9881037 crate_type : config:: CrateType ,
9891038 tmpdir : & Path ,
@@ -1068,7 +1117,7 @@ fn link_args(cmd: &mut dyn Linker,
10681117 // independent executables by default. We have to pass -no-pie to
10691118 // explicitly turn that off. Not applicable to ld.
10701119 if sess. target . target . options . linker_is_gnu
1071- && sess . linker_flavor ( ) != LinkerFlavor :: Ld {
1120+ && flavor != LinkerFlavor :: Ld {
10721121 cmd. no_position_independent_executable ( ) ;
10731122 }
10741123 }
0 commit comments