@@ -21,6 +21,7 @@ use rustc_errors::{DiagCtxtHandle, LintDiagnostic};
2121use rustc_fs_util:: { TempDirBuilder , fix_windows_verbatim_for_gcc, try_canonicalize} ;
2222use rustc_hir:: attrs:: NativeLibKind ;
2323use rustc_hir:: def_id:: { CrateNum , LOCAL_CRATE } ;
24+ use rustc_lint_defs:: builtin:: LINKER_INFO ;
2425use rustc_macros:: LintDiagnostic ;
2526use rustc_metadata:: fs:: { METADATA_FILENAME , copy_to_stdout, emit_wrapper_file} ;
2627use rustc_metadata:: {
@@ -59,7 +60,8 @@ use super::rpath::{self, RPathConfig};
5960use super :: { apple, versioned_llvm_target} ;
6061use crate :: base:: needs_allocator_shim_for_linking;
6162use crate :: {
62- CodegenResults , CompiledModule , CrateInfo , NativeLib , errors, looks_like_rust_object_file,
63+ CodegenLintLevels , CodegenResults , CompiledModule , CrateInfo , NativeLib , errors,
64+ looks_like_rust_object_file,
6365} ;
6466
6567pub fn ensure_removed ( dcx : DiagCtxtHandle < ' _ > , path : & Path ) {
@@ -669,6 +671,133 @@ struct LinkerOutput {
669671 inner : String ,
670672}
671673
674+ fn is_msvc_link_exe ( sess : & Session ) -> bool {
675+ let ( linker_path, flavor) = linker_and_flavor ( sess) ;
676+ sess. target . is_like_msvc
677+ && flavor == LinkerFlavor :: Msvc ( Lld :: No )
678+ // Match exactly "link.exe"
679+ && linker_path. to_str ( ) == Some ( "link.exe" )
680+ }
681+
682+ fn is_macos_ld ( sess : & Session ) -> bool {
683+ let ( _, flavor) = linker_and_flavor ( sess) ;
684+ sess. target . is_like_darwin && matches ! ( flavor, LinkerFlavor :: Darwin ( _, Lld :: No ) )
685+ }
686+
687+ fn is_windows_gnu_ld ( sess : & Session ) -> bool {
688+ let ( _, flavor) = linker_and_flavor ( sess) ;
689+ sess. target . is_like_windows
690+ && !sess. target . is_like_msvc
691+ && matches ! ( flavor, LinkerFlavor :: Gnu ( _, Lld :: No ) )
692+ }
693+
694+ fn report_linker_output ( sess : & Session , levels : CodegenLintLevels , stdout : & [ u8 ] , stderr : & [ u8 ] ) {
695+ let mut escaped_stderr = escape_string ( & stderr) ;
696+ let mut escaped_stdout = escape_string ( & stdout) ;
697+ let mut linker_info = String :: new ( ) ;
698+
699+ info ! ( "linker stderr:\n {}" , & escaped_stderr) ;
700+ info ! ( "linker stdout:\n {}" , & escaped_stdout) ;
701+
702+ fn for_each ( bytes : & [ u8 ] , mut f : impl FnMut ( & str , & mut String ) ) -> String {
703+ let mut output = String :: new ( ) ;
704+ if let Ok ( str) = str:: from_utf8 ( bytes) {
705+ info ! ( "line: {str}" ) ;
706+ output = String :: with_capacity ( str. len ( ) ) ;
707+ for line in str. lines ( ) {
708+ f ( line. trim ( ) , & mut output) ;
709+ }
710+ }
711+ escape_string ( output. trim ( ) . as_bytes ( ) )
712+ }
713+
714+ // Hide some progress messages from link.exe that we don't care about.
715+ // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
716+ if is_msvc_link_exe ( sess) {
717+ escaped_stdout = for_each ( & stdout, |line, output| {
718+ if line. starts_with ( " Creating library" )
719+ || line. starts_with ( "Generating code" )
720+ || line. starts_with ( "Finished generating code" )
721+ {
722+ linker_info += line;
723+ linker_info += "\r \n " ;
724+ } else {
725+ * output += line;
726+ * output += "\r \n "
727+ }
728+ } ) ;
729+ } else if is_macos_ld ( sess) {
730+ let deployment_mismatch = |line : & str | {
731+ line. starts_with ( "ld: warning: object file (" )
732+ && line. contains ( "was built for newer 'macOS' version" )
733+ && line. contains ( "than being linked" )
734+ } ;
735+ let search_path = |line : & str | {
736+ line. starts_with ( "ld: warning: search path '" ) && line. ends_with ( "' not found" )
737+ } ;
738+ escaped_stderr = for_each ( & stderr, |line, output| {
739+ if line. starts_with ( "ld: warning: ignoring duplicate libraries: " )
740+ || deployment_mismatch ( line)
741+ || search_path ( line)
742+ {
743+ linker_info += line;
744+ linker_info += "\n " ;
745+ } else {
746+ * output += line;
747+ * output += "\n "
748+ }
749+ } ) ;
750+ } else if is_windows_gnu_ld ( sess) {
751+ let mut saw_exclude_symbol = false ;
752+ let exclude_symbols = |line : & str | {
753+ line. starts_with ( "Warning: .drectve `-exclude-symbols:" )
754+ && line. ends_with ( "' unrecognized" )
755+ } ;
756+ // FIXME: are we sure this is stderr and not stdout?
757+ escaped_stderr = for_each ( & stderr, |line, output| {
758+ if exclude_symbols ( line) {
759+ saw_exclude_symbol = true ;
760+ linker_info += line;
761+ linker_info += "\n " ;
762+ } else if saw_exclude_symbol && line == "Warning: corrupt .drectve at end of def file" {
763+ linker_info += line;
764+ linker_info += "\n " ;
765+ } else {
766+ * output += line;
767+ * output += "\n "
768+ }
769+ } ) ;
770+ }
771+
772+ let lint_msg = |msg| {
773+ lint_level ( sess, LINKER_MESSAGES , levels. linker_messages , None , |diag| {
774+ LinkerOutput { inner : msg } . decorate_lint ( diag)
775+ } )
776+ } ;
777+ let lint_info = |msg| {
778+ lint_level ( sess, LINKER_INFO , levels. linker_info , None , |diag| {
779+ LinkerOutput { inner : msg } . decorate_lint ( diag)
780+ } )
781+ } ;
782+
783+ if !escaped_stderr. is_empty ( ) {
784+ // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
785+ escaped_stderr =
786+ escaped_stderr. strip_prefix ( "warning: " ) . unwrap_or ( & escaped_stderr) . to_owned ( ) ;
787+ escaped_stderr = escaped_stderr
788+ . strip_prefix ( "Warning: " )
789+ . unwrap_or ( & escaped_stderr)
790+ . replace ( ": warning: " , ": " ) ;
791+ lint_msg ( format ! ( "linker stderr: {escaped_stderr}" ) ) ;
792+ }
793+ if !escaped_stdout. is_empty ( ) {
794+ lint_msg ( format ! ( "linker stdout: {}" , escaped_stdout) )
795+ }
796+ if !linker_info. is_empty ( ) {
797+ lint_info ( linker_info) ;
798+ }
799+ }
800+
672801/// Create a dynamic library or executable.
673802///
674803/// This will invoke the system linker/cc to create the resulting file. This links to all upstream
@@ -855,11 +984,6 @@ fn link_natively(
855984
856985 match prog {
857986 Ok ( prog) => {
858- let is_msvc_link_exe = sess. target . is_like_msvc
859- && flavor == LinkerFlavor :: Msvc ( Lld :: No )
860- // Match exactly "link.exe"
861- && linker_path. to_str ( ) == Some ( "link.exe" ) ;
862-
863987 if !prog. status . success ( ) {
864988 let mut output = prog. stderr . clone ( ) ;
865989 output. extend_from_slice ( & prog. stdout ) ;
@@ -879,7 +1003,7 @@ fn link_natively(
8791003 if let Some ( code) = prog. status . code ( ) {
8801004 // All Microsoft `link.exe` linking ror codes are
8811005 // four digit numbers in the range 1000 to 9999 inclusive
882- if is_msvc_link_exe && ( code < 1000 || code > 9999 ) {
1006+ if is_msvc_link_exe ( sess ) && ( code < 1000 || code > 9999 ) {
8831007 let is_vs_installed = find_msvc_tools:: find_vs_version ( ) . is_ok ( ) ;
8841008 let has_linker =
8851009 find_msvc_tools:: find_tool ( sess. target . arch . desc ( ) , "link.exe" )
@@ -911,48 +1035,12 @@ fn link_natively(
9111035 sess. dcx ( ) . abort_if_errors ( ) ;
9121036 }
9131037
914- let stderr = escape_string ( & prog. stderr ) ;
915- let mut stdout = escape_string ( & prog. stdout ) ;
916- info ! ( "linker stderr:\n {}" , & stderr) ;
917- info ! ( "linker stdout:\n {}" , & stdout) ;
918-
919- // Hide some progress messages from link.exe that we don't care about.
920- // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
921- if is_msvc_link_exe {
922- if let Ok ( str) = str:: from_utf8 ( & prog. stdout ) {
923- let mut output = String :: with_capacity ( str. len ( ) ) ;
924- for line in stdout. lines ( ) {
925- if line. starts_with ( " Creating library" )
926- || line. starts_with ( "Generating code" )
927- || line. starts_with ( "Finished generating code" )
928- {
929- continue ;
930- }
931- output += line;
932- output += "\r \n "
933- }
934- stdout = escape_string ( output. trim ( ) . as_bytes ( ) )
935- }
936- }
937-
938- let level = codegen_results. crate_info . lint_levels . linker_messages ;
939- let lint = |msg| {
940- lint_level ( sess, LINKER_MESSAGES , level, None , |diag| {
941- LinkerOutput { inner : msg } . decorate_lint ( diag)
942- } )
943- } ;
944-
945- if !prog. stderr . is_empty ( ) {
946- // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
947- let stderr = stderr
948- . strip_prefix ( "warning: " )
949- . unwrap_or ( & stderr)
950- . replace ( ": warning: " , ": " ) ;
951- lint ( format ! ( "linker stderr: {stderr}" ) ) ;
952- }
953- if !stdout. is_empty ( ) {
954- lint ( format ! ( "linker stdout: {}" , stdout) )
955- }
1038+ report_linker_output (
1039+ sess,
1040+ codegen_results. crate_info . lint_levels ,
1041+ & prog. stdout ,
1042+ & prog. stderr ,
1043+ ) ;
9561044 }
9571045 Err ( e) => {
9581046 let linker_not_found = e. kind ( ) == io:: ErrorKind :: NotFound ;
0 commit comments