Browse Source

Merge topic 'log_level_command'

23bbac941a Add cmake_language(GET_MESSAGE_LOG_LEVEL) sub command
2b6ef864e0 Move LogLevel enum out of cmake.h header

Acked-by: Kitware Robot <[email protected]>
Tested-by: buildbot <[email protected]>
Merge-request: !7348
Brad King 3 years ago
parent
commit
b1d8141534

+ 26 - 0
Help/command/cmake_language.rst

@@ -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.

+ 5 - 0
Help/manual/cmake.1.rst

@@ -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.

+ 6 - 0
Help/release/dev/cmake_language_GET_MESSAGE_LOG_LEVEL.rst

@@ -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.

+ 5 - 0
Help/variable/CMAKE_MESSAGE_LOG_LEVEL.rst

@@ -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.

+ 27 - 0
Source/cmCMakeLanguageCommand.cxx

@@ -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");
 }

+ 23 - 0
Source/cmMakefile.cxx

@@ -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
 {

+ 1 - 0
Source/cmMakefile.h

@@ -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; }

+ 27 - 38
Source/cmMessageCommand.cxx

@@ -81,94 +81,83 @@ bool cmMessageCommand(std::vector<std::string> const& args,
 
   auto type = MessageType::MESSAGE;
   auto fatal = false;
-  auto level = cmake::LogLevel::LOG_UNDEFINED;
+  auto level = Message::LogLevel::LOG_UNDEFINED;
   auto checkingType = CheckingType::UNDEFINED;
   if (*i == "SEND_ERROR") {
     type = MessageType::FATAL_ERROR;
-    level = cmake::LogLevel::LOG_ERROR;
+    level = Message::LogLevel::LOG_ERROR;
     ++i;
   } else if (*i == "FATAL_ERROR") {
     fatal = true;
     type = MessageType::FATAL_ERROR;
-    level = cmake::LogLevel::LOG_ERROR;
+    level = Message::LogLevel::LOG_ERROR;
     ++i;
   } else if (*i == "WARNING") {
     type = MessageType::WARNING;
-    level = cmake::LogLevel::LOG_WARNING;
+    level = Message::LogLevel::LOG_WARNING;
     ++i;
   } else if (*i == "AUTHOR_WARNING") {
     if (mf.IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") &&
         !mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) {
       fatal = true;
       type = MessageType::AUTHOR_ERROR;
-      level = cmake::LogLevel::LOG_ERROR;
+      level = Message::LogLevel::LOG_ERROR;
     } else if (!mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) {
       type = MessageType::AUTHOR_WARNING;
-      level = cmake::LogLevel::LOG_WARNING;
+      level = Message::LogLevel::LOG_WARNING;
     } else {
       return true;
     }
     ++i;
   } else if (*i == "CHECK_START") {
-    level = cmake::LogLevel::LOG_STATUS;
+    level = Message::LogLevel::LOG_STATUS;
     checkingType = CheckingType::CHECK_START;
     ++i;
   } else if (*i == "CHECK_PASS") {
-    level = cmake::LogLevel::LOG_STATUS;
+    level = Message::LogLevel::LOG_STATUS;
     checkingType = CheckingType::CHECK_PASS;
     ++i;
   } else if (*i == "CHECK_FAIL") {
-    level = cmake::LogLevel::LOG_STATUS;
+    level = Message::LogLevel::LOG_STATUS;
     checkingType = CheckingType::CHECK_FAIL;
     ++i;
   } else if (*i == "STATUS") {
-    level = cmake::LogLevel::LOG_STATUS;
+    level = Message::LogLevel::LOG_STATUS;
     ++i;
   } else if (*i == "VERBOSE") {
-    level = cmake::LogLevel::LOG_VERBOSE;
+    level = Message::LogLevel::LOG_VERBOSE;
     ++i;
   } else if (*i == "DEBUG") {
-    level = cmake::LogLevel::LOG_DEBUG;
+    level = Message::LogLevel::LOG_DEBUG;
     ++i;
   } else if (*i == "TRACE") {
-    level = cmake::LogLevel::LOG_TRACE;
+    level = Message::LogLevel::LOG_TRACE;
     ++i;
   } else if (*i == "DEPRECATION") {
     if (mf.IsOn("CMAKE_ERROR_DEPRECATED")) {
       fatal = true;
       type = MessageType::DEPRECATION_ERROR;
-      level = cmake::LogLevel::LOG_ERROR;
+      level = Message::LogLevel::LOG_ERROR;
     } else if (!mf.IsSet("CMAKE_WARN_DEPRECATED") ||
                mf.IsOn("CMAKE_WARN_DEPRECATED")) {
       type = MessageType::DEPRECATION_WARNING;
-      level = cmake::LogLevel::LOG_WARNING;
+      level = Message::LogLevel::LOG_WARNING;
     } else {
       return true;
     }
     ++i;
   } else if (*i == "NOTICE") {
     // `NOTICE` message type is going to be output to stderr
-    level = cmake::LogLevel::LOG_NOTICE;
+    level = Message::LogLevel::LOG_NOTICE;
     ++i;
   } else {
     // Messages w/o any type are `NOTICE`s
-    level = cmake::LogLevel::LOG_NOTICE;
+    level = Message::LogLevel::LOG_NOTICE;
   }
   assert("Message log level expected to be set" &&
-         level != cmake::LogLevel::LOG_UNDEFINED);
-
-  auto desiredLevel = mf.GetCMakeInstance()->GetLogLevel();
-  assert("Expected a valid log level here" &&
-         desiredLevel != cmake::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 != cmake::LogLevel::LOG_UNDEFINED) {
-      desiredLevel = desiredLevelFromCache;
-    }
-  }
+         level != Message::LogLevel::LOG_UNDEFINED);
+
+  Message::LogLevel desiredLevel = mf.GetCurrentLogLevel();
 
   if (desiredLevel < level) {
     // Suppress the message
@@ -178,17 +167,17 @@ bool cmMessageCommand(std::vector<std::string> const& args,
   auto message = cmJoin(cmMakeRange(i, args.cend()), "");
 
   switch (level) {
-    case cmake::LogLevel::LOG_ERROR:
-    case cmake::LogLevel::LOG_WARNING:
+    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, message, mf.GetBacktrace());
       break;
 
-    case cmake::LogLevel::LOG_NOTICE:
+    case Message::LogLevel::LOG_NOTICE:
       cmSystemTools::Message(IndentText(message, mf));
       break;
 
-    case cmake::LogLevel::LOG_STATUS:
+    case Message::LogLevel::LOG_STATUS:
       switch (checkingType) {
         case CheckingType::CHECK_START:
           mf.DisplayStatus(IndentText(message, mf), -1);
@@ -209,9 +198,9 @@ bool cmMessageCommand(std::vector<std::string> const& args,
       }
       break;
 
-    case cmake::LogLevel::LOG_VERBOSE:
-    case cmake::LogLevel::LOG_DEBUG:
-    case cmake::LogLevel::LOG_TRACE:
+    case Message::LogLevel::LOG_VERBOSE:
+    case Message::LogLevel::LOG_DEBUG:
+    case Message::LogLevel::LOG_TRACE:
       mf.DisplayStatus(IndentText(message, mf), -1);
       break;
 

+ 16 - 0
Source/cmMessageType.h

@@ -16,3 +16,19 @@ enum class MessageType
   DEPRECATION_ERROR,
   DEPRECATION_WARNING
 };
+
+namespace Message {
+
+/** \brief Define log level constants. */
+enum class LogLevel
+{
+  LOG_UNDEFINED,
+  LOG_ERROR,
+  LOG_WARNING,
+  LOG_NOTICE,
+  LOG_STATUS,
+  LOG_VERBOSE,
+  LOG_DEBUG,
+  LOG_TRACE
+};
+}

+ 42 - 12
Source/cmake.cxx

@@ -3,6 +3,7 @@
 #include "cmake.h"
 
 #include <algorithm>
+#include <array>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
@@ -955,7 +956,7 @@ void cmake::SetArgs(const std::vector<std::string>& args)
                      CommandArgument::Values::One,
                      [](std::string const& value, cmake* state) -> bool {
                        const auto logLevel = StringToLogLevel(value);
-                       if (logLevel == LogLevel::LOG_UNDEFINED) {
+                       if (logLevel == Message::LogLevel::LOG_UNDEFINED) {
                          cmSystemTools::Error(
                            "Invalid level specified for --log-level");
                          return false;
@@ -971,7 +972,7 @@ void cmake::SetArgs(const std::vector<std::string>& args)
                      CommandArgument::Values::One,
                      [](std::string const& value, cmake* state) -> bool {
                        const auto logLevel = StringToLogLevel(value);
-                       if (logLevel == LogLevel::LOG_UNDEFINED) {
+                       if (logLevel == Message::LogLevel::LOG_UNDEFINED) {
                          cmSystemTools::Error(
                            "Invalid level specified for --loglevel");
                          return false;
@@ -1402,23 +1403,52 @@ void cmake::SetArgs(const std::vector<std::string>& args)
 #endif
 }
 
-cmake::LogLevel cmake::StringToLogLevel(const std::string& levelStr)
-{
-  using LevelsPair = std::pair<std::string, LogLevel>;
-  static const std::vector<LevelsPair> levels = {
-    { "error", LogLevel::LOG_ERROR },     { "warning", LogLevel::LOG_WARNING },
-    { "notice", LogLevel::LOG_NOTICE },   { "status", LogLevel::LOG_STATUS },
-    { "verbose", LogLevel::LOG_VERBOSE }, { "debug", LogLevel::LOG_DEBUG },
-    { "trace", LogLevel::LOG_TRACE }
+namespace {
+using LevelsPair = std::pair<cm::string_view, Message::LogLevel>;
+using LevelsPairArray = std::array<LevelsPair, 7>;
+const LevelsPairArray& getStringToLogLevelPairs()
+{
+  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
+
+Message::LogLevel cmake::StringToLogLevel(cm::string_view levelStr)
+{
+  const LevelsPairArray& levels = getStringToLogLevelPairs();
 
-  const auto levelStrLowCase = cmSystemTools::LowerCase(levelStr);
+  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;
                                });
-  return (it != levels.cend()) ? it->second : LogLevel::LOG_UNDEFINED;
+  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)

+ 5 - 17
Source/cmake.h

@@ -119,19 +119,6 @@ public:
     FIND_PACKAGE_MODE
   };
 
-  /** \brief Define log level constants. */
-  enum LogLevel
-  {
-    LOG_UNDEFINED,
-    LOG_ERROR,
-    LOG_WARNING,
-    LOG_NOTICE,
-    LOG_STATUS,
-    LOG_VERBOSE,
-    LOG_DEBUG,
-    LOG_TRACE
-  };
-
   /** \brief Define supported trace formats **/
   enum TraceFormat
   {
@@ -469,9 +456,10 @@ public:
   bool WasLogLevelSetViaCLI() const { return this->LogLevelWasSetViaCLI; }
 
   //! Get the selected log level for `message()` commands during the cmake run.
-  LogLevel GetLogLevel() const { return this->MessageLogLevel; }
-  void SetLogLevel(LogLevel level) { this->MessageLogLevel = level; }
-  static LogLevel StringToLogLevel(const std::string& levelStr);
+  Message::LogLevel GetLogLevel() const { return this->MessageLogLevel; }
+  void SetLogLevel(Message::LogLevel level) { this->MessageLogLevel = level; }
+  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
@@ -732,7 +720,7 @@ private:
   std::set<std::string> DebugFindPkgs;
   std::set<std::string> DebugFindVars;
 
-  LogLevel MessageLogLevel = LogLevel::LOG_STATUS;
+  Message::LogLevel MessageLogLevel = Message::LogLevel::LOG_STATUS;
   bool LogLevelWasSetViaCLI = false;
   bool LogContext = false;
 

+ 58 - 0
Tests/RunCMake/cmake_language/RunCMakeTest.cmake

@@ -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
+  )

+ 5 - 0
Tests/RunCMake/cmake_language/get_message_log_level.cmake

@@ -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}")

+ 1 - 0
Tests/RunCMake/cmake_language/get_message_log_level_cache-stdout.txt

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

+ 1 - 0
Tests/RunCMake/cmake_language/get_message_log_level_cli-stdout.txt

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

+ 1 - 0
Tests/RunCMake/cmake_language/get_message_log_level_cli_and_cache-stdout.txt

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

+ 1 - 0
Tests/RunCMake/cmake_language/get_message_log_level_cli_and_var-stdout.txt

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

+ 1 - 0
Tests/RunCMake/cmake_language/get_message_log_level_none-stdout.txt

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

+ 1 - 0
Tests/RunCMake/cmake_language/get_message_log_level_var-stdout.txt

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

+ 1 - 0
Tests/RunCMake/cmake_language/get_message_log_level_var_and_cache-stdout.txt

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