Skip to content

Commit 12aa84b

Browse files
committed
ADD - initial port of link.rs
1 parent 0f97d4a commit 12aa84b

File tree

4 files changed

+166
-58
lines changed

4 files changed

+166
-58
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

+37-52
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ use super::command::Command;
3131
use super::linker::{self, Linker};
3232
use super::metadata::{create_rmeta_file, MetadataPosition};
3333
use super::rpath::{self, RPathConfig};
34-
use crate::{looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib};
34+
use crate::{
35+
errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib,
36+
};
3537

3638
use cc::windows_registry;
3739
use regex::Regex;
@@ -93,7 +95,7 @@ pub fn link_binary<'a>(
9395
let tmpdir = TempFileBuilder::new()
9496
.prefix("rustc")
9597
.tempdir()
96-
.unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err)));
98+
.unwrap_or_else(|error| sess.emit_fatal(errors::CreateTempDir { error }));
9799
let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps);
98100
let out_filename = out_filename(
99101
sess,
@@ -208,7 +210,7 @@ pub fn link_binary<'a>(
208210
pub fn each_linked_rlib(
209211
info: &CrateInfo,
210212
f: &mut dyn FnMut(CrateNum, &Path),
211-
) -> Result<(), String> {
213+
) -> Result<(), errors::LinkRlibError> {
212214
let crates = info.used_crates.iter();
213215
let mut fmts = None;
214216
for (ty, list) in info.dependency_formats.iter() {
@@ -224,26 +226,23 @@ pub fn each_linked_rlib(
224226
}
225227
}
226228
let Some(fmts) = fmts else {
227-
return Err("could not find formats for rlibs".to_string());
229+
return Err(errors::LinkRlibError::MissingFormat);
228230
};
229231
for &cnum in crates {
230232
match fmts.get(cnum.as_usize() - 1) {
231233
Some(&Linkage::NotLinked | &Linkage::IncludedFromDylib) => continue,
232234
Some(_) => {}
233-
None => return Err("could not find formats for rlibs".to_string()),
235+
None => return Err(errors::LinkRlibError::MissingFormat),
234236
}
235-
let name = info.crate_name[&cnum];
237+
let crate_name = info.crate_name[&cnum];
236238
let used_crate_source = &info.used_crate_source[&cnum];
237239
if let Some((path, _)) = &used_crate_source.rlib {
238240
f(cnum, &path);
239241
} else {
240242
if used_crate_source.rmeta.is_some() {
241-
return Err(format!(
242-
"could not find rlib for: `{}`, found rmeta (metadata) file",
243-
name
244-
));
243+
return Err(errors::LinkRlibError::OnlyRmetaFound { crate_name });
245244
} else {
246-
return Err(format!("could not find rlib for: `{}`", name));
245+
return Err(errors::LinkRlibError::NotFound { crate_name });
247246
}
248247
}
249248
}
@@ -340,10 +339,7 @@ fn link_rlib<'a>(
340339
// -whole-archive and it isn't clear how we can currently handle such a
341340
// situation correctly.
342341
// See https://github.com/rust-lang/rust/issues/88085#issuecomment-901050897
343-
sess.err(
344-
"the linking modifiers `+bundle` and `+whole-archive` are not compatible \
345-
with each other when generating rlibs",
346-
);
342+
sess.emit_err(errors::IncompatibleLinkingModifiers);
347343
}
348344
NativeLibKind::Static { bundle: None | Some(true), .. } => {}
349345
NativeLibKind::Static { bundle: Some(false), .. }
@@ -365,12 +361,11 @@ fn link_rlib<'a>(
365361
));
366362
continue;
367363
}
368-
ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|e| {
369-
sess.fatal(&format!(
370-
"failed to add native library {}: {}",
371-
location.to_string_lossy(),
372-
e
373-
));
364+
ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|error| {
365+
sess.emit_fatal(errors::AddNativeLibrary {
366+
library_path: &location.to_string_lossy(),
367+
error,
368+
});
374369
});
375370
}
376371
}
@@ -385,8 +380,11 @@ fn link_rlib<'a>(
385380
tmpdir.as_ref(),
386381
);
387382

388-
ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|e| {
389-
sess.fatal(&format!("failed to add native library {}: {}", output_path.display(), e));
383+
ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| {
384+
sess.emit_fatal(errors::AddNativeLibrary {
385+
library_path: &output_path.display().to_string(),
386+
error,
387+
});
390388
});
391389
}
392390

@@ -451,14 +449,11 @@ fn collate_raw_dylibs(
451449
// FIXME: when we add support for ordinals, figure out if we need to do anything
452450
// if we have two DllImport values with the same name but different ordinals.
453451
if import.calling_convention != old_import.calling_convention {
454-
sess.span_err(
455-
import.span,
456-
&format!(
457-
"multiple declarations of external function `{}` from \
458-
library `{}` have different calling conventions",
459-
import.name, name,
460-
),
461-
);
452+
sess.emit_err(errors::MultipleExternalFuncDecl {
453+
span: import.span,
454+
function: import.name,
455+
library_name: &name,
456+
});
462457
}
463458
}
464459
}
@@ -560,7 +555,7 @@ fn link_staticlib<'a>(
560555
all_native_libs.extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned());
561556
});
562557
if let Err(e) = res {
563-
sess.fatal(&e);
558+
sess.emit_fatal(e);
564559
}
565560

566561
ab.build(out_filename);
@@ -673,9 +668,8 @@ fn link_dwarf_object<'a>(
673668
}) {
674669
Ok(()) => {}
675670
Err(e) => {
676-
sess.struct_err("linking dwarf objects with thorin failed")
677-
.note(&format!("{:?}", e))
678-
.emit();
671+
let thorin_error = errors::ThorinErrorWrapper(e);
672+
sess.emit_err(errors::ThorinDwarfLinking { thorin_error });
679673
sess.abort_if_errors();
680674
}
681675
}
@@ -879,23 +873,14 @@ fn link_natively<'a>(
879873
let mut output = prog.stderr.clone();
880874
output.extend_from_slice(&prog.stdout);
881875
let escaped_output = escape_string(&output);
882-
let mut err = sess.struct_err(&format!(
883-
"linking with `{}` failed: {}",
884-
linker_path.display(),
885-
prog.status
886-
));
887-
err.note(&format!("{:?}", &cmd)).note(&escaped_output);
888-
if escaped_output.contains("undefined reference to") {
889-
err.help(
890-
"some `extern` functions couldn't be found; some native libraries may \
891-
need to be installed or have their path specified",
892-
);
893-
err.note("use the `-l` flag to specify native libraries to link");
894-
err.note("use the `cargo:rustc-link-lib` directive to specify the native \
895-
libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)");
896-
}
897-
err.emit();
898-
876+
// FIXME: Add UI tests for this error.
877+
let err = errors::LinkingFailed {
878+
linker_path: &linker_path,
879+
exit_status: prog.status,
880+
command: &cmd,
881+
escaped_output: &escaped_output,
882+
};
883+
sess.diagnostic().emit_err(err);
899884
// If MSVC's `link.exe` was expected but the return code
900885
// is not a Microsoft LNK error then suggest a way to fix or
901886
// install the Visual Studio build tools.

compiler/rustc_codegen_ssa/src/errors.rs

+103-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
//! Errors emitted by codegen_ssa
22
3-
use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
3+
use crate::back::command::Command;
4+
use rustc_errors::{
5+
fluent, DiagnosticArgValue, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
6+
IntoDiagnosticArg,
7+
};
48
use rustc_macros::Diagnostic;
9+
use rustc_span::{Span, Symbol};
510
use std::borrow::Cow;
611
use std::io::Error;
712
use std::path::{Path, PathBuf};
13+
use std::process::ExitStatus;
814

915
#[derive(Diagnostic)]
1016
#[diag(codegen_ssa::lib_def_write_failure)]
@@ -73,17 +79,15 @@ pub struct CopyPath<'a> {
7379

7480
impl<'a> CopyPath<'a> {
7581
pub fn new(from: &'a Path, to: &'a Path, error: Error) -> CopyPath<'a> {
76-
CopyPath { from: DebugArgPath { path: from }, to: DebugArgPath { path: to }, error }
82+
CopyPath { from: DebugArgPath(from), to: DebugArgPath(to), error }
7783
}
7884
}
7985

80-
struct DebugArgPath<'a> {
81-
pub path: &'a Path,
82-
}
86+
struct DebugArgPath<'a>(pub &'a Path);
8387

8488
impl IntoDiagnosticArg for DebugArgPath<'_> {
8589
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
86-
DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self.path)))
90+
DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self.0)))
8791
}
8892
}
8993

@@ -98,3 +102,96 @@ pub struct IgnoringEmitPath {
98102
pub struct IgnoringOutput {
99103
pub extension: String,
100104
}
105+
106+
#[derive(Diagnostic)]
107+
#[diag(codegen_ssa::create_temp_dir)]
108+
pub struct CreateTempDir {
109+
pub error: Error,
110+
}
111+
112+
#[derive(Diagnostic)]
113+
#[diag(codegen_ssa::incompatible_linking_modifiers)]
114+
pub struct IncompatibleLinkingModifiers;
115+
116+
#[derive(Diagnostic)]
117+
#[diag(codegen_ssa::add_native_library)]
118+
pub struct AddNativeLibrary<'a> {
119+
pub library_path: &'a str,
120+
pub error: Error,
121+
}
122+
123+
#[derive(Diagnostic)]
124+
#[diag(codegen_ssa::multiple_external_func_decl)]
125+
pub struct MultipleExternalFuncDecl<'a> {
126+
#[primary_span]
127+
pub span: Span,
128+
pub function: Symbol,
129+
pub library_name: &'a str,
130+
}
131+
132+
pub enum LinkRlibError {
133+
MissingFormat,
134+
OnlyRmetaFound { crate_name: Symbol },
135+
NotFound { crate_name: Symbol },
136+
}
137+
138+
impl IntoDiagnostic<'_, !> for LinkRlibError {
139+
fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
140+
match self {
141+
LinkRlibError::MissingFormat => {
142+
handler.struct_fatal(fluent::codegen_ssa::rlib_missing_format)
143+
}
144+
LinkRlibError::OnlyRmetaFound { crate_name } => {
145+
let mut diag = handler.struct_fatal(fluent::codegen_ssa::rlib_only_rmeta_found);
146+
diag.set_arg("crate_name", crate_name);
147+
diag
148+
}
149+
LinkRlibError::NotFound { crate_name } => {
150+
let mut diag = handler.struct_fatal(fluent::codegen_ssa::rlib_not_found);
151+
diag.set_arg("crate_name", crate_name);
152+
diag
153+
}
154+
}
155+
}
156+
}
157+
158+
#[derive(Diagnostic)]
159+
#[diag(codegen_ssa::thorin_dwarf_linking)]
160+
#[note]
161+
pub struct ThorinDwarfLinking {
162+
pub thorin_error: ThorinErrorWrapper,
163+
}
164+
pub struct ThorinErrorWrapper(pub thorin::Error);
165+
166+
// FIXME: How should we support translations for external crate errors?
167+
impl IntoDiagnosticArg for ThorinErrorWrapper {
168+
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
169+
DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self.0)))
170+
}
171+
}
172+
173+
pub struct LinkingFailed<'a> {
174+
pub linker_path: &'a PathBuf,
175+
pub exit_status: ExitStatus,
176+
pub command: &'a Command,
177+
pub escaped_output: &'a str,
178+
}
179+
180+
impl IntoDiagnostic<'_> for LinkingFailed<'_> {
181+
fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
182+
let mut diag = handler.struct_err(fluent::codegen_ssa::linking_failed);
183+
diag.set_arg("linker_path", format!("{}", self.linker_path.display()));
184+
diag.set_arg("exit_status", format!("{}", self.exit_status));
185+
186+
diag.note(format!("{:?}", self.command)).note(self.escaped_output);
187+
188+
// Trying to match an error from OS linkers
189+
// which by now we have no way to translate.
190+
if self.escaped_output.contains("undefined reference to") {
191+
diag.note(fluent::codegen_ssa::extern_funcs_not_found)
192+
.note(fluent::codegen_ssa::specify_libraries_to_link)
193+
.note(fluent::codegen_ssa::use_cargo_directive);
194+
}
195+
diag
196+
}
197+
}

compiler/rustc_codegen_ssa/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#![feature(strict_provenance)]
77
#![feature(int_roundings)]
88
#![feature(if_let_guard)]
9+
#![feature(never_type)]
910
#![recursion_limit = "256"]
1011
#![allow(rustc::potential_query_instability)]
1112

compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl

+25
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,28 @@ codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$e
2525
codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced
2626
2727
codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files were produced
28+
29+
codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error}
30+
31+
codegen_ssa_incompatible_linking_modifiers = the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs
32+
33+
codegen_ssa_add_native_library = failed to add native library {$library_path}: {$error}
34+
35+
codegen_ssa_multiple_external_func_decl = multiple declarations of external function `{$function}` from library `{$library_name}` have different calling conventions
36+
37+
codegen_ssa_rlib_missing_format = could not find formats for rlibs
38+
39+
codegen_ssa_rlib_only_rmeta_found = could not find rlib for: `{$crate_name}`, found rmeta (metadata) file
40+
41+
codegen_ssa_rlib_not_found = could not find rlib for: `{$crate_name}`
42+
43+
codegen_ssa_thorin_dwarf_linking = linking dwarf objects with thorin failed
44+
.note = {$thorin_error}
45+
46+
codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status}
47+
48+
codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
49+
50+
codegen_ssa_specify_libraries_to_link = use the `-l` flag to specify native libraries to link
51+
52+
codegen_ssa_use_cargo_directive = use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)

0 commit comments

Comments
 (0)