Coverage Report

Created: 2026-02-09 06:05

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