| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmMessenger.h" |
| |
| #include "cmDocumentationFormatter.h" |
| #include "cmMessageMetadata.h" |
| #include "cmStringAlgorithms.h" |
| #include "cmSystemTools.h" |
| |
| #if !defined(CMAKE_BOOTSTRAP) |
| # include "cmsys/SystemInformation.hxx" |
| #endif |
| |
| #include <sstream> |
| #include <utility> |
| |
| #include "cmsys/Terminal.h" |
| |
| MessageType cmMessenger::ConvertMessageType(MessageType t) const |
| { |
| bool warningsAsErrors; |
| |
| if (t == MessageType::AUTHOR_WARNING || t == MessageType::AUTHOR_ERROR) { |
| warningsAsErrors = this->GetDevWarningsAsErrors(); |
| if (warningsAsErrors && t == MessageType::AUTHOR_WARNING) { |
| t = MessageType::AUTHOR_ERROR; |
| } else if (!warningsAsErrors && t == MessageType::AUTHOR_ERROR) { |
| t = MessageType::AUTHOR_WARNING; |
| } |
| } else if (t == MessageType::DEPRECATION_WARNING || |
| t == MessageType::DEPRECATION_ERROR) { |
| warningsAsErrors = this->GetDeprecatedWarningsAsErrors(); |
| if (warningsAsErrors && t == MessageType::DEPRECATION_WARNING) { |
| t = MessageType::DEPRECATION_ERROR; |
| } else if (!warningsAsErrors && t == MessageType::DEPRECATION_ERROR) { |
| t = MessageType::DEPRECATION_WARNING; |
| } |
| } |
| |
| return t; |
| } |
| |
| bool cmMessenger::IsMessageTypeVisible(MessageType t) const |
| { |
| bool isVisible = true; |
| |
| if (t == MessageType::DEPRECATION_ERROR) { |
| if (!this->GetDeprecatedWarningsAsErrors()) { |
| isVisible = false; |
| } |
| } else if (t == MessageType::DEPRECATION_WARNING) { |
| if (this->GetSuppressDeprecatedWarnings()) { |
| isVisible = false; |
| } |
| } else if (t == MessageType::AUTHOR_ERROR) { |
| if (!this->GetDevWarningsAsErrors()) { |
| isVisible = false; |
| } |
| } else if (t == MessageType::AUTHOR_WARNING) { |
| if (this->GetSuppressDevWarnings()) { |
| isVisible = false; |
| } |
| } |
| |
| return isVisible; |
| } |
| |
| static bool printMessagePreamble(MessageType t, std::ostream& msg) |
| { |
| // Construct the message header. |
| if (t == MessageType::FATAL_ERROR) { |
| msg << "CMake Error"; |
| } else if (t == MessageType::INTERNAL_ERROR) { |
| msg << "CMake Internal Error (please report a bug)"; |
| } else if (t == MessageType::LOG) { |
| msg << "CMake Debug Log"; |
| } else if (t == MessageType::DEPRECATION_ERROR) { |
| msg << "CMake Deprecation Error"; |
| } else if (t == MessageType::DEPRECATION_WARNING) { |
| msg << "CMake Deprecation Warning"; |
| } else if (t == MessageType::AUTHOR_WARNING) { |
| msg << "CMake Warning (dev)"; |
| } else if (t == MessageType::AUTHOR_ERROR) { |
| msg << "CMake Error (dev)"; |
| } else { |
| msg << "CMake Warning"; |
| } |
| return true; |
| } |
| |
| static int getMessageColor(MessageType t) |
| { |
| switch (t) { |
| case MessageType::INTERNAL_ERROR: |
| case MessageType::FATAL_ERROR: |
| case MessageType::AUTHOR_ERROR: |
| return cmsysTerminal_Color_ForegroundRed; |
| case MessageType::AUTHOR_WARNING: |
| case MessageType::WARNING: |
| return cmsysTerminal_Color_ForegroundYellow; |
| default: |
| return cmsysTerminal_Color_Normal; |
| } |
| } |
| |
| static void printMessageText(std::ostream& msg, std::string const& text) |
| { |
| msg << ":\n"; |
| cmDocumentationFormatter formatter; |
| formatter.SetIndent(" "); |
| formatter.PrintFormatted(msg, text.c_str()); |
| } |
| |
| static void displayMessage(MessageType t, std::ostringstream& msg) |
| { |
| // Add a note about warning suppression. |
| if (t == MessageType::AUTHOR_WARNING) { |
| msg << "This warning is for project developers. Use -Wno-dev to suppress " |
| "it."; |
| } else if (t == MessageType::AUTHOR_ERROR) { |
| msg << "This error is for project developers. Use -Wno-error=dev to " |
| "suppress it."; |
| } |
| |
| // Add a terminating blank line. |
| msg << "\n"; |
| |
| #if !defined(CMAKE_BOOTSTRAP) |
| // Add a C++ stack trace to internal errors. |
| if (t == MessageType::INTERNAL_ERROR) { |
| std::string stack = cmsys::SystemInformation::GetProgramStack(0, 0); |
| if (!stack.empty()) { |
| if (cmHasLiteralPrefix(stack, "WARNING:")) { |
| stack = "Note:" + stack.substr(8); |
| } |
| msg << stack << "\n"; |
| } |
| } |
| #endif |
| |
| // Output the message. |
| cmMessageMetadata md; |
| md.desiredColor = getMessageColor(t); |
| if (t == MessageType::FATAL_ERROR || t == MessageType::INTERNAL_ERROR || |
| t == MessageType::DEPRECATION_ERROR || t == MessageType::AUTHOR_ERROR) { |
| cmSystemTools::SetErrorOccured(); |
| md.title = "Error"; |
| cmSystemTools::Message(msg.str(), md); |
| } else { |
| md.title = "Warning"; |
| cmSystemTools::Message(msg.str(), md); |
| } |
| } |
| |
| namespace { |
| void PrintCallStack(std::ostream& out, cmListFileBacktrace bt, |
| cm::optional<std::string> const& topSource) |
| { |
| // The call stack exists only if we have at least two calls on top |
| // of the bottom. |
| if (bt.Empty()) { |
| return; |
| } |
| bt = bt.Pop(); |
| if (bt.Empty()) { |
| return; |
| } |
| |
| bool first = true; |
| for (; !bt.Empty(); bt = bt.Pop()) { |
| cmListFileContext lfc = bt.Top(); |
| if (lfc.Name.empty() && |
| lfc.Line != cmListFileContext::DeferPlaceholderLine) { |
| // Skip this whole-file scope. When we get here we already will |
| // have printed a more-specific context within the file. |
| continue; |
| } |
| if (first) { |
| first = false; |
| out << "Call Stack (most recent call first):\n"; |
| } |
| if (topSource) { |
| lfc.FilePath = cmSystemTools::RelativeIfUnder(*topSource, lfc.FilePath); |
| } |
| out << " " << lfc << "\n"; |
| } |
| } |
| } |
| |
| void cmMessenger::IssueMessage(MessageType t, const std::string& text, |
| const cmListFileBacktrace& backtrace) const |
| { |
| bool force = false; |
| // override the message type, if needed, for warnings and errors |
| MessageType override = this->ConvertMessageType(t); |
| if (override != t) { |
| t = override; |
| force = true; |
| } |
| |
| if (force || this->IsMessageTypeVisible(t)) { |
| this->DisplayMessage(t, text, backtrace); |
| } |
| } |
| |
| void cmMessenger::DisplayMessage(MessageType t, const std::string& text, |
| const cmListFileBacktrace& backtrace) const |
| { |
| std::ostringstream msg; |
| if (!printMessagePreamble(t, msg)) { |
| return; |
| } |
| |
| // Add the immediate context. |
| this->PrintBacktraceTitle(msg, backtrace); |
| |
| printMessageText(msg, text); |
| |
| // Add the rest of the context. |
| PrintCallStack(msg, backtrace, this->TopSource); |
| |
| displayMessage(t, msg); |
| } |
| |
| void cmMessenger::PrintBacktraceTitle(std::ostream& out, |
| cmListFileBacktrace const& bt) const |
| { |
| // The title exists only if we have a call on top of the bottom. |
| if (bt.Empty()) { |
| return; |
| } |
| cmListFileContext lfc = bt.Top(); |
| if (this->TopSource) { |
| lfc.FilePath = |
| cmSystemTools::RelativeIfUnder(*this->TopSource, lfc.FilePath); |
| } |
| out << (lfc.Line ? " at " : " in ") << lfc; |
| } |
| |
| void cmMessenger::SetTopSource(cm::optional<std::string> topSource) |
| { |
| this->TopSource = std::move(topSource); |
| } |