Merge branch 'release-3.29'
[kiteware-cmake.git] / Source / cmMessenger.cxx
blobb4ea71cf450b291b379a36a563f72b4100721122
1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmMessenger.h"
5 #include "cmDocumentationFormatter.h"
6 #include "cmMessageMetadata.h"
7 #include "cmMessageType.h"
8 #include "cmStringAlgorithms.h"
9 #include "cmSystemTools.h"
11 #if !defined(CMAKE_BOOTSTRAP)
12 # include "cmsys/SystemInformation.hxx"
13 #endif
15 #include <sstream>
16 #include <utility>
18 #include "cmsys/Terminal.h"
20 #ifdef CMake_ENABLE_DEBUGGER
21 # include "cmDebuggerAdapter.h"
22 #endif
24 MessageType cmMessenger::ConvertMessageType(MessageType t) const
26 if (t == MessageType::AUTHOR_WARNING || t == MessageType::AUTHOR_ERROR) {
27 if (this->GetDevWarningsAsErrors()) {
28 return MessageType::AUTHOR_ERROR;
30 return MessageType::AUTHOR_WARNING;
32 if (t == MessageType::DEPRECATION_WARNING ||
33 t == MessageType::DEPRECATION_ERROR) {
34 if (this->GetDeprecatedWarningsAsErrors()) {
35 return MessageType::DEPRECATION_ERROR;
37 return MessageType::DEPRECATION_WARNING;
39 return t;
42 bool cmMessenger::IsMessageTypeVisible(MessageType t) const
44 if (t == MessageType::DEPRECATION_ERROR) {
45 return this->GetDeprecatedWarningsAsErrors();
47 if (t == MessageType::DEPRECATION_WARNING) {
48 return !this->GetSuppressDeprecatedWarnings();
50 if (t == MessageType::AUTHOR_ERROR) {
51 return this->GetDevWarningsAsErrors();
53 if (t == MessageType::AUTHOR_WARNING) {
54 return !this->GetSuppressDevWarnings();
57 return true;
60 static bool printMessagePreamble(MessageType t, std::ostream& msg)
62 // Construct the message header.
63 if (t == MessageType::FATAL_ERROR) {
64 msg << "CMake Error";
65 } else if (t == MessageType::INTERNAL_ERROR) {
66 msg << "CMake Internal Error (please report a bug)";
67 } else if (t == MessageType::LOG) {
68 msg << "CMake Debug Log";
69 } else if (t == MessageType::DEPRECATION_ERROR) {
70 msg << "CMake Deprecation Error";
71 } else if (t == MessageType::DEPRECATION_WARNING) {
72 msg << "CMake Deprecation Warning";
73 } else if (t == MessageType::AUTHOR_WARNING) {
74 msg << "CMake Warning (dev)";
75 } else if (t == MessageType::AUTHOR_ERROR) {
76 msg << "CMake Error (dev)";
77 } else {
78 msg << "CMake Warning";
80 return true;
83 static int getMessageColor(MessageType t)
85 switch (t) {
86 case MessageType::INTERNAL_ERROR:
87 case MessageType::FATAL_ERROR:
88 case MessageType::AUTHOR_ERROR:
89 return cmsysTerminal_Color_ForegroundRed;
90 case MessageType::AUTHOR_WARNING:
91 case MessageType::WARNING:
92 return cmsysTerminal_Color_ForegroundYellow;
93 default:
94 return cmsysTerminal_Color_Normal;
98 static void printMessageText(std::ostream& msg, std::string const& text)
100 msg << ":\n";
101 cmDocumentationFormatter formatter;
102 formatter.SetIndent(2u);
103 formatter.PrintFormatted(msg, text);
106 static void displayMessage(MessageType t, std::ostringstream& msg)
108 // Add a note about warning suppression.
109 if (t == MessageType::AUTHOR_WARNING) {
110 msg << "This warning is for project developers. Use -Wno-dev to suppress "
111 "it.";
112 } else if (t == MessageType::AUTHOR_ERROR) {
113 msg << "This error is for project developers. Use -Wno-error=dev to "
114 "suppress it.";
117 // Add a terminating blank line.
118 msg << "\n";
120 #if !defined(CMAKE_BOOTSTRAP)
121 // Add a C++ stack trace to internal errors.
122 if (t == MessageType::INTERNAL_ERROR) {
123 std::string stack = cmsys::SystemInformation::GetProgramStack(0, 0);
124 if (!stack.empty()) {
125 if (cmHasLiteralPrefix(stack, "WARNING:")) {
126 stack = "Note:" + stack.substr(8);
128 msg << stack << "\n";
131 #endif
133 // Output the message.
134 cmMessageMetadata md;
135 md.desiredColor = getMessageColor(t);
136 if (t == MessageType::FATAL_ERROR || t == MessageType::INTERNAL_ERROR ||
137 t == MessageType::DEPRECATION_ERROR || t == MessageType::AUTHOR_ERROR) {
138 cmSystemTools::SetErrorOccurred();
139 md.title = "Error";
140 cmSystemTools::Message(msg.str(), md);
141 } else {
142 md.title = "Warning";
143 cmSystemTools::Message(msg.str(), md);
147 namespace {
148 void PrintCallStack(std::ostream& out, cmListFileBacktrace bt,
149 cm::optional<std::string> const& topSource)
151 // The call stack exists only if we have at least two calls on top
152 // of the bottom.
153 if (bt.Empty()) {
154 return;
156 bt = bt.Pop();
157 if (bt.Empty()) {
158 return;
161 bool first = true;
162 for (; !bt.Empty(); bt = bt.Pop()) {
163 cmListFileContext lfc = bt.Top();
164 if (lfc.Name.empty() &&
165 lfc.Line != cmListFileContext::DeferPlaceholderLine) {
166 // Skip this whole-file scope. When we get here we already will
167 // have printed a more-specific context within the file.
168 continue;
170 if (first) {
171 first = false;
172 out << "Call Stack (most recent call first):\n";
174 if (topSource) {
175 lfc.FilePath = cmSystemTools::RelativeIfUnder(*topSource, lfc.FilePath);
177 out << " " << lfc << "\n";
182 void cmMessenger::IssueMessage(MessageType t, const std::string& text,
183 const cmListFileBacktrace& backtrace) const
185 bool force = false;
186 // override the message type, if needed, for warnings and errors
187 MessageType override = this->ConvertMessageType(t);
188 if (override != t) {
189 t = override;
190 force = true;
193 if (force || this->IsMessageTypeVisible(t)) {
194 this->DisplayMessage(t, text, backtrace);
198 void cmMessenger::DisplayMessage(MessageType t, const std::string& text,
199 const cmListFileBacktrace& backtrace) const
201 std::ostringstream msg;
202 if (!printMessagePreamble(t, msg)) {
203 return;
206 // Add the immediate context.
207 this->PrintBacktraceTitle(msg, backtrace);
209 printMessageText(msg, text);
211 // Add the rest of the context.
212 PrintCallStack(msg, backtrace, this->TopSource);
214 displayMessage(t, msg);
216 #ifdef CMake_ENABLE_DEBUGGER
217 if (DebuggerAdapter != nullptr) {
218 DebuggerAdapter->OnMessageOutput(t, msg.str());
220 #endif
223 void cmMessenger::PrintBacktraceTitle(std::ostream& out,
224 cmListFileBacktrace const& bt) const
226 // The title exists only if we have a call on top of the bottom.
227 if (bt.Empty()) {
228 return;
230 cmListFileContext lfc = bt.Top();
231 if (this->TopSource) {
232 lfc.FilePath =
233 cmSystemTools::RelativeIfUnder(*this->TopSource, lfc.FilePath);
235 out << (lfc.Line ? " at " : " in ") << lfc;
238 void cmMessenger::SetTopSource(cm::optional<std::string> topSource)
240 this->TopSource = std::move(topSource);