cmMessageCommand.cxx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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 "cmMessageCommand.h"
  4. #include <cassert>
  5. #include <utility>
  6. #include <cm/string_view>
  7. #include <cmext/string_view>
  8. #include "cmConfigureLog.h"
  9. #include "cmExecutionStatus.h"
  10. #include "cmMakefile.h"
  11. #include "cmMessageType.h"
  12. #include "cmMessenger.h"
  13. #include "cmRange.h"
  14. #include "cmStringAlgorithms.h"
  15. #include "cmSystemTools.h"
  16. #include "cmake.h"
  17. namespace {
  18. enum class CheckingType
  19. {
  20. UNDEFINED,
  21. CHECK_START,
  22. CHECK_PASS,
  23. CHECK_FAIL
  24. };
  25. std::string IndentText(std::string text, cmMakefile& mf)
  26. {
  27. auto indent =
  28. cmJoin(cmExpandedList(mf.GetSafeDefinition("CMAKE_MESSAGE_INDENT")), "");
  29. const auto showContext = mf.GetCMakeInstance()->GetShowLogContext() ||
  30. mf.IsOn("CMAKE_MESSAGE_CONTEXT_SHOW");
  31. if (showContext) {
  32. auto context = cmJoin(
  33. cmExpandedList(mf.GetSafeDefinition("CMAKE_MESSAGE_CONTEXT")), ".");
  34. if (!context.empty()) {
  35. indent.insert(0u, cmStrCat("["_s, context, "] "_s));
  36. }
  37. }
  38. if (!indent.empty()) {
  39. cmSystemTools::ReplaceString(text, "\n", "\n" + indent);
  40. text.insert(0u, indent);
  41. }
  42. return text;
  43. }
  44. void ReportCheckResult(cm::string_view what, std::string result,
  45. cmMakefile& mf)
  46. {
  47. if (mf.GetCMakeInstance()->HasCheckInProgress()) {
  48. auto text = mf.GetCMakeInstance()->GetTopCheckInProgressMessage() + " - " +
  49. std::move(result);
  50. mf.DisplayStatus(IndentText(std::move(text), mf), -1);
  51. } else {
  52. mf.GetMessenger()->DisplayMessage(
  53. MessageType::AUTHOR_WARNING,
  54. cmStrCat("Ignored "_s, what, " without CHECK_START"_s),
  55. mf.GetBacktrace());
  56. }
  57. }
  58. namespace {
  59. #ifndef CMAKE_BOOTSTRAP
  60. void WriteMessageEvent(cmConfigureLog& log, cmMakefile const& mf,
  61. std::string const& message)
  62. {
  63. // Keep in sync with cmFileAPIConfigureLog's DumpEventKindNames.
  64. static const std::vector<unsigned long> LogVersionsWithMessageV1{ 1 };
  65. if (log.IsAnyLogVersionEnabled(LogVersionsWithMessageV1)) {
  66. log.BeginEvent("message-v1", mf);
  67. log.WriteLiteralTextBlock("message"_s, message);
  68. log.EndEvent();
  69. }
  70. }
  71. #endif
  72. }
  73. } // anonymous namespace
  74. // cmLibraryCommand
  75. bool cmMessageCommand(std::vector<std::string> const& args,
  76. cmExecutionStatus& status)
  77. {
  78. if (args.empty()) {
  79. status.SetError("called with incorrect number of arguments");
  80. return false;
  81. }
  82. auto& mf = status.GetMakefile();
  83. auto i = args.cbegin();
  84. auto type = MessageType::MESSAGE;
  85. auto fatal = false;
  86. auto level = Message::LogLevel::LOG_UNDEFINED;
  87. auto checkingType = CheckingType::UNDEFINED;
  88. if (*i == "SEND_ERROR") {
  89. type = MessageType::FATAL_ERROR;
  90. level = Message::LogLevel::LOG_ERROR;
  91. ++i;
  92. } else if (*i == "FATAL_ERROR") {
  93. fatal = true;
  94. type = MessageType::FATAL_ERROR;
  95. level = Message::LogLevel::LOG_ERROR;
  96. ++i;
  97. } else if (*i == "WARNING") {
  98. type = MessageType::WARNING;
  99. level = Message::LogLevel::LOG_WARNING;
  100. ++i;
  101. } else if (*i == "AUTHOR_WARNING") {
  102. if (mf.IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") &&
  103. !mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) {
  104. fatal = true;
  105. type = MessageType::AUTHOR_ERROR;
  106. level = Message::LogLevel::LOG_ERROR;
  107. } else if (!mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) {
  108. type = MessageType::AUTHOR_WARNING;
  109. level = Message::LogLevel::LOG_WARNING;
  110. } else {
  111. return true;
  112. }
  113. ++i;
  114. } else if (*i == "CHECK_START") {
  115. level = Message::LogLevel::LOG_STATUS;
  116. checkingType = CheckingType::CHECK_START;
  117. ++i;
  118. } else if (*i == "CHECK_PASS") {
  119. level = Message::LogLevel::LOG_STATUS;
  120. checkingType = CheckingType::CHECK_PASS;
  121. ++i;
  122. } else if (*i == "CHECK_FAIL") {
  123. level = Message::LogLevel::LOG_STATUS;
  124. checkingType = CheckingType::CHECK_FAIL;
  125. ++i;
  126. } else if (*i == "CONFIGURE_LOG") {
  127. #ifndef CMAKE_BOOTSTRAP
  128. if (cmConfigureLog* log = mf.GetCMakeInstance()->GetConfigureLog()) {
  129. ++i;
  130. WriteMessageEvent(*log, mf, cmJoin(cmMakeRange(i, args.cend()), ""_s));
  131. }
  132. #endif
  133. return true;
  134. } else if (*i == "STATUS") {
  135. level = Message::LogLevel::LOG_STATUS;
  136. ++i;
  137. } else if (*i == "VERBOSE") {
  138. level = Message::LogLevel::LOG_VERBOSE;
  139. ++i;
  140. } else if (*i == "DEBUG") {
  141. level = Message::LogLevel::LOG_DEBUG;
  142. ++i;
  143. } else if (*i == "TRACE") {
  144. level = Message::LogLevel::LOG_TRACE;
  145. ++i;
  146. } else if (*i == "DEPRECATION") {
  147. if (mf.IsOn("CMAKE_ERROR_DEPRECATED")) {
  148. fatal = true;
  149. type = MessageType::DEPRECATION_ERROR;
  150. level = Message::LogLevel::LOG_ERROR;
  151. } else if (!mf.IsSet("CMAKE_WARN_DEPRECATED") ||
  152. mf.IsOn("CMAKE_WARN_DEPRECATED")) {
  153. type = MessageType::DEPRECATION_WARNING;
  154. level = Message::LogLevel::LOG_WARNING;
  155. } else {
  156. return true;
  157. }
  158. ++i;
  159. } else if (*i == "NOTICE") {
  160. // `NOTICE` message type is going to be output to stderr
  161. level = Message::LogLevel::LOG_NOTICE;
  162. ++i;
  163. } else {
  164. // Messages w/o any type are `NOTICE`s
  165. level = Message::LogLevel::LOG_NOTICE;
  166. }
  167. assert("Message log level expected to be set" &&
  168. level != Message::LogLevel::LOG_UNDEFINED);
  169. Message::LogLevel desiredLevel = mf.GetCurrentLogLevel();
  170. if (desiredLevel < level) {
  171. // Suppress the message
  172. return true;
  173. }
  174. auto message = cmJoin(cmMakeRange(i, args.cend()), "");
  175. switch (level) {
  176. case Message::LogLevel::LOG_ERROR:
  177. case Message::LogLevel::LOG_WARNING:
  178. // we've overridden the message type, above, so display it directly
  179. mf.GetMessenger()->DisplayMessage(type, message, mf.GetBacktrace());
  180. break;
  181. case Message::LogLevel::LOG_NOTICE:
  182. cmSystemTools::Message(IndentText(message, mf));
  183. break;
  184. case Message::LogLevel::LOG_STATUS:
  185. switch (checkingType) {
  186. case CheckingType::CHECK_START:
  187. mf.DisplayStatus(IndentText(message, mf), -1);
  188. mf.GetCMakeInstance()->PushCheckInProgressMessage(message);
  189. break;
  190. case CheckingType::CHECK_PASS:
  191. ReportCheckResult("CHECK_PASS"_s, message, mf);
  192. break;
  193. case CheckingType::CHECK_FAIL:
  194. ReportCheckResult("CHECK_FAIL"_s, message, mf);
  195. break;
  196. default:
  197. mf.DisplayStatus(IndentText(message, mf), -1);
  198. break;
  199. }
  200. break;
  201. case Message::LogLevel::LOG_VERBOSE:
  202. case Message::LogLevel::LOG_DEBUG:
  203. case Message::LogLevel::LOG_TRACE:
  204. mf.DisplayStatus(IndentText(message, mf), -1);
  205. break;
  206. default:
  207. assert("Unexpected log level! Review the `cmMessageCommand.cxx`." &&
  208. false);
  209. break;
  210. }
  211. if (fatal) {
  212. cmSystemTools::SetFatalErrorOccurred();
  213. }
  214. return true;
  215. }