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