Skip to content

Commit b6b0c25

Browse files
committed
Auto merge of #149937 - jyn514:linker-info, r=<try>
spliit out `linker-info` from `linker-messages` try-job: x86_64-mingw-1
2 parents f824853 + 20404bf commit b6b0c25

File tree

20 files changed

+308
-68
lines changed

20 files changed

+308
-68
lines changed

‎compiler/rustc_codegen_ssa/src/back/link.rs‎

Lines changed: 147 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use rustc_errors::DiagCtxtHandle;
2222
use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize};
2323
use rustc_hir::attrs::NativeLibKind;
2424
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
25+
use rustc_lint_defs::builtin::LINKER_INFO;
2526
use rustc_macros::Diagnostic;
2627
use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};
2728
use rustc_metadata::{
@@ -60,7 +61,8 @@ use super::rpath::{self, RPathConfig};
6061
use super::{apple, versioned_llvm_target};
6162
use crate::base::needs_allocator_shim_for_linking;
6263
use crate::{
63-
CompiledModule, CompiledModules, CrateInfo, NativeLib, errors, looks_like_rust_object_file,
64+
CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors,
65+
looks_like_rust_object_file,
6466
};
6567

6668
pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
@@ -672,6 +674,147 @@ struct LinkerOutput {
672674
inner: String,
673675
}
674676

677+
fn is_msvc_link_exe(sess: &Session) -> bool {
678+
let (linker_path, flavor) = linker_and_flavor(sess);
679+
sess.target.is_like_msvc
680+
&& flavor == LinkerFlavor::Msvc(Lld::No)
681+
// Match exactly "link.exe"
682+
&& linker_path.to_str() == Some("link.exe")
683+
}
684+
685+
fn is_macos_ld(sess: &Session) -> bool {
686+
let (_, flavor) = linker_and_flavor(sess);
687+
sess.target.is_like_darwin && matches!(flavor, LinkerFlavor::Darwin(_, Lld::No))
688+
}
689+
690+
fn is_windows_gnu_ld(sess: &Session) -> bool {
691+
let (_, flavor) = linker_and_flavor(sess);
692+
sess.target.is_like_windows
693+
&& !sess.target.is_like_msvc
694+
&& matches!(flavor, LinkerFlavor::Gnu(_, Lld::No))
695+
}
696+
697+
fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8], stderr: &[u8]) {
698+
let mut escaped_stderr = escape_string(&stderr);
699+
let mut escaped_stdout = escape_string(&stdout);
700+
let mut linker_info = String::new();
701+
702+
info!("linker stderr:\n{}", &escaped_stderr);
703+
info!("linker stdout:\n{}", &escaped_stdout);
704+
705+
fn for_each(bytes: &[u8], mut f: impl FnMut(&str, &mut String)) -> String {
706+
let mut output = String::new();
707+
if let Ok(str) = str::from_utf8(bytes) {
708+
info!("line: {str}");
709+
output = String::with_capacity(str.len());
710+
for line in str.lines() {
711+
f(line.trim(), &mut output);
712+
}
713+
}
714+
escape_string(output.trim().as_bytes())
715+
}
716+
717+
if is_msvc_link_exe(sess) {
718+
info!("inferred MSVC link.exe");
719+
720+
escaped_stdout = for_each(&stdout, |line, output| {
721+
// Hide some progress messages from link.exe that we don't care about.
722+
// See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
723+
if line.starts_with(" Creating library")
724+
|| line.starts_with("Generating code")
725+
|| line.starts_with("Finished generating code")
726+
{
727+
linker_info += line;
728+
linker_info += "\r\n";
729+
} else {
730+
*output += line;
731+
*output += "\r\n"
732+
}
733+
});
734+
} else if is_macos_ld(sess) {
735+
info!("inferred macOS LD");
736+
737+
// FIXME: Tracked by https://github.com/rust-lang/rust/issues/136113
738+
let deployment_mismatch = |line: &str| {
739+
line.starts_with("ld: warning: object file (")
740+
&& line.contains("was built for newer 'macOS' version")
741+
&& line.contains("than being linked")
742+
};
743+
// FIXME: This is a real warning we would like to show, but it hits too many crates
744+
// to want to turn it on immediately.
745+
let search_path = |line: &str| {
746+
line.starts_with("ld: warning: search path '") && line.ends_with("' not found")
747+
};
748+
escaped_stderr = for_each(&stderr, |line, output| {
749+
// This duplicate library warning is just not helpful at all.
750+
if line.starts_with("ld: warning: ignoring duplicate libraries: ")
751+
|| deployment_mismatch(line)
752+
|| search_path(line)
753+
{
754+
linker_info += line;
755+
linker_info += "\n";
756+
} else {
757+
*output += line;
758+
*output += "\n"
759+
}
760+
});
761+
} else if is_windows_gnu_ld(sess) {
762+
info!("inferred Windows GNU LD");
763+
764+
let mut saw_exclude_symbol = false;
765+
// See https://github.com/rust-lang/rust/issues/112368.
766+
// FIXME: maybe check that binutils is older than 2.40 before downgrading this warning?
767+
let exclude_symbols = |line: &str| {
768+
line.starts_with("Warning: .drectve `-exclude-symbols:")
769+
&& line.ends_with("' unrecognized")
770+
};
771+
escaped_stderr = for_each(&stderr, |line, output| {
772+
if exclude_symbols(line) {
773+
saw_exclude_symbol = true;
774+
linker_info += line;
775+
linker_info += "\n";
776+
} else if saw_exclude_symbol && line == "Warning: corrupt .drectve at end of def file" {
777+
linker_info += line;
778+
linker_info += "\n";
779+
} else {
780+
*output += line;
781+
*output += "\n"
782+
}
783+
});
784+
}
785+
786+
let lint_msg = |msg| {
787+
diag_lint_level(
788+
sess,
789+
LINKER_MESSAGES,
790+
levels.linker_messages,
791+
None,
792+
LinkerOutput { inner: msg },
793+
);
794+
};
795+
let lint_info = |msg| {
796+
diag_lint_level(sess, LINKER_INFO, levels.linker_info, None, LinkerOutput { inner: msg });
797+
};
798+
799+
if !escaped_stderr.is_empty() {
800+
// We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
801+
escaped_stderr =
802+
escaped_stderr.strip_prefix("warning: ").unwrap_or(&escaped_stderr).to_owned();
803+
// Windows GNU LD prints uppercase Warning
804+
escaped_stderr = escaped_stderr
805+
.strip_prefix("Warning: ")
806+
.unwrap_or(&escaped_stderr)
807+
.replace(": warning: ", ": ");
808+
lint_msg(format!("linker stderr: {escaped_stderr}"));
809+
}
810+
if !escaped_stdout.is_empty() {
811+
lint_msg(format!("linker stdout: {}", escaped_stdout))
812+
}
813+
if !linker_info.is_empty() {
814+
lint_info(linker_info);
815+
}
816+
}
817+
675818
/// Create a dynamic library or executable.
676819
///
677820
/// This will invoke the system linker/cc to create the resulting file. This links to all upstream
@@ -860,11 +1003,6 @@ fn link_natively(
8601003

8611004
match prog {
8621005
Ok(prog) => {
863-
let is_msvc_link_exe = sess.target.is_like_msvc
864-
&& flavor == LinkerFlavor::Msvc(Lld::No)
865-
// Match exactly "link.exe"
866-
&& linker_path.to_str() == Some("link.exe");
867-
8681006
if !prog.status.success() {
8691007
let mut output = prog.stderr.clone();
8701008
output.extend_from_slice(&prog.stdout);
@@ -884,7 +1022,7 @@ fn link_natively(
8841022
if let Some(code) = prog.status.code() {
8851023
// All Microsoft `link.exe` linking ror codes are
8861024
// four digit numbers in the range 1000 to 9999 inclusive
887-
if is_msvc_link_exe && (code < 1000 || code > 9999) {
1025+
if is_msvc_link_exe(sess) && (code < 1000 || code > 9999) {
8881026
let is_vs_installed = find_msvc_tools::find_vs_version().is_ok();
8891027
let has_linker =
8901028
find_msvc_tools::find_tool(sess.target.arch.desc(), "link.exe")
@@ -916,46 +1054,8 @@ fn link_natively(
9161054
sess.dcx().abort_if_errors();
9171055
}
9181056

919-
let stderr = escape_string(&prog.stderr);
920-
let mut stdout = escape_string(&prog.stdout);
921-
info!("linker stderr:\n{}", &stderr);
922-
info!("linker stdout:\n{}", &stdout);
923-
924-
// Hide some progress messages from link.exe that we don't care about.
925-
// See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
926-
if is_msvc_link_exe {
927-
if let Ok(str) = str::from_utf8(&prog.stdout) {
928-
let mut output = String::with_capacity(str.len());
929-
for line in stdout.lines() {
930-
if line.starts_with(" Creating library")
931-
|| line.starts_with("Generating code")
932-
|| line.starts_with("Finished generating code")
933-
{
934-
continue;
935-
}
936-
output += line;
937-
output += "\r\n"
938-
}
939-
stdout = escape_string(output.trim().as_bytes())
940-
}
941-
}
942-
943-
let level = crate_info.lint_levels.linker_messages;
944-
let lint = |msg| {
945-
diag_lint_level(sess, LINKER_MESSAGES, level, None, LinkerOutput { inner: msg });
946-
};
947-
948-
if !prog.stderr.is_empty() {
949-
// We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
950-
let stderr = stderr
951-
.strip_prefix("warning: ")
952-
.unwrap_or(&stderr)
953-
.replace(": warning: ", ": ");
954-
lint(format!("linker stderr: {stderr}"));
955-
}
956-
if !stdout.is_empty() {
957-
lint(format!("linker stdout: {}", stdout))
958-
}
1057+
info!("reporting linker output: flavor={flavor:?}");
1058+
report_linker_output(sess, crate_info.lint_levels, &prog.stdout, &prog.stderr);
9591059
}
9601060
Err(e) => {
9611061
let linker_not_found = e.kind() == io::ErrorKind::NotFound;

‎compiler/rustc_codegen_ssa/src/lib.rs‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc_data_structures::unord::UnordMap;
2424
use rustc_hir::CRATE_HIR_ID;
2525
use rustc_hir::attrs::{CfgEntry, NativeLibKind, WindowsSubsystemKind};
2626
use rustc_hir::def_id::CrateNum;
27+
use rustc_lint_defs::builtin::LINKER_INFO;
2728
use rustc_macros::{Decodable, Encodable};
2829
use rustc_metadata::EncodedMetadata;
2930
use rustc_middle::dep_graph::WorkProduct;
@@ -364,10 +365,14 @@ impl CompiledModules {
364365
#[derive(Copy, Clone, Debug, Encodable, Decodable)]
365366
pub struct CodegenLintLevels {
366367
linker_messages: LevelAndSource,
368+
linker_info: LevelAndSource,
367369
}
368370

369371
impl CodegenLintLevels {
370372
pub fn from_tcx(tcx: TyCtxt<'_>) -> Self {
371-
Self { linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID) }
373+
Self {
374+
linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID),
375+
linker_info: tcx.lint_level_at_node(LINKER_INFO, CRATE_HIR_ID),
376+
}
372377
}
373378
}

‎compiler/rustc_lint_defs/src/builtin.rs‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ declare_lint_pass! {
6161
LARGE_ASSIGNMENTS,
6262
LATE_BOUND_LIFETIME_ARGUMENTS,
6363
LEGACY_DERIVE_HELPERS,
64+
LINKER_INFO,
6465
LINKER_MESSAGES,
6566
LONG_RUNNING_CONST_EVAL,
6667
LOSSY_PROVENANCE_CASTS,
@@ -4062,6 +4063,40 @@ declare_lint! {
40624063
"warnings emitted at runtime by the target-specific linker program"
40634064
}
40644065

4066+
declare_lint! {
4067+
/// The `linker_info` lint forwards warnings from the linker that are known to be informational-only.
4068+
///
4069+
/// ### Example
4070+
///
4071+
/// ```rust,ignore (needs CLI args, platform-specific)
4072+
/// #[warn(linker_info)]
4073+
/// fn main () {}
4074+
/// ```
4075+
///
4076+
/// On MacOS, using `-C link-arg=-lc` and the default linker, this will produce
4077+
///
4078+
/// ```text
4079+
/// warning: linker stderr: ld: ignoring duplicate libraries: '-lc'
4080+
/// |
4081+
/// note: the lint level is defined here
4082+
/// --> ex.rs:1:9
4083+
/// |
4084+
/// 1 | #![warn(linker_info)]
4085+
/// | ^^^^^^^^^^^^^^^
4086+
/// ```
4087+
///
4088+
/// ### Explanation
4089+
///
4090+
/// Many linkers are very "chatty" and print lots of information that is not necessarily
4091+
/// indicative of an issue. This output has been ignored for many years and is often not
4092+
/// actionable by developers. It is silenced unless the developer specifically requests for it
4093+
/// to be printed. See this tracking issue for more details:
4094+
/// <https://github.com/rust-lang/rust/issues/136096>.
4095+
pub LINKER_INFO,
4096+
Allow,
4097+
"linker warnings known to be informational-only and not indicative of a problem"
4098+
}
4099+
40654100
declare_lint! {
40664101
/// The `named_arguments_used_positionally` lint detects cases where named arguments are only
40674102
/// used positionally in format strings. This usage is valid but potentially very confusing.

‎compiler/rustc_passes/src/check_attr.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1621,7 +1621,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
16211621
sym::expect,
16221622
]) && let Some(meta) = attr.meta_item_list()
16231623
&& meta.iter().any(|meta| {
1624-
meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
1624+
meta.meta_item().map_or(false, |item| {
1625+
item.path == sym::linker_messages || item.path == sym::linker_info
1626+
})
16251627
})
16261628
{
16271629
if hir_id != CRATE_HIR_ID {

‎compiler/rustc_passes/src/errors.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ pub(crate) enum UnusedNote {
310310
#[note("`default_method_body_is_const` has been replaced with `const` on traits")]
311311
DefaultMethodBodyConst,
312312
#[note(
313-
"the `linker_messages` lint can only be controlled at the root of a crate that needs to be linked"
313+
"the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked"
314314
)]
315315
LinkerMessagesBinaryCrateOnly,
316316
}

‎compiler/rustc_span/src/symbol.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,7 @@ symbols! {
11521152
link_section,
11531153
linkage,
11541154
linker,
1155+
linker_info,
11551156
linker_messages,
11561157
linkonce,
11571158
linkonce_odr,

‎tests/run-make/linker-warning/fake-linker.sh‎

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void foo() {}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#![warn(linker_info, linker_messages)]
2+
unsafe extern "C" {
3+
safe fn foo();
4+
}
5+
6+
fn main() {
7+
foo();
8+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//@ only-apple
2+
//! Tests that deployment target linker warnings are shown as `linker-info`, not `linker-messages`
3+
4+
use run_make_support::external_deps::c_cxx_compiler::cc;
5+
use run_make_support::external_deps::llvm::llvm_ar;
6+
use run_make_support::{bare_rustc, diff};
7+
8+
fn main() {
9+
let cwd = std::env::current_dir().unwrap().to_str().unwrap().to_owned();
10+
11+
cc().arg("-c").arg("-mmacosx-version-min=15.5").output("foo.o").input("foo.c").run();
12+
llvm_ar().obj_to_ar().output_input("libfoo.a", "foo.o").run();
13+
14+
let warnings = bare_rustc()
15+
.arg("-L")
16+
.arg(format!("native={cwd}"))
17+
.arg("-lstatic=foo")
18+
.link_arg("-mmacosx-version-min=11.2")
19+
.input("main.rs")
20+
.crate_type("bin")
21+
.run()
22+
.stderr_utf8();
23+
24+
diff()
25+
.expected_file("warnings.txt")
26+
.actual_text("(rustc -W linker-info)", &warnings)
27+
.normalize(r"\(.*/rmake_out/", "(TEST_DIR/")
28+
.run()
29+
}

0 commit comments

Comments
 (0)