Add cmake_language(GET_MESSAGE_LOG_LEVEL) sub command

The new sub-command writes a string representation of the
current log level to the output variable given to the
sub-command.

Given that the log-level might be set either via the --log-level
command line option or via the CMAKE_MESSAGE_LOG_LEVEL
cache / regular variables, the priority for each of the log level
sources is as follows, with the first one being the highest:
1) --log-level
2) CMAKE_MESSAGE_LOG_LEVEL regular variable
3) CMAKE_MESSAGE_LOG_LEVEL cache variable
4) default log level (STATUS)

Fixes: #23572
This commit is contained in:
Alexandru Croitor
2022-06-24 15:44:12 +02:00
parent 2b6ef864e0
commit 23bbac941a
19 changed files with 204 additions and 24 deletions

View File

@@ -14,6 +14,7 @@ Synopsis
cmake_language(`EVAL`_ CODE <code>...)
cmake_language(`DEFER`_ <options>... CALL <command> [<arg>...])
cmake_language(`SET_DEPENDENCY_PROVIDER`_ <command> SUPPORTED_METHODS <methods>...)
cmake_language(`GET_MESSAGE_LOG_LEVEL`_ <out-var>)
Introduction
^^^^^^^^^^^^
@@ -491,3 +492,28 @@ calling the provider command recursively for the same dependency.
SET_DEPENDENCY_PROVIDER mycomp_provide_dependency
SUPPORTED_METHODS FIND_PACKAGE
)
Getting current message log level
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 3.25
.. _GET_MESSAGE_LOG_LEVEL:
.. _query_message_log_level:
.. code-block:: cmake
cmake_language(GET_MESSAGE_LOG_LEVEL <output_variable>)
Writes the current :command:`message` logging level
into the given ``<output_variable>``.
See :command:`message` for the possible logging levels.
The current message logging level can be set either using the ``--log-level``
command line option of the :manual:`cmake(1)` program or using
the :variable:`CMAKE_MESSAGE_LOG_LEVEL` variable.
If both the command line option and the variable are set, the command line
option takes precedence. If neither are set, the default logging level
is returned.

View File

@@ -249,6 +249,11 @@ Options
For backward compatibility reasons, ``--loglevel`` is also accepted as a
synonym for this option.
.. versionadded:: 3.25
See the :command:`cmake_language`
:ref:`cmake_language <query_message_log_level>` command for a way to query
the current message logging level.
``--log-context``
Enable the :command:`message` command outputting context attached to each
message.

View File

@@ -0,0 +1,6 @@
cmake-language_GET_MESSAGE_LOG_LEVEL
------------------------------------
* The :command:`cmake_language` command gained a new
``GET_MESSAGE_LOG_LEVEL`` sub-command. It can be used to
query the current message logging level.

View File

@@ -15,3 +15,8 @@ subsequent CMake runs will continue to use the chosen log level.
Projects should not set this variable, it is intended for users so that
they may control the log level according to their own needs.
.. versionadded:: 3.25
See the :command:`cmake_language`
:ref:`cmake_language <query_message_log_level>` command for a way to query
the current message logging level.

View File

@@ -19,10 +19,12 @@
#include "cmGlobalGenerator.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmRange.h"
#include "cmState.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmake.h"
namespace {
@@ -303,6 +305,27 @@ bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER(
return true;
}
bool cmCMakeLanguageCommandGET_MESSAGE_LOG_LEVEL(
std::vector<cmListFileArgument> const& args, cmExecutionStatus& status)
{
cmMakefile& makefile = status.GetMakefile();
std::vector<std::string> expandedArgs;
makefile.ExpandArguments(args, expandedArgs);
if (args.size() < 2 || expandedArgs.size() > 2) {
return FatalError(
status,
"sub-command GET_MESSAGE_LOG_LEVEL expects exactly one argument");
}
Message::LogLevel logLevel = makefile.GetCurrentLogLevel();
std::string outputValue = cmake::LogLevelToString(logLevel);
const std::string& outputVariable = expandedArgs[1];
makefile.AddDefinition(outputVariable, outputValue);
return true;
}
}
bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args,
@@ -451,5 +474,9 @@ bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args,
return cmCMakeLanguageCommandEVAL(args, status);
}
if (expArgs[expArg] == "GET_MESSAGE_LOG_LEVEL") {
return cmCMakeLanguageCommandGET_MESSAGE_LOG_LEVEL(args, status);
}
return FatalError(status, "called with unknown meta-operation");
}

View File

@@ -149,6 +149,29 @@ void cmMakefile::IssueMessage(MessageType t, std::string const& text) const
this->GetCMakeInstance()->IssueMessage(t, text, this->Backtrace);
}
Message::LogLevel cmMakefile::GetCurrentLogLevel() const
{
const cmake* cmakeInstance = this->GetCMakeInstance();
const Message::LogLevel logLevelCliOrDefault = cmakeInstance->GetLogLevel();
assert("Expected a valid log level here" &&
logLevelCliOrDefault != Message::LogLevel::LOG_UNDEFINED);
Message::LogLevel result = logLevelCliOrDefault;
// If the log-level was set via the command line option, it takes precedence
// over the CMAKE_MESSAGE_LOG_LEVEL variable.
if (!cmakeInstance->WasLogLevelSetViaCLI()) {
const Message::LogLevel logLevelFromVar = cmake::StringToLogLevel(
this->GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL"));
if (logLevelFromVar != Message::LogLevel::LOG_UNDEFINED) {
result = logLevelFromVar;
}
}
return result;
}
bool cmMakefile::CheckCMP0037(std::string const& targetName,
cmStateEnums::TargetType targetType) const
{

View File

@@ -925,6 +925,7 @@ public:
};
void IssueMessage(MessageType t, std::string const& text) const;
Message::LogLevel GetCurrentLogLevel() const;
/** Set whether or not to report a CMP0000 violation. */
void SetCheckCMP0000(bool b) { this->CheckCMP0000 = b; }

View File

@@ -157,18 +157,7 @@ bool cmMessageCommand(std::vector<std::string> const& args,
assert("Message log level expected to be set" &&
level != Message::LogLevel::LOG_UNDEFINED);
auto desiredLevel = mf.GetCMakeInstance()->GetLogLevel();
assert("Expected a valid log level here" &&
desiredLevel != Message::LogLevel::LOG_UNDEFINED);
// Command line option takes precedence over the cache variable
if (!mf.GetCMakeInstance()->WasLogLevelSetViaCLI()) {
const auto desiredLevelFromCache =
cmake::StringToLogLevel(mf.GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL"));
if (desiredLevelFromCache != Message::LogLevel::LOG_UNDEFINED) {
desiredLevel = desiredLevelFromCache;
}
}
Message::LogLevel desiredLevel = mf.GetCurrentLogLevel();
if (desiredLevel < level) {
// Suppress the message

View File

@@ -3,6 +3,7 @@
#include "cmake.h"
#include <algorithm>
#include <array>
#include <cstdio>
#include <cstdlib>
#include <cstring>
@@ -1402,21 +1403,32 @@ void cmake::SetArgs(const std::vector<std::string>& args)
#endif
}
Message::LogLevel cmake::StringToLogLevel(const std::string& levelStr)
namespace {
using LevelsPair = std::pair<cm::string_view, Message::LogLevel>;
using LevelsPairArray = std::array<LevelsPair, 7>;
const LevelsPairArray& getStringToLogLevelPairs()
{
using LevelsPair = std::pair<std::string, Message::LogLevel>;
static const std::vector<LevelsPair> levels = {
{ "error", Message::LogLevel::LOG_ERROR },
{ "warning", Message::LogLevel::LOG_WARNING },
{ "notice", Message::LogLevel::LOG_NOTICE },
{ "status", Message::LogLevel::LOG_STATUS },
{ "verbose", Message::LogLevel::LOG_VERBOSE },
{ "debug", Message::LogLevel::LOG_DEBUG },
{ "trace", Message::LogLevel::LOG_TRACE }
static const LevelsPairArray levels = {
{ { "error", Message::LogLevel::LOG_ERROR },
{ "warning", Message::LogLevel::LOG_WARNING },
{ "notice", Message::LogLevel::LOG_NOTICE },
{ "status", Message::LogLevel::LOG_STATUS },
{ "verbose", Message::LogLevel::LOG_VERBOSE },
{ "debug", Message::LogLevel::LOG_DEBUG },
{ "trace", Message::LogLevel::LOG_TRACE } }
};
return levels;
}
} // namespace
const auto levelStrLowCase = cmSystemTools::LowerCase(levelStr);
Message::LogLevel cmake::StringToLogLevel(cm::string_view levelStr)
{
const LevelsPairArray& levels = getStringToLogLevelPairs();
const auto levelStrLowCase =
cmSystemTools::LowerCase(std::string{ levelStr });
// NOLINTNEXTLINE(readability-qualified-auto)
const auto it = std::find_if(levels.cbegin(), levels.cend(),
[&levelStrLowCase](const LevelsPair& p) {
return p.first == levelStrLowCase;
@@ -1424,6 +1436,21 @@ Message::LogLevel cmake::StringToLogLevel(const std::string& levelStr)
return (it != levels.cend()) ? it->second : Message::LogLevel::LOG_UNDEFINED;
}
std::string cmake::LogLevelToString(Message::LogLevel level)
{
const LevelsPairArray& levels = getStringToLogLevelPairs();
// NOLINTNEXTLINE(readability-qualified-auto)
const auto it =
std::find_if(levels.cbegin(), levels.cend(),
[&level](const LevelsPair& p) { return p.second == level; });
const cm::string_view levelStrLowerCase =
(it != levels.cend()) ? it->first : "undefined";
std::string levelStrUpperCase =
cmSystemTools::UpperCase(std::string{ levelStrLowerCase });
return levelStrUpperCase;
}
cmake::TraceFormat cmake::StringToTraceFormat(const std::string& traceStr)
{
using TracePair = std::pair<std::string, TraceFormat>;

View File

@@ -458,7 +458,8 @@ public:
//! Get the selected log level for `message()` commands during the cmake run.
Message::LogLevel GetLogLevel() const { return this->MessageLogLevel; }
void SetLogLevel(Message::LogLevel level) { this->MessageLogLevel = level; }
static Message::LogLevel StringToLogLevel(const std::string& levelStr);
static Message::LogLevel StringToLogLevel(cm::string_view levelStr);
static std::string LogLevelToString(Message::LogLevel level);
static TraceFormat StringToTraceFormat(const std::string& levelStr);
bool HasCheckInProgress() const

View File

@@ -82,3 +82,61 @@ run_cmake(defer_get_call_id_var)
run_cmake(defer_missing_arg)
run_cmake(defer_missing_call)
run_cmake(defer_unknown_option)
# Default log level
run_cmake_command(
get_message_log_level_none
${CMAKE_COMMAND}
-P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
)
# Log level from cache
run_cmake_command(
get_message_log_level_cache
${CMAKE_COMMAND}
-DCMAKE_MESSAGE_LOG_LEVEL=TRACE
-P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
)
# Log level from regular variable
run_cmake_command(
get_message_log_level_var
${CMAKE_COMMAND}
-DNEW_LOG_LEVEL=TRACE
-P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
)
# Log level from command line
run_cmake_command(
get_message_log_level_cli
${CMAKE_COMMAND}
--log-level=DEBUG
-P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
)
# Log level from command line, it has higher priority over a cache variable
run_cmake_command(
get_message_log_level_cli_and_cache
${CMAKE_COMMAND}
--log-level=DEBUG
-DCMAKE_MESSAGE_LOG_LEVEL=TRACE
-P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
)
# Log level from command line, it has higher priority over a regular variable
run_cmake_command(
get_message_log_level_cli_and_var
${CMAKE_COMMAND}
--log-level=DEBUG
-DNEW_LOG_LEVEL=TRACE
-P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
)
# Log level from variable, it has higher priority over a cache variable
run_cmake_command(
get_message_log_level_var_and_cache
${CMAKE_COMMAND}
-DNEW_LOG_LEVEL=DEBUG
-DCMAKE_MESSAGE_LOG_LEVEL=TRACE
-P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
)

View File

@@ -0,0 +1,5 @@
if(NEW_LOG_LEVEL)
set(CMAKE_MESSAGE_LOG_LEVEL "${NEW_LOG_LEVEL}")
endif()
cmake_language(GET_MESSAGE_LOG_LEVEL log_level)
message(STATUS "log level is: ${log_level}")

View File

@@ -0,0 +1 @@
log level is: TRACE

View File

@@ -0,0 +1 @@
log level is: DEBUG

View File

@@ -0,0 +1 @@
log level is: DEBUG

View File

@@ -0,0 +1 @@
log level is: DEBUG

View File

@@ -0,0 +1 @@
log level is: STATUS

View File

@@ -0,0 +1 @@
log level is: TRACE

View File

@@ -0,0 +1 @@
log level is: DEBUG