diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 5881c6236ece6..f76a63c3897c7 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -160,6 +160,8 @@ codegen_ssa_linker_file_stem = couldn't extract file stem from specified linker codegen_ssa_linker_not_found = linker `{$linker_path}` not found .note = {$error} +codegen_ssa_linker_output = {$inner} + codegen_ssa_linker_unsupported_modifier = `as-needed` modifier not supported for current linker codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index b32865a0518ba..da542f797c1c5 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -721,6 +721,15 @@ fn link_dwarf_object<'a>( } } +#[derive(Diagnostic)] +#[diag(codegen_ssa_linker_output)] +/// Translating this is kind of useless. We don't pass translation flags to the linker, so we'd just +/// end up with inconsistent languages within the same diagnostic. Ideally we'd accept strings in +/// `emit_warning`, but I didn't feel like doing that much of a refactor. +struct LinkerOutput { + inner: String, +} + /// Create a dynamic library or executable. /// /// This will invoke the system linker/cc to create the resulting file. This links to all upstream @@ -972,8 +981,20 @@ fn link_natively<'a>( sess.abort_if_errors(); } - info!("linker stderr:\n{}", escape_string(&prog.stderr)); - info!("linker stdout:\n{}", escape_string(&prog.stdout)); + + if !prog.stderr.is_empty() { + // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present. + let stderr = escape_string(&prog.stderr); + debug!("original stderr: {stderr}"); + let stderr = + stderr.strip_prefix("warning: ").unwrap_or(&stderr).replace(" warning: ", " "); + sess.emit_warning(LinkerOutput { inner: format!("linker stderr: {stderr}") }); + } + if !prog.stdout.is_empty() && sess.opts.verbose { + sess.emit_warning(LinkerOutput { + inner: format!("linker stdout: {}", escape_string(&prog.stdout)), + }); + } } Err(e) => { let linker_not_found = e.kind() == io::ErrorKind::NotFound; diff --git a/tests/run-make/linker-warning/Makefile b/tests/run-make/linker-warning/Makefile new file mode 100644 index 0000000000000..8b9e98f4ec54e --- /dev/null +++ b/tests/run-make/linker-warning/Makefile @@ -0,0 +1,17 @@ +include ../tools.mk + +RUN_RUSTC := $(RUSTC_ORIGINAL) main.rs -o $(TMPDIR)/main -C linker=./fake-linker.sh + +all: + # Run rustc with our fake linker, and make sure it shows warnings + $(RUN_RUSTC) -C link-arg=run_make_warn 2>&1 | $(CGREP) "warning: linker stderr: bar" + + # Make sure it shows stdout, but only when --verbose is passed + $(RUN_RUSTC) -C link-arg=run_make_info --verbose 2>&1 | $(CGREP) "warning: linker stdout: foo" + $(RUN_RUSTC) -C link-arg=run_make_info 2>&1 | $(CGREP) -v "warning: linker stdout: foo" + + # Make sure we short-circuit this new path if the linker exits with an error (so the diagnostic is less verbose) + rm -f $(TMPDIR)/main + $(RUN_RUSTC) -C link-arg=run_make_error 2>&1 | $(CGREP) "note: error: baz" + ! [ -e $(TMPDIR)/main ] + diff --git a/tests/run-make/linker-warning/fake-linker.sh b/tests/run-make/linker-warning/fake-linker.sh new file mode 100755 index 0000000000000..ed4d472c3bfbd --- /dev/null +++ b/tests/run-make/linker-warning/fake-linker.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +code=0 +while ! [ $# = 0 ]; do + case "$1" in + run_make_info) echo "foo" + ;; + run_make_warn) echo "warning: bar" >&2 + ;; + run_make_error) echo "error: baz" >&2; code=1 + ;; + *) ;; # rustc passes lots of args we don't care about + esac + shift +done + +exit $code diff --git a/tests/run-make/linker-warning/main.rs b/tests/run-make/linker-warning/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make/linker-warning/main.rs @@ -0,0 +1 @@ +fn main() {}