From 00d3fdce7cb36a8c3fa090f797bdd093665cbe93 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 3 May 2025 09:57:20 -0700 Subject: [PATCH] Allow linking rustc and rustdoc against the same single tracing crate By consecutively initializing `tracing` and `rustc_log`, Rustdoc assumes that these involve 2 different tracing crates. I would like to be able to build rustdoc against the same tracing crate that rustc_log is also built against. Previously this arrangement would crash rustdoc: thread 'main' panicked at rust/compiler/rustc_log/src/lib.rs:142:65: called `Result::unwrap()` on an `Err` value: SetGlobalDefaultError("a global default trace dispatcher has already been set") stack backtrace: 0: rust_begin_unwind 1: core::panicking::panic_fmt 2: core::result::unwrap_failed 3: rustc_log::init_logger 4: rustc_driver_impl::init_logger 5: rustdoc::main note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. error: the compiler unexpectedly panicked. this is a bug. note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md note: please make sure that you have updated to the latest nightly query stack during panic: end of query stack --- compiler/rustc_log/src/lib.rs | 13 +++++++++++-- src/librustdoc/lib.rs | 26 +++++++++++++++++++++----- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 49dd388f14cc8..1bb502ca3d062 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -37,6 +37,7 @@ use std::env::{self, VarError}; use std::fmt::{self, Display}; use std::io::{self, IsTerminal}; +use tracing::dispatcher::SetGlobalDefaultError; use tracing_core::{Event, Subscriber}; use tracing_subscriber::filter::{Directive, EnvFilter, LevelFilter}; use tracing_subscriber::fmt::FmtContext; @@ -131,10 +132,10 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> { .without_time() .event_format(BacktraceFormatter { backtrace_target }); let subscriber = subscriber.with(fmt_layer); - tracing::subscriber::set_global_default(subscriber).unwrap(); + tracing::subscriber::set_global_default(subscriber)?; } Err(_) => { - tracing::subscriber::set_global_default(subscriber).unwrap(); + tracing::subscriber::set_global_default(subscriber)?; } }; @@ -180,6 +181,7 @@ pub enum Error { InvalidColorValue(String), NonUnicodeColorValue, InvalidWraptree(String), + AlreadyInit(SetGlobalDefaultError), } impl std::error::Error for Error {} @@ -199,6 +201,13 @@ impl Display for Error { formatter, "invalid log WRAPTREE value '{value}': expected a non-negative integer", ), + Error::AlreadyInit(tracing_error) => Display::fmt(tracing_error, formatter), } } } + +impl From for Error { + fn from(tracing_error: SetGlobalDefaultError) -> Self { + Error::AlreadyInit(tracing_error) + } +} diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 44bd96a7e4503..bca40b8117bdb 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -170,12 +170,28 @@ pub fn main() { // NOTE: this compiles both versions of tracing unconditionally, because // - The compile time hit is not that bad, especially compared to rustdoc's incremental times, and // - Otherwise, there's no warning that logging is being ignored when `download-rustc` is enabled - // NOTE: The reason this doesn't show double logging when `download-rustc = false` and - // `debug_logging = true` is because all rustc logging goes to its version of tracing (the one - // in the sysroot), and all of rustdoc's logging goes to its version (the one in Cargo.toml). - init_logging(&early_dcx); - rustc_driver::init_logger(&early_dcx, rustc_log::LoggerConfig::from_env("RUSTDOC_LOG")); + crate::init_logging(&early_dcx); + match rustc_log::init_logger(rustc_log::LoggerConfig::from_env("RUSTDOC_LOG")) { + Ok(()) => {} + // With `download-rustc = true` there are definitely 2 distinct tracing crates in the + // dependency graph: one in the downloaded sysroot and one built just now as a dependency of + // rustdoc. So the sysroot's tracing is definitely not yet initialized here. + // + // But otherwise, depending on link style, there may or may not be 2 tracing crates in play. + // The one we just initialized in `crate::init_logging` above is rustdoc's direct dependency + // on tracing. When rustdoc is built by x.py using Cargo, rustc_driver's and rustc_log's + // tracing dependency is distinct from this one and also needs to be initialized (using the + // same RUSTDOC_LOG environment variable for both). Other build systems may use just a + // single tracing crate throughout the rustc and rustdoc build. + // + // The reason initializing 2 tracings does not show double logging when `download-rustc = + // false` and `debug_logging = true` is because all rustc logging goes only to its version + // of tracing (the one in the sysroot) and all of rustdoc's logging only goes to its version + // (the one in Cargo.toml). + Err(rustc_log::Error::AlreadyInit(_)) => {} + Err(error) => early_dcx.early_fatal(error.to_string()), + } let exit_code = rustc_driver::catch_with_exit_code(|| { let at_args = rustc_driver::args::raw_args(&early_dcx);