cmMessenger.cxx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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"
  4. #include "cmDocumentationFormatter.h"
  5. #include "cmMessageMetadata.h"
  6. #include "cmState.h"
  7. #include "cmStateSnapshot.h"
  8. #include "cmStringAlgorithms.h"
  9. #include "cmSystemTools.h"
  10. #if !defined(CMAKE_BOOTSTRAP)
  11. # include "cmsys/SystemInformation.hxx"
  12. #endif
  13. #include <sstream>
  14. #include "cmsys/Terminal.h"
  15. MessageType cmMessenger::ConvertMessageType(MessageType t) const
  16. {
  17. bool warningsAsErrors;
  18. if (t == MessageType::AUTHOR_WARNING || t == MessageType::AUTHOR_ERROR) {
  19. warningsAsErrors = this->GetDevWarningsAsErrors();
  20. if (warningsAsErrors && t == MessageType::AUTHOR_WARNING) {
  21. t = MessageType::AUTHOR_ERROR;
  22. } else if (!warningsAsErrors && t == MessageType::AUTHOR_ERROR) {
  23. t = MessageType::AUTHOR_WARNING;
  24. }
  25. } else if (t == MessageType::DEPRECATION_WARNING ||
  26. t == MessageType::DEPRECATION_ERROR) {
  27. warningsAsErrors = this->GetDeprecatedWarningsAsErrors();
  28. if (warningsAsErrors && t == MessageType::DEPRECATION_WARNING) {
  29. t = MessageType::DEPRECATION_ERROR;
  30. } else if (!warningsAsErrors && t == MessageType::DEPRECATION_ERROR) {
  31. t = MessageType::DEPRECATION_WARNING;
  32. }
  33. }
  34. return t;
  35. }
  36. bool cmMessenger::IsMessageTypeVisible(MessageType t) const
  37. {
  38. bool isVisible = true;
  39. if (t == MessageType::DEPRECATION_ERROR) {
  40. if (!this->GetDeprecatedWarningsAsErrors()) {
  41. isVisible = false;
  42. }
  43. } else if (t == MessageType::DEPRECATION_WARNING) {
  44. if (this->GetSuppressDeprecatedWarnings()) {
  45. isVisible = false;
  46. }
  47. } else if (t == MessageType::AUTHOR_ERROR) {
  48. if (!this->GetDevWarningsAsErrors()) {
  49. isVisible = false;
  50. }
  51. } else if (t == MessageType::AUTHOR_WARNING) {
  52. if (this->GetSuppressDevWarnings()) {
  53. isVisible = false;
  54. }
  55. }
  56. return isVisible;
  57. }
  58. static bool printMessagePreamble(MessageType t, std::ostream& msg)
  59. {
  60. // Construct the message header.
  61. if (t == MessageType::FATAL_ERROR) {
  62. msg << "CMake Error";
  63. } else if (t == MessageType::INTERNAL_ERROR) {
  64. msg << "CMake Internal Error (please report a bug)";
  65. } else if (t == MessageType::LOG) {
  66. msg << "CMake Debug Log";
  67. } else if (t == MessageType::DEPRECATION_ERROR) {
  68. msg << "CMake Deprecation Error";
  69. } else if (t == MessageType::DEPRECATION_WARNING) {
  70. msg << "CMake Deprecation Warning";
  71. } else if (t == MessageType::AUTHOR_WARNING) {
  72. msg << "CMake Warning (dev)";
  73. } else if (t == MessageType::AUTHOR_ERROR) {
  74. msg << "CMake Error (dev)";
  75. } else {
  76. msg << "CMake Warning";
  77. }
  78. return true;
  79. }
  80. static int getMessageColor(MessageType t)
  81. {
  82. switch (t) {
  83. case MessageType::INTERNAL_ERROR:
  84. case MessageType::FATAL_ERROR:
  85. case MessageType::AUTHOR_ERROR:
  86. return cmsysTerminal_Color_ForegroundRed;
  87. case MessageType::AUTHOR_WARNING:
  88. case MessageType::WARNING:
  89. return cmsysTerminal_Color_ForegroundYellow;
  90. default:
  91. return cmsysTerminal_Color_Normal;
  92. }
  93. }
  94. static void printMessageText(std::ostream& msg, std::string const& text)
  95. {
  96. msg << ":\n";
  97. cmDocumentationFormatter formatter;
  98. formatter.SetIndent(" ");
  99. formatter.PrintFormatted(msg, text.c_str());
  100. }
  101. static void displayMessage(MessageType t, std::ostringstream& msg)
  102. {
  103. // Add a note about warning suppression.
  104. if (t == MessageType::AUTHOR_WARNING) {
  105. msg << "This warning is for project developers. Use -Wno-dev to suppress "
  106. "it.";
  107. } else if (t == MessageType::AUTHOR_ERROR) {
  108. msg << "This error is for project developers. Use -Wno-error=dev to "
  109. "suppress it.";
  110. }
  111. // Add a terminating blank line.
  112. msg << "\n";
  113. #if !defined(CMAKE_BOOTSTRAP)
  114. // Add a C++ stack trace to internal errors.
  115. if (t == MessageType::INTERNAL_ERROR) {
  116. std::string stack = cmsys::SystemInformation::GetProgramStack(0, 0);
  117. if (!stack.empty()) {
  118. if (cmHasLiteralPrefix(stack, "WARNING:")) {
  119. stack = "Note:" + stack.substr(8);
  120. }
  121. msg << stack << "\n";
  122. }
  123. }
  124. #endif
  125. // Output the message.
  126. cmMessageMetadata md;
  127. md.desiredColor = getMessageColor(t);
  128. if (t == MessageType::FATAL_ERROR || t == MessageType::INTERNAL_ERROR ||
  129. t == MessageType::DEPRECATION_ERROR || t == MessageType::AUTHOR_ERROR) {
  130. cmSystemTools::SetErrorOccured();
  131. md.title = "Error";
  132. cmSystemTools::Message(msg.str(), md);
  133. } else {
  134. md.title = "Warning";
  135. cmSystemTools::Message(msg.str(), md);
  136. }
  137. }
  138. namespace {
  139. void PrintCallStack(std::ostream& out, cmListFileBacktrace bt)
  140. {
  141. // The call stack exists only if we have at least two calls on top
  142. // of the bottom.
  143. if (bt.Empty()) {
  144. return;
  145. }
  146. bt = bt.Pop();
  147. if (bt.Empty()) {
  148. return;
  149. }
  150. bool first = true;
  151. cmStateSnapshot bottom = bt.GetBottom();
  152. for (; !bt.Empty(); bt = bt.Pop()) {
  153. cmListFileContext lfc = bt.Top();
  154. if (lfc.Name.empty() &&
  155. lfc.Line != cmListFileContext::DeferPlaceholderLine) {
  156. // Skip this whole-file scope. When we get here we already will
  157. // have printed a more-specific context within the file.
  158. continue;
  159. }
  160. if (first) {
  161. first = false;
  162. out << "Call Stack (most recent call first):\n";
  163. }
  164. if (bottom.GetState()->GetProjectKind() == cmState::ProjectKind::Normal) {
  165. lfc.FilePath = cmSystemTools::RelativeIfUnder(
  166. bottom.GetState()->GetSourceDirectory(), lfc.FilePath);
  167. }
  168. out << " " << lfc << "\n";
  169. }
  170. }
  171. }
  172. void cmMessenger::IssueMessage(MessageType t, const std::string& text,
  173. const cmListFileBacktrace& backtrace) const
  174. {
  175. bool force = false;
  176. // override the message type, if needed, for warnings and errors
  177. MessageType override = this->ConvertMessageType(t);
  178. if (override != t) {
  179. t = override;
  180. force = true;
  181. }
  182. if (force || this->IsMessageTypeVisible(t)) {
  183. this->DisplayMessage(t, text, backtrace);
  184. }
  185. }
  186. void cmMessenger::DisplayMessage(MessageType t, const std::string& text,
  187. const cmListFileBacktrace& backtrace) const
  188. {
  189. std::ostringstream msg;
  190. if (!printMessagePreamble(t, msg)) {
  191. return;
  192. }
  193. // Add the immediate context.
  194. this->PrintBacktraceTitle(msg, backtrace);
  195. printMessageText(msg, text);
  196. // Add the rest of the context.
  197. PrintCallStack(msg, backtrace);
  198. displayMessage(t, msg);
  199. }
  200. void cmMessenger::PrintBacktraceTitle(std::ostream& out,
  201. cmListFileBacktrace const& bt) const
  202. {
  203. // The title exists only if we have a call on top of the bottom.
  204. if (bt.Empty()) {
  205. return;
  206. }
  207. cmListFileContext lfc = bt.Top();
  208. cmStateSnapshot bottom = bt.GetBottom();
  209. if (bottom.GetState()->GetProjectKind() == cmState::ProjectKind::Normal) {
  210. lfc.FilePath = cmSystemTools::RelativeIfUnder(
  211. bottom.GetState()->GetSourceDirectory(), lfc.FilePath);
  212. }
  213. out << (lfc.Line ? " at " : " in ") << lfc;
  214. }