Skip to content

Commit

Permalink
Auto merge of rust-lang#119286 - jyn514:linker-output, r=<try>
Browse files Browse the repository at this point in the history
show linker output even if the linker succeeds

Show stderr and stderr by default, controlled by a new `linker_messages` lint.

fixes rust-lang#83436. fixes rust-lang#38206. cc https://rust-lang.zulipchat.com/#narrow/stream/233931-t-compiler.2Fmajor-changes/topic/uplift.20some.20-Zverbose.20calls.20and.20rename.20to.E2.80.A6.20compiler-team.23706/near/408986134

<!-- try-job: dist-x86_64-msvc -->
try-job: aarch64-apple

r? `@bjorn3`
  • Loading branch information
bors committed Jan 21, 2025
2 parents f3d1d47 + 4b4007a commit cbcfbed
Show file tree
Hide file tree
Showing 26 changed files with 372 additions and 90 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_codegen_ssa/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,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}
Expand Down
115 changes: 85 additions & 30 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ use rustc_ast::CRATE_NODE_ID;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::DiagCtxtHandle;
use rustc_errors::{DiagCtxtHandle, LintDiagnostic};
use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_macros::LintDiagnostic;
use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};
use rustc_metadata::{find_native_static_library, walk_native_lib_search_dirs};
use rustc_middle::bug;
use rustc_middle::lint::lint_level;
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::SymbolExportKind;
Expand All @@ -29,6 +31,7 @@ use rustc_session::config::{
OutputType, PrintKind, SplitDwarfKind, Strip,
};
use rustc_session::cstore::DllImport;
use rustc_session::lint::builtin::LINKER_MESSAGES;
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
use rustc_session::search_paths::PathKind;
use rustc_session::utils::NativeLibKind;
Expand Down Expand Up @@ -749,6 +752,14 @@ fn link_dwarf_object(sess: &Session, cg_results: &CodegenResults, executable_out
}
}

#[derive(LintDiagnostic)]
#[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.
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
Expand Down Expand Up @@ -981,6 +992,20 @@ fn link_natively(

match prog {
Ok(prog) => {
let is_msvc_link_exe = if let Some(code) = prog.status.code() {
sess.target.is_like_msvc
&& flavor == LinkerFlavor::Msvc(Lld::No)
// Respect the command line override
&& sess.opts.cg.linker.is_none()
// Match exactly "link.exe"
&& linker_path.to_str() == Some("link.exe")
// All Microsoft `link.exe` linking error codes are
// four digit numbers in the range 1000 to 9999 inclusive
&& (code < 1000 || code > 9999)
} else {
false
};

if !prog.status.success() {
let mut output = prog.stderr.clone();
output.extend_from_slice(&prog.stdout);
Expand All @@ -996,40 +1021,70 @@ fn link_natively(
// If MSVC's `link.exe` was expected but the return code
// is not a Microsoft LNK error then suggest a way to fix or
// install the Visual Studio build tools.
if let Some(code) = prog.status.code() {
if sess.target.is_like_msvc
&& flavor == LinkerFlavor::Msvc(Lld::No)
// Respect the command line override
&& sess.opts.cg.linker.is_none()
// Match exactly "link.exe"
&& linker_path.to_str() == Some("link.exe")
// All Microsoft `link.exe` linking error codes are
// four digit numbers in the range 1000 to 9999 inclusive
&& (code < 1000 || code > 9999)
{
let is_vs_installed = windows_registry::find_vs_version().is_ok();
let has_linker =
windows_registry::find_tool(&sess.target.arch, "link.exe").is_some();

sess.dcx().emit_note(errors::LinkExeUnexpectedError);
if is_vs_installed && has_linker {
// the linker is broken
sess.dcx().emit_note(errors::RepairVSBuildTools);
sess.dcx().emit_note(errors::MissingCppBuildToolComponent);
} else if is_vs_installed {
// the linker is not installed
sess.dcx().emit_note(errors::SelectCppBuildToolWorkload);
} else {
// visual studio is not installed
sess.dcx().emit_note(errors::VisualStudioNotInstalled);
}
if is_msvc_link_exe {
let is_vs_installed = windows_registry::find_vs_version().is_ok();
let has_linker =
windows_registry::find_tool(&sess.target.arch, "link.exe").is_some();

sess.dcx().emit_note(errors::LinkExeUnexpectedError);
if is_vs_installed && has_linker {
// the linker is broken
sess.dcx().emit_note(errors::RepairVSBuildTools);
sess.dcx().emit_note(errors::MissingCppBuildToolComponent);
} else if is_vs_installed {
// the linker is not installed
sess.dcx().emit_note(errors::SelectCppBuildToolWorkload);
} else {
// visual studio is not installed
sess.dcx().emit_note(errors::VisualStudioNotInstalled);
}
}

sess.dcx().abort_if_errors();
}
info!("linker stderr:\n{}", escape_string(&prog.stderr));
info!("linker stdout:\n{}", escape_string(&prog.stdout));

let stderr = escape_string(&prog.stderr);
let mut stdout = escape_string(&prog.stdout);
info!("linker stderr:\n{}", &stderr);
info!("linker stdout:\n{}", &stdout);

// Hide some progress messages from link.exe that we don't care about.
// See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
if is_msvc_link_exe {
if let Ok(str) = str::from_utf8(&prog.stdout) {
let mut output = String::with_capacity(str.len());
for line in stdout.lines() {
if line.starts_with(" Creating library")
|| line.starts_with("Generating code")
|| line.starts_with("Finished generating code")
{
continue;
}
output += line;
output += "\r\n"
}
stdout = escape_string(output.trim().as_bytes())
}
}

let (level, src) = codegen_results.crate_info.lint_levels.linker_messages;
let lint = |msg| {
lint_level(sess, LINKER_MESSAGES, level, src, None, |diag| {
LinkerOutput { inner: msg }.decorate_lint(diag)
})
};

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 = stderr
.strip_prefix("warning: ")
.unwrap_or(&stderr)
.replace(": warning: ", ": ");
lint(format!("linker stderr: {stderr}"));
}
if !stdout.is_empty() {
lint(format!("linker stdout: {}", stdout))
}
}
Err(e) => {
let linker_not_found = e.kind() == io::ErrorKind::NotFound;
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ use crate::mir::operand::OperandValue;
use crate::mir::place::PlaceRef;
use crate::traits::*;
use crate::{
CachedModuleCodegen, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, errors, meth, mir,
CachedModuleCodegen, CodegenLintLevels, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind,
errors, meth, mir,
};

pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate {
Expand Down Expand Up @@ -927,6 +928,7 @@ impl CrateInfo {
dependency_formats: Lrc::clone(tcx.dependency_formats(())),
windows_subsystem,
natvis_debugger_visualizers: Default::default(),
lint_levels: CodegenLintLevels::from_tcx(tcx),
};

info.native_libraries.reserve(n_crates);
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,23 @@ use rustc_ast as ast;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::unord::UnordMap;
use rustc_hir::CRATE_HIR_ID;
use rustc_hir::def_id::CrateNum;
use rustc_macros::{Decodable, Encodable, HashStable};
use rustc_middle::dep_graph::WorkProduct;
use rustc_middle::lint::LintLevelSource;
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
use rustc_middle::middle::dependency_format::Dependencies;
use rustc_middle::middle::exported_symbols::SymbolExportKind;
use rustc_middle::ty::TyCtxt;
use rustc_middle::util::Providers;
use rustc_serialize::opaque::{FileEncoder, MemDecoder};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_session::Session;
use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
use rustc_session::cstore::{self, CrateSource};
use rustc_session::lint::Level;
use rustc_session::lint::builtin::LINKER_MESSAGES;
use rustc_session::utils::NativeLibKind;
use rustc_span::Symbol;

Expand Down Expand Up @@ -200,6 +205,7 @@ pub struct CrateInfo {
pub dependency_formats: Lrc<Dependencies>,
pub windows_subsystem: Option<String>,
pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>,
pub lint_levels: CodegenLintLevels,
}

#[derive(Encodable, Decodable)]
Expand Down Expand Up @@ -302,3 +308,19 @@ impl CodegenResults {
Ok((codegen_results, outputs))
}
}

/// A list of lint levels used in codegen.
///
/// When using `-Z link-only`, we don't have access to the tcx and must work
/// solely from the `.rlink` file. `Lint`s are defined too early to be encodeable.
/// Instead, encode exactly the information we need.
#[derive(Copy, Clone, Debug, Encodable, Decodable)]
pub struct CodegenLintLevels {
linker_messages: (Level, LintLevelSource),
}

impl CodegenLintLevels {
pub fn from_tcx(tcx: TyCtxt<'_>) -> Self {
Self { linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID) }
}
}
47 changes: 30 additions & 17 deletions compiler/rustc_errors/src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use rustc_error_messages::FluentArgs;
use rustc_lint_defs::Applicability;
use rustc_span::Span;
use rustc_span::hygiene::ExpnData;
use rustc_span::source_map::SourceMap;
use rustc_span::source_map::{FilePathMapping, SourceMap};
use serde::Serialize;
use termcolor::{ColorSpec, WriteColor};

Expand All @@ -45,7 +45,7 @@ pub struct JsonEmitter {
#[setters(skip)]
dst: IntoDynSyncSend<Box<dyn Write + Send>>,
#[setters(skip)]
sm: Lrc<SourceMap>,
sm: Option<Lrc<SourceMap>>,
fluent_bundle: Option<Lrc<FluentBundle>>,
#[setters(skip)]
fallback_bundle: LazyFallbackBundle,
Expand All @@ -65,7 +65,7 @@ pub struct JsonEmitter {
impl JsonEmitter {
pub fn new(
dst: Box<dyn Write + Send>,
sm: Lrc<SourceMap>,
sm: Option<Lrc<SourceMap>>,
fallback_bundle: LazyFallbackBundle,
pretty: bool,
json_rendered: HumanReadableErrorType,
Expand Down Expand Up @@ -171,7 +171,7 @@ impl Emitter for JsonEmitter {
}

fn source_map(&self) -> Option<&SourceMap> {
Some(&self.sm)
self.sm.as_deref()
}

fn should_show_explain(&self) -> bool {
Expand Down Expand Up @@ -371,7 +371,7 @@ impl Diagnostic {
}
HumanEmitter::new(dst, Lrc::clone(&je.fallback_bundle))
.short_message(short)
.sm(Some(Lrc::clone(&je.sm)))
.sm(je.sm.clone())
.fluent_bundle(je.fluent_bundle.clone())
.diagnostic_width(je.diagnostic_width)
.macro_backtrace(je.macro_backtrace)
Expand Down Expand Up @@ -458,23 +458,34 @@ impl DiagnosticSpan {
mut backtrace: impl Iterator<Item = ExpnData>,
je: &JsonEmitter,
) -> DiagnosticSpan {
let start = je.sm.lookup_char_pos(span.lo());
let empty_source_map;
let sm = match &je.sm {
Some(s) => s,
None => {
span = rustc_span::DUMMY_SP;
empty_source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
empty_source_map
.new_source_file(std::path::PathBuf::from("empty.rs").into(), String::new());
&empty_source_map
}
};
let start = sm.lookup_char_pos(span.lo());
// If this goes from the start of a line to the end and the replacement
// is an empty string, increase the length to include the newline so we don't
// leave an empty line
if start.col.0 == 0
&& let Some((suggestion, _)) = suggestion
&& suggestion.is_empty()
&& let Ok(after) = je.sm.span_to_next_source(span)
&& let Ok(after) = sm.span_to_next_source(span)
&& after.starts_with('\n')
{
span = span.with_hi(span.hi() + rustc_span::BytePos(1));
}
let end = je.sm.lookup_char_pos(span.hi());
let end = sm.lookup_char_pos(span.hi());
let backtrace_step = backtrace.next().map(|bt| {
let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je);
let def_site_span = Self::from_span_full(
je.sm.guess_head_span(bt.def_site),
sm.guess_head_span(bt.def_site),
false,
None,
None,
Expand All @@ -489,7 +500,7 @@ impl DiagnosticSpan {
});

DiagnosticSpan {
file_name: je.sm.filename_for_diagnostics(&start.file.name).to_string(),
file_name: sm.filename_for_diagnostics(&start.file.name).to_string(),
byte_start: start.file.original_relative_byte_pos(span.lo()).0,
byte_end: start.file.original_relative_byte_pos(span.hi()).0,
line_start: start.line,
Expand Down Expand Up @@ -559,19 +570,20 @@ impl DiagnosticSpanLine {
/// `span` within the line.
fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
je.sm
.span_to_lines(span)
.map(|lines| {
.as_ref()
.and_then(|sm| {
let lines = sm.span_to_lines(span).ok()?;
// We can't get any lines if the source is unavailable.
if !should_show_source_code(
&je.ignored_directories_in_source_blocks,
&je.sm,
&sm,
&lines.file,
) {
return vec![];
return None;
}

let sf = &*lines.file;
lines
let span_lines = lines
.lines
.iter()
.map(|line| {
Expand All @@ -582,8 +594,9 @@ impl DiagnosticSpanLine {
line.end_col.0 + 1,
)
})
.collect()
.collect();
Some(span_lines)
})
.unwrap_or_else(|_| vec![])
.unwrap_or_default()
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_errors/src/json/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
let output = Arc::new(Mutex::new(Vec::new()));
let je = JsonEmitter::new(
Box::new(Shared { data: output.clone() }),
sm,
Some(sm),
fallback_bundle,
true, // pretty
HumanReadableErrorType::Short,
Expand Down
Loading

0 comments on commit cbcfbed

Please sign in to comment.