@@ -22,6 +22,7 @@ use rustc_errors::DiagCtxtHandle;
2222use rustc_fs_util:: { TempDirBuilder , fix_windows_verbatim_for_gcc, try_canonicalize} ;
2323use rustc_hir:: attrs:: NativeLibKind ;
2424use rustc_hir:: def_id:: { CrateNum , LOCAL_CRATE } ;
25+ use rustc_lint_defs:: builtin:: LINKER_INFO ;
2526use rustc_macros:: Diagnostic ;
2627use rustc_metadata:: fs:: { METADATA_FILENAME , copy_to_stdout, emit_wrapper_file} ;
2728use rustc_metadata:: {
@@ -60,7 +61,8 @@ use super::rpath::{self, RPathConfig};
6061use super :: { apple, versioned_llvm_target} ;
6162use crate :: base:: needs_allocator_shim_for_linking;
6263use crate :: {
63- CompiledModule , CompiledModules , CrateInfo , NativeLib , errors, looks_like_rust_object_file,
64+ CodegenLintLevels , CompiledModule , CompiledModules , CrateInfo , NativeLib , errors,
65+ looks_like_rust_object_file,
6466} ;
6567
6668pub fn ensure_removed ( dcx : DiagCtxtHandle < ' _ > , path : & Path ) {
@@ -672,6 +674,147 @@ struct LinkerOutput {
672674 inner : String ,
673675}
674676
677+ fn is_msvc_link_exe ( sess : & Session ) -> bool {
678+ let ( linker_path, flavor) = linker_and_flavor ( sess) ;
679+ sess. target . is_like_msvc
680+ && flavor == LinkerFlavor :: Msvc ( Lld :: No )
681+ // Match exactly "link.exe"
682+ && linker_path. to_str ( ) == Some ( "link.exe" )
683+ }
684+
685+ fn is_macos_ld ( sess : & Session ) -> bool {
686+ let ( _, flavor) = linker_and_flavor ( sess) ;
687+ sess. target . is_like_darwin && matches ! ( flavor, LinkerFlavor :: Darwin ( _, Lld :: No ) )
688+ }
689+
690+ fn is_windows_gnu_ld ( sess : & Session ) -> bool {
691+ let ( _, flavor) = linker_and_flavor ( sess) ;
692+ sess. target . is_like_windows
693+ && !sess. target . is_like_msvc
694+ && matches ! ( flavor, LinkerFlavor :: Gnu ( _, Lld :: No ) )
695+ }
696+
697+ fn report_linker_output ( sess : & Session , levels : CodegenLintLevels , stdout : & [ u8 ] , stderr : & [ u8 ] ) {
698+ let mut escaped_stderr = escape_string ( & stderr) ;
699+ let mut escaped_stdout = escape_string ( & stdout) ;
700+ let mut linker_info = String :: new ( ) ;
701+
702+ info ! ( "linker stderr:\n {}" , & escaped_stderr) ;
703+ info ! ( "linker stdout:\n {}" , & escaped_stdout) ;
704+
705+ fn for_each ( bytes : & [ u8 ] , mut f : impl FnMut ( & str , & mut String ) ) -> String {
706+ let mut output = String :: new ( ) ;
707+ if let Ok ( str) = str:: from_utf8 ( bytes) {
708+ info ! ( "line: {str}" ) ;
709+ output = String :: with_capacity ( str. len ( ) ) ;
710+ for line in str. lines ( ) {
711+ f ( line. trim ( ) , & mut output) ;
712+ }
713+ }
714+ escape_string ( output. trim ( ) . as_bytes ( ) )
715+ }
716+
717+ if is_msvc_link_exe ( sess) {
718+ info ! ( "inferred MSVC link.exe" ) ;
719+
720+ escaped_stdout = for_each ( & stdout, |line, output| {
721+ // Hide some progress messages from link.exe that we don't care about.
722+ // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
723+ if line. starts_with ( " Creating library" )
724+ || line. starts_with ( "Generating code" )
725+ || line. starts_with ( "Finished generating code" )
726+ {
727+ linker_info += line;
728+ linker_info += "\r \n " ;
729+ } else {
730+ * output += line;
731+ * output += "\r \n "
732+ }
733+ } ) ;
734+ } else if is_macos_ld ( sess) {
735+ info ! ( "inferred macOS LD" ) ;
736+
737+ // FIXME: Tracked by https://github.com/rust-lang/rust/issues/136113
738+ let deployment_mismatch = |line : & str | {
739+ line. starts_with ( "ld: warning: object file (" )
740+ && line. contains ( "was built for newer 'macOS' version" )
741+ && line. contains ( "than being linked" )
742+ } ;
743+ // FIXME: This is a real warning we would like to show, but it hits too many crates
744+ // to want to turn it on immediately.
745+ let search_path = |line : & str | {
746+ line. starts_with ( "ld: warning: search path '" ) && line. ends_with ( "' not found" )
747+ } ;
748+ escaped_stderr = for_each ( & stderr, |line, output| {
749+ // This duplicate library warning is just not helpful at all.
750+ if line. starts_with ( "ld: warning: ignoring duplicate libraries: " )
751+ || deployment_mismatch ( line)
752+ || search_path ( line)
753+ {
754+ linker_info += line;
755+ linker_info += "\n " ;
756+ } else {
757+ * output += line;
758+ * output += "\n "
759+ }
760+ } ) ;
761+ } else if is_windows_gnu_ld ( sess) {
762+ info ! ( "inferred Windows GNU LD" ) ;
763+
764+ let mut saw_exclude_symbol = false ;
765+ // See https://github.com/rust-lang/rust/issues/112368.
766+ // FIXME: maybe check that binutils is older than 2.40 before downgrading this warning?
767+ let exclude_symbols = |line : & str | {
768+ line. starts_with ( "Warning: .drectve `-exclude-symbols:" )
769+ && line. ends_with ( "' unrecognized" )
770+ } ;
771+ escaped_stderr = for_each ( & stderr, |line, output| {
772+ if exclude_symbols ( line) {
773+ saw_exclude_symbol = true ;
774+ linker_info += line;
775+ linker_info += "\n " ;
776+ } else if saw_exclude_symbol && line == "Warning: corrupt .drectve at end of def file" {
777+ linker_info += line;
778+ linker_info += "\n " ;
779+ } else {
780+ * output += line;
781+ * output += "\n "
782+ }
783+ } ) ;
784+ }
785+
786+ let lint_msg = |msg| {
787+ diag_lint_level (
788+ sess,
789+ LINKER_MESSAGES ,
790+ levels. linker_messages ,
791+ None ,
792+ LinkerOutput { inner : msg } ,
793+ ) ;
794+ } ;
795+ let lint_info = |msg| {
796+ diag_lint_level ( sess, LINKER_INFO , levels. linker_info , None , LinkerOutput { inner : msg } ) ;
797+ } ;
798+
799+ if !escaped_stderr. is_empty ( ) {
800+ // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
801+ escaped_stderr =
802+ escaped_stderr. strip_prefix ( "warning: " ) . unwrap_or ( & escaped_stderr) . to_owned ( ) ;
803+ // Windows GNU LD prints uppercase Warning
804+ escaped_stderr = escaped_stderr
805+ . strip_prefix ( "Warning: " )
806+ . unwrap_or ( & escaped_stderr)
807+ . replace ( ": warning: " , ": " ) ;
808+ lint_msg ( format ! ( "linker stderr: {escaped_stderr}" ) ) ;
809+ }
810+ if !escaped_stdout. is_empty ( ) {
811+ lint_msg ( format ! ( "linker stdout: {}" , escaped_stdout) )
812+ }
813+ if !linker_info. is_empty ( ) {
814+ lint_info ( linker_info) ;
815+ }
816+ }
817+
675818/// Create a dynamic library or executable.
676819///
677820/// This will invoke the system linker/cc to create the resulting file. This links to all upstream
@@ -860,11 +1003,6 @@ fn link_natively(
8601003
8611004 match prog {
8621005 Ok ( prog) => {
863- let is_msvc_link_exe = sess. target . is_like_msvc
864- && flavor == LinkerFlavor :: Msvc ( Lld :: No )
865- // Match exactly "link.exe"
866- && linker_path. to_str ( ) == Some ( "link.exe" ) ;
867-
8681006 if !prog. status . success ( ) {
8691007 let mut output = prog. stderr . clone ( ) ;
8701008 output. extend_from_slice ( & prog. stdout ) ;
@@ -884,7 +1022,7 @@ fn link_natively(
8841022 if let Some ( code) = prog. status . code ( ) {
8851023 // All Microsoft `link.exe` linking ror codes are
8861024 // four digit numbers in the range 1000 to 9999 inclusive
887- if is_msvc_link_exe && ( code < 1000 || code > 9999 ) {
1025+ if is_msvc_link_exe ( sess ) && ( code < 1000 || code > 9999 ) {
8881026 let is_vs_installed = find_msvc_tools:: find_vs_version ( ) . is_ok ( ) ;
8891027 let has_linker =
8901028 find_msvc_tools:: find_tool ( sess. target . arch . desc ( ) , "link.exe" )
@@ -916,46 +1054,8 @@ fn link_natively(
9161054 sess. dcx ( ) . abort_if_errors ( ) ;
9171055 }
9181056
919- let stderr = escape_string ( & prog. stderr ) ;
920- let mut stdout = escape_string ( & prog. stdout ) ;
921- info ! ( "linker stderr:\n {}" , & stderr) ;
922- info ! ( "linker stdout:\n {}" , & stdout) ;
923-
924- // Hide some progress messages from link.exe that we don't care about.
925- // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
926- if is_msvc_link_exe {
927- if let Ok ( str) = str:: from_utf8 ( & prog. stdout ) {
928- let mut output = String :: with_capacity ( str. len ( ) ) ;
929- for line in stdout. lines ( ) {
930- if line. starts_with ( " Creating library" )
931- || line. starts_with ( "Generating code" )
932- || line. starts_with ( "Finished generating code" )
933- {
934- continue ;
935- }
936- output += line;
937- output += "\r \n "
938- }
939- stdout = escape_string ( output. trim ( ) . as_bytes ( ) )
940- }
941- }
942-
943- let level = crate_info. lint_levels . linker_messages ;
944- let lint = |msg| {
945- diag_lint_level ( sess, LINKER_MESSAGES , level, None , LinkerOutput { inner : msg } ) ;
946- } ;
947-
948- if !prog. stderr . is_empty ( ) {
949- // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
950- let stderr = stderr
951- . strip_prefix ( "warning: " )
952- . unwrap_or ( & stderr)
953- . replace ( ": warning: " , ": " ) ;
954- lint ( format ! ( "linker stderr: {stderr}" ) ) ;
955- }
956- if !stdout. is_empty ( ) {
957- lint ( format ! ( "linker stdout: {}" , stdout) )
958- }
1057+ info ! ( "reporting linker output: flavor={flavor:?}" ) ;
1058+ report_linker_output ( sess, crate_info. lint_levels , & prog. stdout , & prog. stderr ) ;
9591059 }
9601060 Err ( e) => {
9611061 let linker_not_found = e. kind ( ) == io:: ErrorKind :: NotFound ;
0 commit comments