Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmMessenger.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 "cmMessenger.h"
4
5
#include "cmDocumentationFormatter.h"
6
#include "cmMessageMetadata.h"
7
#include "cmMessageType.h"
8
#include "cmStdIoTerminal.h"
9
#include "cmStringAlgorithms.h"
10
#include "cmSystemTools.h"
11
12
#if !defined(CMAKE_BOOTSTRAP)
13
#  include "cmsys/SystemInformation.hxx"
14
15
#  include "cmSarifLog.h"
16
#endif
17
18
#include <sstream>
19
#include <utility>
20
21
#ifdef CMake_ENABLE_DEBUGGER
22
#  include "cmDebuggerAdapter.h"
23
#endif
24
25
namespace {
26
char const* getMessageTypeStr(MessageType t)
27
1
{
28
1
  switch (t) {
29
1
    case MessageType::FATAL_ERROR:
30
1
      return "Error";
31
0
    case MessageType::INTERNAL_ERROR:
32
0
      return "Internal Error (please report a bug)";
33
0
    case MessageType::LOG:
34
0
      return "Debug Log";
35
0
    case MessageType::DEPRECATION_ERROR:
36
0
      return "Deprecation Error";
37
0
    case MessageType::DEPRECATION_WARNING:
38
0
      return "Deprecation Warning";
39
0
    case MessageType::AUTHOR_WARNING:
40
0
      return "Warning (dev)";
41
0
    case MessageType::AUTHOR_ERROR:
42
0
      return "Error (dev)";
43
0
    default:
44
0
      break;
45
1
  }
46
0
  return "Warning";
47
1
}
48
49
cm::StdIo::TermAttr getMessageColor(MessageType t)
50
1
{
51
1
  switch (t) {
52
0
    case MessageType::INTERNAL_ERROR:
53
1
    case MessageType::FATAL_ERROR:
54
1
    case MessageType::AUTHOR_ERROR:
55
1
      return cm::StdIo::TermAttr::ForegroundRed;
56
0
    case MessageType::AUTHOR_WARNING:
57
0
    case MessageType::WARNING:
58
0
      return cm::StdIo::TermAttr::ForegroundYellow;
59
0
    default:
60
0
      return cm::StdIo::TermAttr::Normal;
61
1
  }
62
1
}
63
64
void printMessageText(std::ostream& msg, std::string const& text)
65
1
{
66
1
  msg << ":\n";
67
1
  cmDocumentationFormatter formatter;
68
1
  formatter.SetIndent(2u);
69
1
  formatter.PrintFormatted(msg, text);
70
1
}
71
72
void displayMessage(MessageType t, std::ostringstream& msg)
73
1
{
74
  // Add a note about warning suppression.
75
1
  if (t == MessageType::AUTHOR_WARNING) {
76
0
    msg << "This warning is for project developers.  Use -Wno-dev to suppress "
77
0
           "it.";
78
1
  } else if (t == MessageType::AUTHOR_ERROR) {
79
0
    msg << "This error is for project developers. Use -Wno-error=dev to "
80
0
           "suppress it.";
81
0
  }
82
83
  // Add a terminating blank line.
84
1
  msg << '\n';
85
86
1
#if !defined(CMAKE_BOOTSTRAP)
87
  // Add a C++ stack trace to internal errors.
88
1
  if (t == MessageType::INTERNAL_ERROR) {
89
0
    std::string stack = cmsys::SystemInformation::GetProgramStack(0, 0);
90
0
    if (!stack.empty()) {
91
0
      if (cmHasLiteralPrefix(stack, "WARNING:")) {
92
0
        stack = "Note:" + stack.substr(8);
93
0
      }
94
0
      msg << stack << '\n';
95
0
    }
96
0
  }
97
1
#endif
98
99
  // Output the message.
100
1
  cmMessageMetadata md;
101
1
  md.attrs = getMessageColor(t);
102
1
  if (t == MessageType::FATAL_ERROR || t == MessageType::INTERNAL_ERROR ||
103
1
      t == MessageType::DEPRECATION_ERROR || t == MessageType::AUTHOR_ERROR) {
104
1
    cmSystemTools::SetErrorOccurred();
105
1
    md.title = "Error";
106
1
  } else {
107
0
    md.title = "Warning";
108
0
  }
109
1
  cmSystemTools::Message(msg.str(), md);
110
1
}
111
112
void PrintCallStack(std::ostream& out, cmListFileBacktrace bt,
113
                    cm::optional<std::string> const& topSource)
114
1
{
115
  // The call stack exists only if we have at least two calls on top
116
  // of the bottom.
117
1
  if (bt.Empty()) {
118
0
    return;
119
0
  }
120
1
  std::string lastFilePath = bt.Top().FilePath;
121
1
  bt = bt.Pop();
122
1
  if (bt.Empty()) {
123
0
    return;
124
0
  }
125
126
1
  bool first = true;
127
2
  for (; !bt.Empty(); bt = bt.Pop()) {
128
1
    cmListFileContext lfc = bt.Top();
129
1
    if (lfc.Name.empty() &&
130
1
        lfc.Line != cmListFileContext::DeferPlaceholderLine &&
131
1
        lfc.FilePath == lastFilePath) {
132
      // An entry with no function name is frequently preceded (in the stack)
133
      // by a more specific entry.  When this happens (as verified by the
134
      // preceding entry referencing the same file path), skip the less
135
      // specific entry, as we have already printed the more specific one.
136
1
      continue;
137
1
    }
138
0
    if (first) {
139
0
      first = false;
140
0
      out << "Call Stack (most recent call first):\n";
141
0
    }
142
0
    lastFilePath = lfc.FilePath;
143
0
    if (topSource) {
144
0
      lfc.FilePath = cmSystemTools::RelativeIfUnder(*topSource, lfc.FilePath);
145
0
    }
146
0
    out << "  " << lfc << '\n';
147
0
  }
148
1
}
149
150
} // anonymous namespace
151
152
MessageType cmMessenger::ConvertMessageType(MessageType t) const
153
1
{
154
1
  if (t == MessageType::AUTHOR_WARNING || t == MessageType::AUTHOR_ERROR) {
155
0
    if (this->GetDevWarningsAsErrors()) {
156
0
      return MessageType::AUTHOR_ERROR;
157
0
    }
158
0
    return MessageType::AUTHOR_WARNING;
159
0
  }
160
1
  if (t == MessageType::DEPRECATION_WARNING ||
161
1
      t == MessageType::DEPRECATION_ERROR) {
162
0
    if (this->GetDeprecatedWarningsAsErrors()) {
163
0
      return MessageType::DEPRECATION_ERROR;
164
0
    }
165
0
    return MessageType::DEPRECATION_WARNING;
166
0
  }
167
1
  return t;
168
1
}
169
170
bool cmMessenger::IsMessageTypeVisible(MessageType t) const
171
1
{
172
1
  if (t == MessageType::DEPRECATION_ERROR) {
173
0
    return this->GetDeprecatedWarningsAsErrors();
174
0
  }
175
1
  if (t == MessageType::DEPRECATION_WARNING) {
176
0
    return !this->GetSuppressDeprecatedWarnings();
177
0
  }
178
1
  if (t == MessageType::AUTHOR_ERROR) {
179
0
    return this->GetDevWarningsAsErrors();
180
0
  }
181
1
  if (t == MessageType::AUTHOR_WARNING) {
182
0
    return !this->GetSuppressDevWarnings();
183
0
  }
184
185
1
  return true;
186
1
}
187
188
void cmMessenger::IssueMessage(MessageType t, std::string const& text,
189
                               cmListFileBacktrace const& backtrace) const
190
1
{
191
1
  bool force = false;
192
  // override the message type, if needed, for warnings and errors
193
1
  MessageType override = this->ConvertMessageType(t);
194
1
  if (override != t) {
195
0
    t = override;
196
0
    force = true;
197
0
  }
198
199
1
  if (force || this->IsMessageTypeVisible(t)) {
200
1
    this->DisplayMessage(t, text, backtrace);
201
1
  }
202
1
}
203
204
void cmMessenger::DisplayMessage(MessageType t, std::string const& text,
205
                                 cmListFileBacktrace const& backtrace) const
206
1
{
207
1
  std::ostringstream msg;
208
209
  // Print the message preamble.
210
1
  msg << "CMake " << getMessageTypeStr(t);
211
212
  // Add the immediate context.
213
1
  this->PrintBacktraceTitle(msg, backtrace);
214
215
1
  printMessageText(msg, text);
216
217
  // Add the rest of the context.
218
1
  PrintCallStack(msg, backtrace, this->TopSource);
219
220
1
  displayMessage(t, msg);
221
222
1
#ifndef CMAKE_BOOTSTRAP
223
  // Add message to SARIF logs
224
1
  this->SarifLog.LogMessage(t, text, backtrace);
225
1
#endif
226
227
1
#ifdef CMake_ENABLE_DEBUGGER
228
1
  if (DebuggerAdapter) {
229
0
    DebuggerAdapter->OnMessageOutput(t, msg.str());
230
0
  }
231
1
#endif
232
1
}
233
234
void cmMessenger::PrintBacktraceTitle(std::ostream& out,
235
                                      cmListFileBacktrace const& bt) const
236
1
{
237
  // The title exists only if we have a call on top of the bottom.
238
1
  if (bt.Empty()) {
239
0
    return;
240
0
  }
241
1
  cmListFileContext lfc = bt.Top();
242
1
  if (this->TopSource) {
243
1
    lfc.FilePath =
244
1
      cmSystemTools::RelativeIfUnder(*this->TopSource, lfc.FilePath);
245
1
  }
246
1
  out << (lfc.Line ? " at " : " in ") << lfc;
247
1
}
248
249
void cmMessenger::SetTopSource(cm::optional<std::string> topSource)
250
36
{
251
36
  this->TopSource = std::move(topSource);
252
36
}