From 42c7a97446e010df89f14f2cd58d5365e683a56e Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Wed, 6 May 2026 15:51:07 -0400 Subject: [PATCH] Diagnostics: Add class to record state Create new cmDiagnosticContext used to capture the diagnostic state for instances when the issuing of a diagnostic might happen at a point far separated from processing the CMake command ultimately responsible for the diagnostic. (This is particularly the case for diagnostics that get emitted during the generation phase.) In such cases, it is preferred that we record the diagnostic state that pertained when the ultimately instigating command was processed, so that users can control the diagnostic in a natural manner. Copying the entire cmStateSnapshot is unreasonably expensive; this will provide a lighter-weight mechanism for capturing the relevant diagnostic state for later use. Since it is almost universal that the points where we would capture this state also capture a stack trace (which is likewise used to issue diagnostics), the new class contains a stack trace, and replaces the stack trace in some diagnostic calls. (In some instances, an overload which implicitly converts a stack trace to the new context class is retained for convenience. These will behave the same, since capturing diagnostic state is an optional function of the new class.) --- Source/CMakeLists.txt | 2 ++ Source/cmDiagnosticContext.cxx | 14 ++++++++++++ Source/cmDiagnosticContext.h | 39 ++++++++++++++++++++++++++++++++++ Source/cmFLTKWrapUICommand.cxx | 2 +- Source/cmLocalGenerator.cxx | 8 +++---- Source/cmLocalGenerator.h | 11 ++++++++-- Source/cmMakefile.cxx | 4 ++-- Source/cmMakefile.h | 12 +++++++++-- Source/cmMessenger.cxx | 32 +++++++++++++++++++++++----- Source/cmMessenger.h | 8 +++---- Source/cmake.cxx | 4 ++-- Source/cmake.h | 21 +++++++++++------- bootstrap | 1 + 13 files changed, 128 insertions(+), 30 deletions(-) create mode 100644 Source/cmDiagnosticContext.cxx create mode 100644 Source/cmDiagnosticContext.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index a5910ef815..231d0704ad 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -189,6 +189,8 @@ add_library( cmDependsJavaParserHelper.h cmDependsCompiler.cxx cmDependsCompiler.h + cmDiagnosticContext.h + cmDiagnosticContext.cxx cmDiagnostics.h cmDiagnostics.cxx cmDocumentation.cxx diff --git a/Source/cmDiagnosticContext.cxx b/Source/cmDiagnosticContext.cxx new file mode 100644 index 0000000000..665201f03d --- /dev/null +++ b/Source/cmDiagnosticContext.cxx @@ -0,0 +1,14 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmDiagnosticContext.h" + +#include + +#include "cmStateSnapshot.h" + +void cmDiagnosticContext::RecordDiagnostic(cmDiagnosticCategory category, + cmStateSnapshot const& state) +{ + this->DiagnosticState[category] = state.GetDiagnostic(category); + this->HasState = true; +} diff --git a/Source/cmDiagnosticContext.h b/Source/cmDiagnosticContext.h new file mode 100644 index 0000000000..9f7224404f --- /dev/null +++ b/Source/cmDiagnosticContext.h @@ -0,0 +1,39 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include + +#include "cmDiagnostics.h" +#include "cmListFileCache.h" + +class cmStateSnapshot; + +/** \class cmDiagnosticContext + * \brief Records context for issuing diagnostics + */ +class cmDiagnosticContext +{ +public: + explicit cmDiagnosticContext(cmListFileBacktrace backtrace) + : Backtrace{ std::move(backtrace) } + { + } + cmDiagnosticContext() = default; + cmDiagnosticContext(cmDiagnosticContext&&) = default; + cmDiagnosticContext(cmDiagnosticContext const&) = default; + + void RecordDiagnostic(cmDiagnosticCategory category, + cmStateSnapshot const& state); + + cmListFileBacktrace const& GetBacktrace() const { return this->Backtrace; } + +protected: + friend class cmMessenger; + + cmListFileBacktrace Backtrace; + cmDiagnostics::DiagnosticMap DiagnosticState; + bool HasState = false; +}; diff --git a/Source/cmFLTKWrapUICommand.cxx b/Source/cmFLTKWrapUICommand.cxx index b43eb67943..b0ef2d01a7 100644 --- a/Source/cmFLTKWrapUICommand.cxx +++ b/Source/cmFLTKWrapUICommand.cxx @@ -12,6 +12,7 @@ #include "cmCustomCommandLines.h" #include "cmDiagnostics.h" #include "cmExecutionStatus.h" +#include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmRange.h" @@ -19,7 +20,6 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -class cmListFileBacktrace; class cmTarget; static void FinalAction(cmMakefile& makefile, std::string const& name, diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 03dceb744e..fbc4253791 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -246,11 +246,11 @@ void cmLocalGenerator::IssueMessage(MessageType type, std::string const& text, this->GetMakefile()->IssueMessage(type, text, bt); } -void cmLocalGenerator::IssueDiagnostic(cmDiagnosticCategory category, - std::string const& text, - cmListFileBacktrace const& bt) const +void cmLocalGenerator::IssueDiagnostic( + cmDiagnosticCategory category, std::string const& text, + cmDiagnosticContext const& context) const { - this->GetMakefile()->IssueDiagnostic(category, text, bt); + this->GetMakefile()->IssueDiagnostic(category, text, context); } void cmLocalGenerator::ComputeObjectMaxPath() diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 77f1e30a0f..7a0227e712 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -17,6 +17,7 @@ #include #include "cmCustomCommandTypes.h" +#include "cmDiagnosticContext.h" #include "cmDiagnostics.h" #include "cmGeneratorOptions.h" #include "cmGeneratorTarget.h" @@ -581,10 +582,16 @@ public: void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text) const { - this->IssueDiagnostic(category, text, this->DirectoryBacktrace); + this->IssueDiagnostic(category, text, + cmDiagnosticContext{ this->DirectoryBacktrace }); } void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, - cmListFileBacktrace const& bt) const; + cmListFileBacktrace const& bt) const + { + this->IssueDiagnostic(category, text, cmDiagnosticContext{ bt }); + } + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmDiagnosticContext const& context) const; void CreateEvaluationFileOutputs(); void CreateEvaluationFileOutputs(std::string const& config); diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index e2b454959b..b5a02fe34b 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -228,7 +228,7 @@ void cmMakefile::IssueMessage(MessageType t, std::string const& text, void cmMakefile::IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, - cmListFileBacktrace const& bt) const + cmDiagnosticContext const& context) const { if (!this->ExecutionStatusStack.empty()) { cmDiagnosticAction const action = this->GetDiagnosticAction(category); @@ -237,7 +237,7 @@ void cmMakefile::IssueDiagnostic(cmDiagnosticCategory category, } } this->GetCMakeInstance()->IssueDiagnostic(category, text, - this->GetStateSnapshot(), bt); + this->GetStateSnapshot(), context); } Message::LogLevel cmMakefile::GetCurrentLogLevel() const diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 41fff122c8..c8e062ad2d 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -25,6 +25,7 @@ #include "cmAlgorithms.h" #include "cmCustomCommand.h" +#include "cmDiagnosticContext.h" #include "cmDiagnostics.h" #include "cmFindPackageStack.h" #include "cmFunctionBlocker.h" @@ -1047,10 +1048,17 @@ public: void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text) const { - this->IssueDiagnostic(category, text, this->Backtrace); + this->IssueDiagnostic(category, text, + cmDiagnosticContext{ this->Backtrace }); } void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, - cmListFileBacktrace const& bt) const; + cmListFileBacktrace backtrace) const + { + this->IssueDiagnostic(category, text, + cmDiagnosticContext{ std::move(backtrace) }); + } + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmDiagnosticContext const& context) const; Message::LogLevel GetCurrentLogLevel() const; /** Set whether or not to report a CMP0000 violation. */ diff --git a/Source/cmMessenger.cxx b/Source/cmMessenger.cxx index cbfe69f3f8..ebe24166c1 100644 --- a/Source/cmMessenger.cxx +++ b/Source/cmMessenger.cxx @@ -7,6 +7,8 @@ #include #include +#include + #include "cmDocumentationFormatter.h" #include "cmMessageMetadata.h" #include "cmMessageType.h" @@ -161,10 +163,29 @@ void cmMessenger::IssueMessage(MessageType t, std::string const& text, void cmMessenger::IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, - cmStateSnapshot const& context, - cmListFileBacktrace const& backtrace) const + cmStateSnapshot const& fallbackContext, + cmDiagnosticContext const& context) const { - cmDiagnosticAction const action = context.GetDiagnostic(category); + cmDiagnosticAction const action = [&] { + if (context.HasState) { + cmDiagnosticAction const ca = context.DiagnosticState[category]; + if (ca != cmDiagnostics::Undefined) { + return ca; + } + + // If the context has recorded states, but not the state we want, this + // implies that we had the opportunity to record the state and failed to + // do so. Ask users to report this. + std::string msg = + cmStrCat("Stored diagnostic context did not record state for "_s, + cmDiagnostics::GetCategoryString(category), + ". Please report this as a bug.\n"_s); + this->IssueMessage(MessageType::LOG, msg, context.GetBacktrace()); + } + + return fallbackContext.GetDiagnostic(category); + }(); + switch (action) { case cmDiagnostics::FatalError: cmSystemTools::SetFatalErrorOccurred(); @@ -172,10 +193,11 @@ void cmMessenger::IssueDiagnostic(cmDiagnosticCategory category, case cmDiagnostics::SendError: cmSystemTools::SetErrorOccurred(); this->DisplayMessage(MessageType::FATAL_ERROR, category, text, - backtrace); + context.GetBacktrace()); break; case cmDiagnostics::Warn: - this->DisplayMessage(MessageType::WARNING, category, text, backtrace); + this->DisplayMessage(MessageType::WARNING, category, text, + context.GetBacktrace()); break; default: return; diff --git a/Source/cmMessenger.h b/Source/cmMessenger.h index 17e09b7863..60f736b864 100644 --- a/Source/cmMessenger.h +++ b/Source/cmMessenger.h @@ -10,6 +10,7 @@ #include +#include "cmDiagnosticContext.h" #include "cmDiagnostics.h" #include "cmListFileCache.h" #include "cmMessageType.h" // IWYU pragma: keep @@ -33,10 +34,9 @@ public: MessageType type, std::string const& text, cmListFileBacktrace const& backtrace = cmListFileBacktrace()) const; - void IssueDiagnostic( - cmDiagnosticCategory category, std::string const& text, - cmStateSnapshot const& context, - cmListFileBacktrace const& backtrace = cmListFileBacktrace()) const; + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmStateSnapshot const& fallbackContext, + cmDiagnosticContext const& context = {}) const; void DisplayMessage(MessageType type, cmDiagnosticCategory category, std::string const& text, diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 2918cd1136..c26ff3f0cf 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -3894,9 +3894,9 @@ void cmake::IssueMessage(MessageType t, std::string const& text, void cmake::IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, cmStateSnapshot const& state, - cmListFileBacktrace const& backtrace) const + cmDiagnosticContext const& context) const { - this->Messenger->IssueDiagnostic(category, text, state, backtrace); + this->Messenger->IssueDiagnostic(category, text, state, context); } std::vector cmake::GetDebugConfigs() diff --git a/Source/cmake.h b/Source/cmake.h index 900cbb44f1..6344339e4f 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -19,6 +19,7 @@ #include #include "cmBuildArgs.h" +#include "cmDiagnosticContext.h" #include "cmDiagnostics.h" #include "cmDocumentationEntry.h" // IWYU pragma: keep #include "cmGeneratedFileStream.h" @@ -605,15 +606,19 @@ public: void IssueMessage( MessageType t, std::string const& text, cmListFileBacktrace const& backtrace = cmListFileBacktrace()) const; - void IssueDiagnostic( - cmDiagnosticCategory category, std::string const& text, - cmStateSnapshot const& state, - cmListFileBacktrace const& backtrace = cmListFileBacktrace()) const; - void IssueDiagnostic( - cmDiagnosticCategory category, std::string const& text, - cmListFileBacktrace const& backtrace = cmListFileBacktrace()) const + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmStateSnapshot const& state, + cmDiagnosticContext const& context = {}) const; + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmDiagnosticContext const& context = {}) const { - this->IssueDiagnostic(category, text, this->CurrentSnapshot, backtrace); + this->IssueDiagnostic(category, text, this->CurrentSnapshot, context); + } + void IssueDiagnostic(cmDiagnosticCategory category, std::string const& text, + cmListFileBacktrace backtrace) const + { + this->IssueDiagnostic(category, text, this->CurrentSnapshot, + cmDiagnosticContext{ std::move(backtrace) }); } //! run the --build option diff --git a/bootstrap b/bootstrap index 56ef84d0ea..2739a0ba16 100755 --- a/bootstrap +++ b/bootstrap @@ -314,6 +314,7 @@ CMAKE_CXX_SOURCES="\ cmDefinePropertyCommand \ cmDefinitions \ cmDiagnostics \ + cmDiagnosticContext \ cmDiscoverTestsCommand \ cmDocumentationFormatter \ cmELF \