Coverage Report

Created: 2026-04-29 07:01

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