@@ -22,6 +22,7 @@ use rustc_errors::{DiagCtxtHandle, LintDiagnostic};
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:: LintDiagnostic ;
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- CodegenResults , CompiledModule , CrateInfo , NativeLib , errors, looks_like_rust_object_file,
64+ CodegenLintLevels , CodegenResults , CompiledModule , CrateInfo , NativeLib , errors,
65+ looks_like_rust_object_file,
6466} ;
6567
6668pub fn ensure_removed ( dcx : DiagCtxtHandle < ' _ > , path : & Path ) {
@@ -670,6 +672,145 @@ struct LinkerOutput {
670672 inner : String ,
671673}
672674
675+ fn is_msvc_link_exe ( sess : & Session ) -> bool {
676+ let ( linker_path, flavor) = linker_and_flavor ( sess) ;
677+ sess. target . is_like_msvc
678+ && flavor == LinkerFlavor :: Msvc ( Lld :: No )
679+ // Match exactly "link.exe"
680+ && linker_path. to_str ( ) == Some ( "link.exe" )
681+ }
682+
683+ fn is_macos_ld ( sess : & Session ) -> bool {
684+ let ( _, flavor) = linker_and_flavor ( sess) ;
685+ sess. target . is_like_darwin && matches ! ( flavor, LinkerFlavor :: Darwin ( _, Lld :: No ) )
686+ }
687+
688+ fn is_windows_gnu_ld ( sess : & Session ) -> bool {
689+ let ( _, flavor) = linker_and_flavor ( sess) ;
690+ sess. target . is_like_windows
691+ && !sess. target . is_like_msvc
692+ && matches ! ( flavor, LinkerFlavor :: Gnu ( _, Lld :: No ) )
693+ }
694+
695+ fn report_linker_output ( sess : & Session , levels : CodegenLintLevels , stdout : & [ u8 ] , stderr : & [ u8 ] ) {
696+ let mut escaped_stderr = escape_string ( & stderr) ;
697+ let mut escaped_stdout = escape_string ( & stdout) ;
698+ let mut linker_info = String :: new ( ) ;
699+
700+ info ! ( "linker stderr:\n {}" , & escaped_stderr) ;
701+ info ! ( "linker stdout:\n {}" , & escaped_stdout) ;
702+
703+ fn for_each ( bytes : & [ u8 ] , mut f : impl FnMut ( & str , & mut String ) ) -> String {
704+ let mut output = String :: new ( ) ;
705+ if let Ok ( str) = str:: from_utf8 ( bytes) {
706+ info ! ( "line: {str}" ) ;
707+ output = String :: with_capacity ( str. len ( ) ) ;
708+ for line in str. lines ( ) {
709+ f ( line. trim ( ) , & mut output) ;
710+ }
711+ }
712+ escape_string ( output. trim ( ) . as_bytes ( ) )
713+ }
714+
715+ if is_msvc_link_exe ( sess) {
716+ info ! ( "inferred MSVC link.exe" ) ;
717+
718+ escaped_stdout = for_each ( & stdout, |line, output| {
719+ // Hide some progress messages from link.exe that we don't care about.
720+ // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
721+ if line. starts_with ( " Creating library" )
722+ || line. starts_with ( "Generating code" )
723+ || line. starts_with ( "Finished generating code" )
724+ {
725+ linker_info += line;
726+ linker_info += "\r \n " ;
727+ } else {
728+ * output += line;
729+ * output += "\r \n "
730+ }
731+ } ) ;
732+ } else if is_macos_ld ( sess) {
733+ info ! ( "inferred macOS LD" ) ;
734+
735+ // FIXME: Tracked by https://github.com/rust-lang/rust/issues/136113
736+ let deployment_mismatch = |line : & str | {
737+ line. starts_with ( "ld: warning: object file (" )
738+ && line. contains ( "was built for newer 'macOS' version" )
739+ && line. contains ( "than being linked" )
740+ } ;
741+ // FIXME: This is a real warning we would like to show, but it hits too many crates
742+ // to want to turn it on immediately.
743+ let search_path = |line : & str | {
744+ line. starts_with ( "ld: warning: search path '" ) && line. ends_with ( "' not found" )
745+ } ;
746+ escaped_stderr = for_each ( & stderr, |line, output| {
747+ // This duplicate library warning is just not helpful at all.
748+ if line. starts_with ( "ld: warning: ignoring duplicate libraries: " )
749+ || deployment_mismatch ( line)
750+ || search_path ( line)
751+ {
752+ linker_info += line;
753+ linker_info += "\n " ;
754+ } else {
755+ * output += line;
756+ * output += "\n "
757+ }
758+ } ) ;
759+ } else if is_windows_gnu_ld ( sess) {
760+ info ! ( "inferred Windows GNU LD" ) ;
761+
762+ let mut saw_exclude_symbol = false ;
763+ // See https://github.com/rust-lang/rust/issues/112368.
764+ // FIXME: maybe check that binutils is older than 2.40 before downgrading this warning?
765+ let exclude_symbols = |line : & str | {
766+ line. starts_with ( "Warning: .drectve `-exclude-symbols:" )
767+ && line. ends_with ( "' unrecognized" )
768+ } ;
769+ escaped_stderr = for_each ( & stderr, |line, output| {
770+ if exclude_symbols ( line) {
771+ saw_exclude_symbol = true ;
772+ linker_info += line;
773+ linker_info += "\n " ;
774+ } else if saw_exclude_symbol && line == "Warning: corrupt .drectve at end of def file" {
775+ linker_info += line;
776+ linker_info += "\n " ;
777+ } else {
778+ * output += line;
779+ * output += "\n "
780+ }
781+ } ) ;
782+ }
783+
784+ let lint_msg = |msg| {
785+ lint_level ( sess, LINKER_MESSAGES , levels. linker_messages , None , |diag| {
786+ LinkerOutput { inner : msg } . decorate_lint ( diag)
787+ } )
788+ } ;
789+ let lint_info = |msg| {
790+ lint_level ( sess, LINKER_INFO , levels. linker_info , None , |diag| {
791+ LinkerOutput { inner : msg } . decorate_lint ( diag)
792+ } )
793+ } ;
794+
795+ if !escaped_stderr. is_empty ( ) {
796+ // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
797+ escaped_stderr =
798+ escaped_stderr. strip_prefix ( "warning: " ) . unwrap_or ( & escaped_stderr) . to_owned ( ) ;
799+ // Windows GNU LD prints uppercase Warning
800+ escaped_stderr = escaped_stderr
801+ . strip_prefix ( "Warning: " )
802+ . unwrap_or ( & escaped_stderr)
803+ . replace ( ": warning: " , ": " ) ;
804+ lint_msg ( format ! ( "linker stderr: {escaped_stderr}" ) ) ;
805+ }
806+ if !escaped_stdout. is_empty ( ) {
807+ lint_msg ( format ! ( "linker stdout: {}" , escaped_stdout) )
808+ }
809+ if !linker_info. is_empty ( ) {
810+ lint_info ( linker_info) ;
811+ }
812+ }
813+
673814/// Create a dynamic library or executable.
674815///
675816/// This will invoke the system linker/cc to create the resulting file. This links to all upstream
@@ -856,11 +997,6 @@ fn link_natively(
856997
857998 match prog {
858999 Ok ( prog) => {
859- let is_msvc_link_exe = sess. target . is_like_msvc
860- && flavor == LinkerFlavor :: Msvc ( Lld :: No )
861- // Match exactly "link.exe"
862- && linker_path. to_str ( ) == Some ( "link.exe" ) ;
863-
8641000 if !prog. status . success ( ) {
8651001 let mut output = prog. stderr . clone ( ) ;
8661002 output. extend_from_slice ( & prog. stdout ) ;
@@ -880,7 +1016,7 @@ fn link_natively(
8801016 if let Some ( code) = prog. status . code ( ) {
8811017 // All Microsoft `link.exe` linking ror codes are
8821018 // four digit numbers in the range 1000 to 9999 inclusive
883- if is_msvc_link_exe && ( code < 1000 || code > 9999 ) {
1019+ if is_msvc_link_exe ( sess ) && ( code < 1000 || code > 9999 ) {
8841020 let is_vs_installed = find_msvc_tools:: find_vs_version ( ) . is_ok ( ) ;
8851021 let has_linker =
8861022 find_msvc_tools:: find_tool ( sess. target . arch . desc ( ) , "link.exe" )
@@ -912,48 +1048,13 @@ fn link_natively(
9121048 sess. dcx ( ) . abort_if_errors ( ) ;
9131049 }
9141050
915- let stderr = escape_string ( & prog. stderr ) ;
916- let mut stdout = escape_string ( & prog. stdout ) ;
917- info ! ( "linker stderr:\n {}" , & stderr) ;
918- info ! ( "linker stdout:\n {}" , & stdout) ;
919-
920- // Hide some progress messages from link.exe that we don't care about.
921- // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
922- if is_msvc_link_exe {
923- if let Ok ( str) = str:: from_utf8 ( & prog. stdout ) {
924- let mut output = String :: with_capacity ( str. len ( ) ) ;
925- for line in stdout. lines ( ) {
926- if line. starts_with ( " Creating library" )
927- || line. starts_with ( "Generating code" )
928- || line. starts_with ( "Finished generating code" )
929- {
930- continue ;
931- }
932- output += line;
933- output += "\r \n "
934- }
935- stdout = escape_string ( output. trim ( ) . as_bytes ( ) )
936- }
937- }
938-
939- let level = codegen_results. crate_info . lint_levels . linker_messages ;
940- let lint = |msg| {
941- lint_level ( sess, LINKER_MESSAGES , level, None , |diag| {
942- LinkerOutput { inner : msg } . decorate_lint ( diag)
943- } )
944- } ;
945-
946- if !prog. stderr . is_empty ( ) {
947- // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
948- let stderr = stderr
949- . strip_prefix ( "warning: " )
950- . unwrap_or ( & stderr)
951- . replace ( ": warning: " , ": " ) ;
952- lint ( format ! ( "linker stderr: {stderr}" ) ) ;
953- }
954- if !stdout. is_empty ( ) {
955- lint ( format ! ( "linker stdout: {}" , stdout) )
956- }
1051+ info ! ( "reporting linker output: flavor={flavor:?}" ) ;
1052+ report_linker_output (
1053+ sess,
1054+ codegen_results. crate_info . lint_levels ,
1055+ & prog. stdout ,
1056+ & prog. stderr ,
1057+ ) ;
9571058 }
9581059 Err ( e) => {
9591060 let linker_not_found = e. kind ( ) == io:: ErrorKind :: NotFound ;
0 commit comments