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"
18 #include "cmsys/Terminal.h"
20 #ifdef CMake_ENABLE_DEBUGGER
21 # include "cmDebuggerAdapter.h"
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
;
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();
60 static bool printMessagePreamble(MessageType t
, std::ostream
& msg
)
62 // Construct the message header.
63 if (t
== MessageType::FATAL_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)";
78 msg
<< "CMake Warning";
83 static int getMessageColor(MessageType 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
;
94 return cmsysTerminal_Color_Normal
;
98 static void printMessageText(std::ostream
& msg
, std::string
const& text
)
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 "
112 } else if (t
== MessageType::AUTHOR_ERROR
) {
113 msg
<< "This error is for project developers. Use -Wno-error=dev to "
117 // Add a terminating blank line.
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";
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();
140 cmSystemTools::Message(msg
.str(), md
);
142 md
.title
= "Warning";
143 cmSystemTools::Message(msg
.str(), md
);
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
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.
172 out
<< "Call Stack (most recent call first):\n";
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
186 // override the message type, if needed, for warnings and errors
187 MessageType override
= this->ConvertMessageType(t
);
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
)) {
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());
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.
230 cmListFileContext lfc
= bt
.Top();
231 if (this->TopSource
) {
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
);