Coverage Report

Created: 2026-06-15 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmMessageCommand.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmMessageCommand.h"
4
5
#include <cassert>
6
#include <utility>
7
8
#include <cm/memory>
9
#include <cm/string_view>
10
#include <cmext/string_view>
11
12
#include "cmConfigureLog.h"
13
#include "cmDiagnostics.h"
14
#include "cmExecutionStatus.h"
15
#include "cmList.h"
16
#include "cmMakefile.h"
17
#include "cmMessageType.h"
18
#include "cmMessenger.h"
19
#include "cmPolicies.h"
20
#include "cmRange.h"
21
#include "cmStringAlgorithms.h"
22
#include "cmSystemTools.h"
23
#include "cmValue.h"
24
#include "cmake.h"
25
26
#ifdef CMake_ENABLE_DEBUGGER
27
#  include "cmDebuggerAdapter.h"
28
#endif
29
30
namespace {
31
32
std::string IndentText(std::string text, cmMakefile& mf)
33
0
{
34
0
  auto indent =
35
0
    cmList{ mf.GetSafeDefinition("CMAKE_MESSAGE_INDENT") }.join("");
36
37
0
  auto const showContext = mf.GetCMakeInstance()->GetShowLogContext() ||
38
0
    mf.IsOn("CMAKE_MESSAGE_CONTEXT_SHOW");
39
0
  if (showContext) {
40
0
    auto context =
41
0
      cmList{ mf.GetSafeDefinition("CMAKE_MESSAGE_CONTEXT") }.join(".");
42
0
    if (!context.empty()) {
43
0
      indent.insert(0u, cmStrCat("["_s, context, "] "_s));
44
0
    }
45
0
  }
46
47
0
  if (!indent.empty()) {
48
0
    cmSystemTools::ReplaceString(text, "\n", "\n" + indent);
49
0
    text.insert(0u, indent);
50
0
  }
51
0
  return text;
52
0
}
53
54
void ReportCheckResult(cm::string_view what, std::string result,
55
                       cmMakefile& mf)
56
0
{
57
0
  if (mf.GetCMakeInstance()->HasCheckInProgress()) {
58
0
    auto text = mf.GetCMakeInstance()->GetTopCheckInProgressMessage() + " - " +
59
0
      std::move(result);
60
0
    mf.DisplayStatus(IndentText(std::move(text), mf), -1);
61
0
  } else {
62
0
    mf.GetMessenger()->DisplayMessage(
63
0
      MessageType::WARNING, cmDiagnostics::CMD_AUTHOR,
64
0
      cmStrCat("Ignored "_s, what, " without CHECK_START"_s),
65
0
      mf.GetBacktrace());
66
0
  }
67
0
}
68
69
namespace {
70
#ifndef CMAKE_BOOTSTRAP
71
void WriteMessageEvent(cmConfigureLog& log, cmMakefile const& mf,
72
                       std::string const& message)
73
0
{
74
  // Keep in sync with cmFileAPIConfigureLog's DumpEventKindNames.
75
0
  static std::vector<unsigned int> const LogVersionsWithMessageV1{ 1 };
76
77
0
  if (log.IsAnyLogVersionEnabled(LogVersionsWithMessageV1)) {
78
0
    log.BeginEvent("message-v1", mf);
79
0
    log.WriteLiteralTextBlock("message"_s, message);
80
0
    log.EndEvent();
81
0
  }
82
0
}
83
#endif
84
}
85
86
} // anonymous namespace
87
88
// cmLibraryCommand
89
bool cmMessageCommand(std::vector<std::string> const& args,
90
                      cmExecutionStatus& status)
91
0
{
92
0
  if (args.empty()) {
93
0
    status.SetError("called with incorrect number of arguments");
94
0
    return false;
95
0
  }
96
97
0
  auto& mf = status.GetMakefile();
98
99
0
  auto i = args.cbegin();
100
101
0
  std::unique_ptr<cmMakefile::DiagnosticPushPop> ds;
102
0
  auto category = cmDiagnostics::CMD_NONE;
103
0
  auto type = MessageType::MESSAGE;
104
0
  auto fatal = false;
105
0
  auto level = Message::LogLevel::LOG_UNDEFINED;
106
0
  auto checkingType = Message::CheckType::UNDEFINED;
107
0
  if (*i == "SEND_ERROR") {
108
0
    type = MessageType::FATAL_ERROR;
109
0
    level = Message::LogLevel::LOG_ERROR;
110
0
    ++i;
111
0
  } else if (*i == "FATAL_ERROR") {
112
0
    fatal = true;
113
0
    type = MessageType::FATAL_ERROR;
114
0
    level = Message::LogLevel::LOG_ERROR;
115
0
    ++i;
116
0
  } else if (*i == "WARNING") {
117
0
    type = MessageType::WARNING;
118
0
    level = Message::LogLevel::LOG_WARNING;
119
0
    ++i;
120
0
  } else if (*i == "AUTHOR_WARNING") {
121
0
    category = cmDiagnostics::CMD_AUTHOR;
122
0
    switch (mf.GetDiagnosticAction(category)) {
123
0
      case cmDiagnostics::Ignore:
124
0
        return true;
125
0
      case cmDiagnostics::FatalError:
126
0
        fatal = true;
127
0
        CM_FALLTHROUGH;
128
0
      case cmDiagnostics::SendError:
129
0
        type = MessageType::FATAL_ERROR;
130
0
        level = Message::LogLevel::LOG_ERROR;
131
0
        break;
132
0
      default:
133
0
        type = MessageType::WARNING;
134
0
        level = Message::LogLevel::LOG_WARNING;
135
0
        break;
136
0
    }
137
0
    ++i;
138
0
  } else if (*i == "CHECK_START") {
139
0
    level = Message::LogLevel::LOG_STATUS;
140
0
    checkingType = Message::CheckType::CHECK_START;
141
0
    ++i;
142
0
  } else if (*i == "CHECK_PASS") {
143
0
    level = Message::LogLevel::LOG_STATUS;
144
0
    checkingType = Message::CheckType::CHECK_PASS;
145
0
    ++i;
146
0
  } else if (*i == "CHECK_FAIL") {
147
0
    level = Message::LogLevel::LOG_STATUS;
148
0
    checkingType = Message::CheckType::CHECK_FAIL;
149
0
    ++i;
150
0
  } else if (*i == "CONFIGURE_LOG") {
151
0
#ifndef CMAKE_BOOTSTRAP
152
0
    if (cmConfigureLog* log = mf.GetCMakeInstance()->GetConfigureLog()) {
153
0
      ++i;
154
0
      WriteMessageEvent(*log, mf, cmJoin(cmMakeRange(i, args.cend()), ""_s));
155
0
    }
156
0
#endif
157
0
    return true;
158
0
  } else if (*i == "STATUS") {
159
0
    level = Message::LogLevel::LOG_STATUS;
160
0
    ++i;
161
0
  } else if (*i == "VERBOSE") {
162
0
    level = Message::LogLevel::LOG_VERBOSE;
163
0
    ++i;
164
0
  } else if (*i == "DEBUG") {
165
0
    level = Message::LogLevel::LOG_DEBUG;
166
0
    ++i;
167
0
  } else if (*i == "TRACE") {
168
0
    level = Message::LogLevel::LOG_TRACE;
169
0
    ++i;
170
0
  } else if (*i == "DEPRECATION") {
171
0
    category = cmDiagnostics::CMD_DEPRECATED;
172
173
0
    cmPolicies::PolicyStatus const cmp0218 =
174
0
      mf.GetPolicyStatus(cmPolicies::CMP0218);
175
0
    if (cmp0218 != cmPolicies::NEW) {
176
0
      std::unique_ptr<cmMakefile::PolicyPushPop> ps;
177
178
0
      if (cmp0218 != cmPolicies::OLD) {
179
        // Suppress warnings about using old variables.
180
0
        ps = cm::make_unique<cmMakefile::PolicyPushPop>(&mf);
181
0
        mf.SetPolicy(cmPolicies::CMP0218, cmPolicies::OLD);
182
0
      }
183
184
0
      ds = cm::make_unique<cmMakefile::DiagnosticPushPop>(&mf);
185
186
      // Use old variables to determine diagnostic action.
187
0
      if (mf.IsOn("CMAKE_ERROR_DEPRECATED")) {
188
0
        mf.SetDiagnostic(category, cmDiagnostics::FatalError);
189
0
      } else {
190
0
        cmValue const warn = mf.GetDefinition("CMAKE_WARN_DEPRECATED");
191
0
        if (warn.IsSet() && !warn.IsOn()) {
192
0
          mf.SetDiagnostic(category, cmDiagnostics::Ignore);
193
0
        } else {
194
0
          mf.SetDiagnostic(category, cmDiagnostics::Warn);
195
0
        }
196
0
      }
197
0
    }
198
199
0
    switch (mf.GetDiagnosticAction(category)) {
200
0
      case cmDiagnostics::Ignore:
201
0
        return true;
202
0
      case cmDiagnostics::FatalError:
203
0
        fatal = true;
204
0
        CM_FALLTHROUGH;
205
0
      case cmDiagnostics::SendError:
206
0
        type = MessageType::FATAL_ERROR;
207
0
        level = Message::LogLevel::LOG_ERROR;
208
0
        break;
209
0
      default:
210
0
        type = MessageType::WARNING;
211
0
        level = Message::LogLevel::LOG_WARNING;
212
0
        break;
213
0
    }
214
0
    ++i;
215
0
  } else if (*i == "NOTICE") {
216
    // `NOTICE` message type is going to be output to stderr
217
0
    level = Message::LogLevel::LOG_NOTICE;
218
0
    ++i;
219
0
  } else {
220
    // Messages w/o any type are `NOTICE`s
221
0
    level = Message::LogLevel::LOG_NOTICE;
222
0
  }
223
0
  assert("Message log level expected to be set" &&
224
0
         level != Message::LogLevel::LOG_UNDEFINED);
225
226
0
  Message::LogLevel desiredLevel = mf.GetCurrentLogLevel();
227
228
0
  if (desiredLevel < level) {
229
    // Suppress the message
230
0
    return true;
231
0
  }
232
233
0
  auto message = cmJoin(cmMakeRange(i, args.cend()), "");
234
235
0
  switch (level) {
236
0
    case Message::LogLevel::LOG_ERROR:
237
0
    case Message::LogLevel::LOG_WARNING:
238
      // we've overridden the message type, above, so display it directly
239
0
      mf.GetMessenger()->DisplayMessage(type, category, message,
240
0
                                        mf.GetBacktrace());
241
0
      break;
242
243
0
    case Message::LogLevel::LOG_NOTICE:
244
0
      cmSystemTools::Message(IndentText(message, mf));
245
0
#ifdef CMake_ENABLE_DEBUGGER
246
0
      if (mf.GetCMakeInstance()->GetDebugAdapter()) {
247
0
        mf.GetCMakeInstance()->GetDebugAdapter()->OnMessageOutput(type,
248
0
                                                                  message);
249
0
      }
250
0
#endif
251
0
      break;
252
253
0
    case Message::LogLevel::LOG_STATUS:
254
0
      switch (checkingType) {
255
0
        case Message::CheckType::CHECK_START:
256
0
          mf.DisplayStatus(IndentText(message, mf), -1);
257
0
          mf.GetCMakeInstance()->PushCheckInProgressMessage(message);
258
0
          break;
259
260
0
        case Message::CheckType::CHECK_PASS:
261
0
          ReportCheckResult("CHECK_PASS"_s, message, mf);
262
0
          break;
263
264
0
        case Message::CheckType::CHECK_FAIL:
265
0
          ReportCheckResult("CHECK_FAIL"_s, message, mf);
266
0
          break;
267
268
0
        default:
269
0
          mf.DisplayStatus(IndentText(message, mf), -1);
270
0
          break;
271
0
      }
272
0
      break;
273
274
0
    case Message::LogLevel::LOG_VERBOSE:
275
0
    case Message::LogLevel::LOG_DEBUG:
276
0
    case Message::LogLevel::LOG_TRACE:
277
0
      mf.DisplayStatus(IndentText(message, mf), -1);
278
0
      break;
279
280
0
    default:
281
0
      assert("Unexpected log level! Review the `cmMessageCommand.cxx`." &&
282
0
             false);
283
0
      break;
284
0
  }
285
286
0
  if (fatal) {
287
0
    cmSystemTools::SetFatalErrorOccurred();
288
0
  }
289
0
  return true;
290
0
}