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