Files
cmake/Source/cmMessageCommand.cxx
T
Matthew Woehlke ca5d569524 Diagnostics: Switch state to new system
Add an additional state to the cmake instance to track diagnostics. Use
this to initialize any new/reset states we create so that other contexts
will inherit the diagnostic state from presets / command-line options.
Modify the front-end to map presets and -W command-line options to
manipulation of this new diagnostic state. Remove old members for
storing diagnostic state. Rework how diagnostic state is persisted.
Deprecate old, non-standard spellings for manipulating diagnostics.
Deprecate old 'dev' spelling for CMD_AUTHOR.

Note that this is a work in progress. Messaging has not yet been
updated, so the new state is not yet being used to control diagnostics,
and some of the logic to update the old state has been stripped (and
will eventually be removed entirely). Backward compatibility support for
the deprecated CMAKE_{WARN,ERROR}_DEPRECATED variables is only partially
implemented. The GUI's interface for manipulating warnings is partially
broken and needs to be overhauled. These tasks are being done in
separate commits in order to minimize review burden and keep the changes
per commit / per merge request more manageable.
2026-04-14 12:42:33 -04:00

258 lines
7.3 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmMessageCommand.h"
#include <cassert>
#include <memory>
#include <utility>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmConfigureLog.h"
#include "cmDiagnostics.h"
#include "cmExecutionStatus.h"
#include "cmList.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmMessenger.h"
#include "cmRange.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmake.h"
#ifdef CMake_ENABLE_DEBUGGER
# include "cmDebuggerAdapter.h"
#endif
namespace {
std::string IndentText(std::string text, cmMakefile& mf)
{
auto indent =
cmList{ mf.GetSafeDefinition("CMAKE_MESSAGE_INDENT") }.join("");
auto const showContext = mf.GetCMakeInstance()->GetShowLogContext() ||
mf.IsOn("CMAKE_MESSAGE_CONTEXT_SHOW");
if (showContext) {
auto context =
cmList{ mf.GetSafeDefinition("CMAKE_MESSAGE_CONTEXT") }.join(".");
if (!context.empty()) {
indent.insert(0u, cmStrCat("["_s, context, "] "_s));
}
}
if (!indent.empty()) {
cmSystemTools::ReplaceString(text, "\n", "\n" + indent);
text.insert(0u, indent);
}
return text;
}
void ReportCheckResult(cm::string_view what, std::string result,
cmMakefile& mf)
{
if (mf.GetCMakeInstance()->HasCheckInProgress()) {
auto text = mf.GetCMakeInstance()->GetTopCheckInProgressMessage() + " - " +
std::move(result);
mf.DisplayStatus(IndentText(std::move(text), mf), -1);
} else {
mf.GetMessenger()->DisplayMessage(
MessageType::AUTHOR_WARNING, cmDiagnostics::CMD_NONE,
cmStrCat("Ignored "_s, what, " without CHECK_START"_s),
mf.GetBacktrace());
}
}
namespace {
#ifndef CMAKE_BOOTSTRAP
void WriteMessageEvent(cmConfigureLog& log, cmMakefile const& mf,
std::string const& message)
{
// Keep in sync with cmFileAPIConfigureLog's DumpEventKindNames.
static std::vector<unsigned int> const LogVersionsWithMessageV1{ 1 };
if (log.IsAnyLogVersionEnabled(LogVersionsWithMessageV1)) {
log.BeginEvent("message-v1", mf);
log.WriteLiteralTextBlock("message"_s, message);
log.EndEvent();
}
}
#endif
}
} // anonymous namespace
// cmLibraryCommand
bool cmMessageCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
if (args.empty()) {
status.SetError("called with incorrect number of arguments");
return false;
}
auto& mf = status.GetMakefile();
auto i = args.cbegin();
auto type = MessageType::MESSAGE;
auto fatal = false;
auto level = Message::LogLevel::LOG_UNDEFINED;
auto checkingType = Message::CheckType::UNDEFINED;
if (*i == "SEND_ERROR") {
type = MessageType::FATAL_ERROR;
level = Message::LogLevel::LOG_ERROR;
++i;
} else if (*i == "FATAL_ERROR") {
fatal = true;
type = MessageType::FATAL_ERROR;
level = Message::LogLevel::LOG_ERROR;
++i;
} else if (*i == "WARNING") {
type = MessageType::WARNING;
level = Message::LogLevel::LOG_WARNING;
++i;
} else if (*i == "AUTHOR_WARNING") {
switch (mf.GetDiagnosticAction(cmDiagnostics::CMD_AUTHOR)) {
case cmDiagnostics::Ignore:
return true;
case cmDiagnostics::FatalError:
fatal = true;
CM_FALLTHROUGH;
case cmDiagnostics::SendError:
type = MessageType::AUTHOR_ERROR;
level = Message::LogLevel::LOG_ERROR;
break;
default:
type = MessageType::AUTHOR_WARNING;
level = Message::LogLevel::LOG_WARNING;
break;
}
++i;
} else if (*i == "CHECK_START") {
level = Message::LogLevel::LOG_STATUS;
checkingType = Message::CheckType::CHECK_START;
++i;
} else if (*i == "CHECK_PASS") {
level = Message::LogLevel::LOG_STATUS;
checkingType = Message::CheckType::CHECK_PASS;
++i;
} else if (*i == "CHECK_FAIL") {
level = Message::LogLevel::LOG_STATUS;
checkingType = Message::CheckType::CHECK_FAIL;
++i;
} else if (*i == "CONFIGURE_LOG") {
#ifndef CMAKE_BOOTSTRAP
if (cmConfigureLog* log = mf.GetCMakeInstance()->GetConfigureLog()) {
++i;
WriteMessageEvent(*log, mf, cmJoin(cmMakeRange(i, args.cend()), ""_s));
}
#endif
return true;
} else if (*i == "STATUS") {
level = Message::LogLevel::LOG_STATUS;
++i;
} else if (*i == "VERBOSE") {
level = Message::LogLevel::LOG_VERBOSE;
++i;
} else if (*i == "DEBUG") {
level = Message::LogLevel::LOG_DEBUG;
++i;
} else if (*i == "TRACE") {
level = Message::LogLevel::LOG_TRACE;
++i;
} else if (*i == "DEPRECATION") {
switch (mf.GetDiagnosticAction(cmDiagnostics::CMD_DEPRECATED)) {
case cmDiagnostics::Ignore:
return true;
case cmDiagnostics::FatalError:
fatal = true;
CM_FALLTHROUGH;
case cmDiagnostics::SendError:
type = MessageType::DEPRECATION_ERROR;
level = Message::LogLevel::LOG_ERROR;
break;
default:
type = MessageType::DEPRECATION_WARNING;
level = Message::LogLevel::LOG_WARNING;
break;
}
++i;
} else if (*i == "NOTICE") {
// `NOTICE` message type is going to be output to stderr
level = Message::LogLevel::LOG_NOTICE;
++i;
} else {
// Messages w/o any type are `NOTICE`s
level = Message::LogLevel::LOG_NOTICE;
}
assert("Message log level expected to be set" &&
level != Message::LogLevel::LOG_UNDEFINED);
Message::LogLevel desiredLevel = mf.GetCurrentLogLevel();
if (desiredLevel < level) {
// Suppress the message
return true;
}
auto message = cmJoin(cmMakeRange(i, args.cend()), "");
switch (level) {
case Message::LogLevel::LOG_ERROR:
case Message::LogLevel::LOG_WARNING:
// we've overridden the message type, above, so display it directly
mf.GetMessenger()->DisplayMessage(type, cmDiagnostics::CMD_NONE, message,
mf.GetBacktrace());
break;
case Message::LogLevel::LOG_NOTICE:
cmSystemTools::Message(IndentText(message, mf));
#ifdef CMake_ENABLE_DEBUGGER
if (mf.GetCMakeInstance()->GetDebugAdapter()) {
mf.GetCMakeInstance()->GetDebugAdapter()->OnMessageOutput(type,
message);
}
#endif
break;
case Message::LogLevel::LOG_STATUS:
switch (checkingType) {
case Message::CheckType::CHECK_START:
mf.DisplayStatus(IndentText(message, mf), -1);
mf.GetCMakeInstance()->PushCheckInProgressMessage(message);
break;
case Message::CheckType::CHECK_PASS:
ReportCheckResult("CHECK_PASS"_s, message, mf);
break;
case Message::CheckType::CHECK_FAIL:
ReportCheckResult("CHECK_FAIL"_s, message, mf);
break;
default:
mf.DisplayStatus(IndentText(message, mf), -1);
break;
}
break;
case Message::LogLevel::LOG_VERBOSE:
case Message::LogLevel::LOG_DEBUG:
case Message::LogLevel::LOG_TRACE:
mf.DisplayStatus(IndentText(message, mf), -1);
break;
default:
assert("Unexpected log level! Review the `cmMessageCommand.cxx`." &&
false);
break;
}
if (fatal) {
cmSystemTools::SetFatalErrorOccurred();
}
return true;
}