diff options
author | Alex Turbov <i.zaufi@gmail.com> | 2019-04-27 07:31:03 (GMT) |
---|---|---|
committer | Craig Scott <craig.scott@crascit.com> | 2019-04-28 12:45:44 (GMT) |
commit | 6cc93b370ef59208b9b468ebc9d84cc02e3cbd85 (patch) | |
tree | d3dfd8c1ae2826e369ce504ffb483388cb6ddeaf | |
parent | 377d1b7896e07a171bcfe8da6d9dcda6545052c2 (diff) | |
download | CMake-6cc93b370ef59208b9b468ebc9d84cc02e3cbd85.zip CMake-6cc93b370ef59208b9b468ebc9d84cc02e3cbd85.tar.gz CMake-6cc93b370ef59208b9b468ebc9d84cc02e3cbd85.tar.bz2 |
message(): Add support for log levels
Relates: #18943
Co-Authored-By: Craig Scott <craig.scott@crascit.com>
24 files changed, 303 insertions, 18 deletions
diff --git a/Auxiliary/bash-completion/cmake b/Auxiliary/bash-completion/cmake index 8c0c5e8..638b1c4 100644 --- a/Auxiliary/bash-completion/cmake +++ b/Auxiliary/bash-completion/cmake @@ -116,6 +116,9 @@ _cmake() 2>/dev/null )' -- "$quoted" ) ) return ;; + --loglevel) + COMPREPLY=( $(compgen -W 'error warning notice status verbose debug trace' -- $cur ) ) + ;; --help-command) COMPREPLY=( $( compgen -W '$( cmake --help-command-list 2>/dev/null| grep -v "^cmake version " )' -- "$cur" ) ) diff --git a/Help/command/message.rst b/Help/command/message.rst index a3c3a89..3f9216a 100644 --- a/Help/command/message.rst +++ b/Help/command/message.rst @@ -9,24 +9,56 @@ Display a message to the user. The optional ``<mode>`` keyword determines the type of message: -:: - - (none) = Important information - STATUS = Incidental information - WARNING = CMake Warning, continue processing - AUTHOR_WARNING = CMake Warning (dev), continue processing - SEND_ERROR = CMake Error, continue processing, - but skip generation - FATAL_ERROR = CMake Error, stop processing and generation - DEPRECATION = CMake Deprecation Error or Warning if variable - CMAKE_ERROR_DEPRECATED or CMAKE_WARN_DEPRECATED - is enabled, respectively, else no message. - -The CMake command-line tool displays STATUS messages on stdout and all -other message types on stderr. The CMake GUI displays all messages in -its log area. The interactive dialogs (ccmake and CMakeSetup) show -``STATUS`` messages one at a time on a status line and other messages in -interactive pop-up boxes. +``FATAL_ERROR`` + CMake Error, stop processing and generation. + +``SEND_ERROR`` + CMake Error, continue processing, but skip generation. + +``WARNING`` + CMake Warning, continue processing. + +``AUTHOR_WARNING`` + CMake Warning (dev), continue processing. + +``DEPRECATION`` + CMake Deprecation Error or Warning if variable + :variable:`CMAKE_ERROR_DEPRECATED` or :variable:`CMAKE_WARN_DEPRECATED` + is enabled, respectively, else no message. + +(none) or ``NOTICE`` + Important message printed to stderr to attract user's attention. + +``STATUS`` + The main interesting messages that project users might be interested in. + Ideally these should be concise, no more than a single line, but still + informative. + +``VERBOSE`` + Detailed informational messages intended for project users. These messages + should provide additional details that won't be of interest in most cases, + but which may be useful to those building the project when they want deeper + insight into what's happening. + +``DEBUG`` + Detailed informational messages intended for developers working on the + project itself as opposed to users who just want to build it. These messages + will not typically be of interest to other users building the project and + will often be closely related to internal implementation details. + +``TRACE`` + Fine-grained messages with very low-level implementation details. Messages + using this log level would normally only be temporary and would expect to be + removed before releasing the project, packaging up the files, etc. + +The CMake command-line tool displays ``STATUS`` to ``TRACE`` messages on stdout +with the message preceded by two hyphens and a space. All other message types +are sent to stderr and are not prefixed with hyphens. The CMake GUI displays +all messages in its log area. The interactive dialogs (:manual:`ccmake(1)` +and :manual:`cmake-gui(1)`) show ``STATUS`` to ``TRACE`` messages one at a +time on a status line and other messages in interactive pop-up boxes. +The ``--loglevel`` command-line option to each of these tools can be used to +control which messages will be shown. CMake Warning and Error message text displays using a simple markup language. Non-indented text is formatted in line-wrapped paragraphs diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index 5b88694..3e1680c 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst @@ -200,6 +200,12 @@ Options from the top of a binary tree for a CMake project it will dump additional information such as the cache, log files etc. +``--loglevel=<error|warning|notice|status|verbose|debug|trace>`` + Set the log level. + + The :command:`message` command will only output messages of the specified + log level or higher. The default log level is ``status``. + ``--debug-trycompile`` Do not delete the :command:`try_compile` build tree. Only useful on one :command:`try_compile` at a time. diff --git a/Help/release/dev/new-message-types-and-logging.rst b/Help/release/dev/new-message-types-and-logging.rst new file mode 100644 index 0000000..cd470bb --- /dev/null +++ b/Help/release/dev/new-message-types-and-logging.rst @@ -0,0 +1,7 @@ +new-message-types-and-logging +----------------------------- + +* The :command:`message` command learned new types: ``NOTICE``, ``VERBOSE``, + ``DEBUG`` and ``TRACE``. + +* The :manual:`cmake(1)` command learned a new CLI option ``--loglevel``. diff --git a/Source/cmMessageCommand.cxx b/Source/cmMessageCommand.cxx index 2724030..7242037 100644 --- a/Source/cmMessageCommand.cxx +++ b/Source/cmMessageCommand.cxx @@ -8,6 +8,9 @@ #include "cmMessenger.h" #include "cmRange.h" #include "cmSystemTools.h" +#include "cmake.h" + +#include <cassert> class cmExecutionStatus; @@ -24,41 +27,80 @@ bool cmMessageCommand::InitialPass(std::vector<std::string> const& args, MessageType type = MessageType::MESSAGE; bool status = false; bool fatal = false; + auto level = cmake::LogLevel::LOG_UNDEFINED; if (*i == "SEND_ERROR") { type = MessageType::FATAL_ERROR; + level = cmake::LogLevel::LOG_ERROR; ++i; } else if (*i == "FATAL_ERROR") { fatal = true; type = MessageType::FATAL_ERROR; + level = cmake::LogLevel::LOG_ERROR; ++i; } else if (*i == "WARNING") { type = MessageType::WARNING; + level = cmake::LogLevel::LOG_WARNING; ++i; } else if (*i == "AUTHOR_WARNING") { if (this->Makefile->IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") && !this->Makefile->IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) { fatal = true; type = MessageType::AUTHOR_ERROR; + level = cmake::LogLevel::LOG_ERROR; } else if (!this->Makefile->IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) { type = MessageType::AUTHOR_WARNING; + level = cmake::LogLevel::LOG_WARNING; } else { return true; } ++i; } else if (*i == "STATUS") { status = true; + level = cmake::LogLevel::LOG_STATUS; + ++i; + } else if (*i == "VERBOSE") { + status = true; + level = cmake::LogLevel::LOG_VERBOSE; + ++i; + } else if (*i == "DEBUG") { + status = true; + level = cmake::LogLevel::LOG_DEBUG; + ++i; + } else if (*i == "TRACE") { + status = true; + level = cmake::LogLevel::LOG_TRACE; ++i; } else if (*i == "DEPRECATION") { if (this->Makefile->IsOn("CMAKE_ERROR_DEPRECATED")) { fatal = true; type = MessageType::DEPRECATION_ERROR; + level = cmake::LogLevel::LOG_ERROR; } else if ((!this->Makefile->IsSet("CMAKE_WARN_DEPRECATED") || this->Makefile->IsOn("CMAKE_WARN_DEPRECATED"))) { type = MessageType::DEPRECATION_WARNING; + level = cmake::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; + ++i; + } else { + // Messages w/o any type are `NOTICE`s + level = cmake::LogLevel::LOG_NOTICE; + } + assert("Message log level expected to be set" && + level != cmake::LogLevel::LOG_UNDEFINED); + + auto desiredLevel = this->Makefile->GetCMakeInstance()->GetLogLevel(); + assert("Expected a valid log level here" && + desiredLevel != cmake::LogLevel::LOG_UNDEFINED); + + if (desiredLevel < level) { + // Suppress the message + return true; } std::string message = cmJoin(cmMakeRange(i, args.end()), std::string()); diff --git a/Source/cmake.cxx b/Source/cmake.cxx index fc24ac0..121d12d 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -718,6 +718,14 @@ void cmake::SetArgs(const std::vector<std::string>& args) } else if (arg.find("--debug-output", 0) == 0) { std::cout << "Running with debug output on.\n"; this->SetDebugOutputOn(true); + } else if (arg.find("--loglevel=", 0) == 0) { + const auto logLevel = + StringToLogLevel(arg.substr(sizeof("--loglevel=") - 1)); + if (logLevel == LogLevel::LOG_UNDEFINED) { + cmSystemTools::Error("Invalid level specified for --loglevel"); + return; + } + this->SetLogLevel(logLevel); } else if (arg.find("--trace-expand", 0) == 0) { std::cout << "Running with expanded trace output on.\n"; this->SetTrace(true); @@ -828,6 +836,25 @@ void cmake::SetArgs(const std::vector<std::string>& args) } } +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 } + }; + + const auto levelStrLowCase = cmSystemTools::LowerCase(levelStr); + + 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; +} + void cmake::SetDirectoriesFromFile(const char* arg) { // Check if the argument refers to a CMakeCache.txt or diff --git a/Source/cmake.h b/Source/cmake.h index 8b4b396..4a345cf 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -96,6 +96,19 @@ 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 + }; + struct GeneratorInfo { std::string name; @@ -331,6 +344,11 @@ public: */ cmFileTimeCache* GetFileTimeCache() { return this->FileTimeCache; } + // 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); + // Do we want debug output during the cmake run. bool GetDebugOutput() { return this->DebugOutput; } void SetDebugOutputOn(bool b) { this->DebugOutput = b; } @@ -524,6 +542,8 @@ private: std::vector<std::string> TraceOnlyThisSources; + LogLevel MessageLogLevel = LogLevel::LOG_STATUS; + void UpdateConversionPathTable(); // Print a list of valid generators to stderr. diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index d70c9d9..5631d10 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -95,6 +95,8 @@ static const char* cmDocumentationOptions[][2] = { "Generate graphviz of dependencies, see " "CMakeGraphVizOptions.cmake for more." }, { "--system-information [file]", "Dump information about this system." }, + { "--loglevel=<error|warn|notice|status|verbose|debug|trace>", + "Set the verbosity of messages from CMake files." }, { "--debug-trycompile", "Do not delete the try_compile build tree. Only " "useful on one try_compile at a time." }, diff --git a/Tests/RunCMake/message/RunCMakeTest.cmake b/Tests/RunCMake/message/RunCMakeTest.cmake index 24dad03..cecfc7f 100644 --- a/Tests/RunCMake/message/RunCMakeTest.cmake +++ b/Tests/RunCMake/message/RunCMakeTest.cmake @@ -10,3 +10,45 @@ run_cmake(warnmessage) # separately run_cmake(errormessage_deprecated) run_cmake(errormessage_dev) + +run_cmake_command( + message-loglevel-invalid + ${CMAKE_COMMAND} --loglevel=blah -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake + ) + +# Checking various combinations of `message(...)` and log levels `WARNING` to `TRACE` +# - no CLI option -> `WARNING` to `STATUS` output +run_cmake_command( + message-loglevel-default + ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake + ) +# - Only `WARNING` output +run_cmake_command( + message-loglevel-warning + ${CMAKE_COMMAND} --loglevel=warning -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake + ) +# - Only `WARNING` and `NOTICE` output +run_cmake_command( + message-loglevel-notice + ${CMAKE_COMMAND} --loglevel=notice -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake + ) +# - `WARNING` to `STATUS` output +run_cmake_command( + message-loglevel-status + ${CMAKE_COMMAND} --loglevel=status -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake + ) +# - `WARNING` to `VERBOSE` output +run_cmake_command( + message-loglevel-verbose + ${CMAKE_COMMAND} --loglevel=verbose -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake + ) +# - `WARNING` to `DEBUG` output +run_cmake_command( + message-loglevel-debug + ${CMAKE_COMMAND} --loglevel=debug -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake + ) +# - `WARNING` to `TRACE` output +run_cmake_command( + message-loglevel-trace + ${CMAKE_COMMAND} --loglevel=trace -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake + ) diff --git a/Tests/RunCMake/message/message-all-loglevels.cmake b/Tests/RunCMake/message/message-all-loglevels.cmake new file mode 100644 index 0000000..f8d8841 --- /dev/null +++ b/Tests/RunCMake/message/message-all-loglevels.cmake @@ -0,0 +1,10 @@ +# Produce a message for everything except FATAL_ERROR and SEND_ERROR +message(DEPRECATION "Deprecation warning") +message(AUTHOR_WARNING "Author warning message") +message(WARNING "Warning message") +message("Default NOTICE message") +message(NOTICE "NOTICE message") +message(STATUS "STATUS message") +message(VERBOSE "VERBOSE message") +message(DEBUG "DEBUG message") +message(TRACE "TRACE message") diff --git a/Tests/RunCMake/message/message-loglevel-debug-stderr.txt b/Tests/RunCMake/message/message-loglevel-debug-stderr.txt new file mode 100644 index 0000000..efec736 --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-debug-stderr.txt @@ -0,0 +1,12 @@ +^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): + Deprecation warning ++ +CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): + Author warning message +This warning is for project developers\. Use -Wno-dev to suppress it\. ++ +CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): + Warning message ++ +Default NOTICE message +NOTICE message$ diff --git a/Tests/RunCMake/message/message-loglevel-debug-stdout.txt b/Tests/RunCMake/message/message-loglevel-debug-stdout.txt new file mode 100644 index 0000000..1452137 --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-debug-stdout.txt @@ -0,0 +1,3 @@ +-- STATUS message +-- VERBOSE message +-- DEBUG message diff --git a/Tests/RunCMake/message/message-loglevel-default-stderr.txt b/Tests/RunCMake/message/message-loglevel-default-stderr.txt new file mode 100644 index 0000000..efec736 --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-default-stderr.txt @@ -0,0 +1,12 @@ +^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): + Deprecation warning ++ +CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): + Author warning message +This warning is for project developers\. Use -Wno-dev to suppress it\. ++ +CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): + Warning message ++ +Default NOTICE message +NOTICE message$ diff --git a/Tests/RunCMake/message/message-loglevel-default-stdout.txt b/Tests/RunCMake/message/message-loglevel-default-stdout.txt new file mode 100644 index 0000000..809f4cc --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-default-stdout.txt @@ -0,0 +1 @@ +-- STATUS message diff --git a/Tests/RunCMake/message/message-loglevel-invalid-result.txt b/Tests/RunCMake/message/message-loglevel-invalid-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-invalid-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/message/message-loglevel-invalid-stderr.txt b/Tests/RunCMake/message/message-loglevel-invalid-stderr.txt new file mode 100644 index 0000000..f54d0f8 --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-invalid-stderr.txt @@ -0,0 +1 @@ +CMake Error: Invalid level specified for --loglevel diff --git a/Tests/RunCMake/message/message-loglevel-notice-stderr.txt b/Tests/RunCMake/message/message-loglevel-notice-stderr.txt new file mode 100644 index 0000000..efec736 --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-notice-stderr.txt @@ -0,0 +1,12 @@ +^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): + Deprecation warning ++ +CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): + Author warning message +This warning is for project developers\. Use -Wno-dev to suppress it\. ++ +CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): + Warning message ++ +Default NOTICE message +NOTICE message$ diff --git a/Tests/RunCMake/message/message-loglevel-status-stderr.txt b/Tests/RunCMake/message/message-loglevel-status-stderr.txt new file mode 100644 index 0000000..efec736 --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-status-stderr.txt @@ -0,0 +1,12 @@ +^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): + Deprecation warning ++ +CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): + Author warning message +This warning is for project developers\. Use -Wno-dev to suppress it\. ++ +CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): + Warning message ++ +Default NOTICE message +NOTICE message$ diff --git a/Tests/RunCMake/message/message-loglevel-status-stdout.txt b/Tests/RunCMake/message/message-loglevel-status-stdout.txt new file mode 100644 index 0000000..809f4cc --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-status-stdout.txt @@ -0,0 +1 @@ +-- STATUS message diff --git a/Tests/RunCMake/message/message-loglevel-trace-stderr.txt b/Tests/RunCMake/message/message-loglevel-trace-stderr.txt new file mode 100644 index 0000000..efec736 --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-trace-stderr.txt @@ -0,0 +1,12 @@ +^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): + Deprecation warning ++ +CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): + Author warning message +This warning is for project developers\. Use -Wno-dev to suppress it\. ++ +CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): + Warning message ++ +Default NOTICE message +NOTICE message$ diff --git a/Tests/RunCMake/message/message-loglevel-trace-stdout.txt b/Tests/RunCMake/message/message-loglevel-trace-stdout.txt new file mode 100644 index 0000000..1cfce6f --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-trace-stdout.txt @@ -0,0 +1,4 @@ +-- STATUS message +-- VERBOSE message +-- DEBUG message +-- TRACE message diff --git a/Tests/RunCMake/message/message-loglevel-verbose-stderr.txt b/Tests/RunCMake/message/message-loglevel-verbose-stderr.txt new file mode 100644 index 0000000..efec736 --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-verbose-stderr.txt @@ -0,0 +1,12 @@ +^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): + Deprecation warning ++ +CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): + Author warning message +This warning is for project developers\. Use -Wno-dev to suppress it\. ++ +CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): + Warning message ++ +Default NOTICE message +NOTICE message$ diff --git a/Tests/RunCMake/message/message-loglevel-verbose-stdout.txt b/Tests/RunCMake/message/message-loglevel-verbose-stdout.txt new file mode 100644 index 0000000..c15d43f --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-verbose-stdout.txt @@ -0,0 +1,2 @@ +-- STATUS message +-- VERBOSE message diff --git a/Tests/RunCMake/message/message-loglevel-warning-stderr.txt b/Tests/RunCMake/message/message-loglevel-warning-stderr.txt new file mode 100644 index 0000000..c721b06 --- /dev/null +++ b/Tests/RunCMake/message/message-loglevel-warning-stderr.txt @@ -0,0 +1,9 @@ +^CMake Deprecation Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:2 \(message\): + Deprecation warning ++ +CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:3 \(message\): + Author warning message +This warning is for project developers\. Use -Wno-dev to suppress it\. ++ +CMake Warning at.*/Tests/RunCMake/message/message-all-loglevels\.cmake:4 \(message\): + Warning message$ |