Commit 25eef45f authored by Craig Scott's avatar Craig Scott Committed by Kitware Robot
Browse files

Merge topic 'feature/CMAKE_MESSAGE_CONTEXT'

7cf79f44 message: Support logging a context with each message
5bf85e25 message: Add new CMAKE_MESSAGE_LOG_LEVEL variable
aa59badd Tests: Message log level tests must fail on unwanted output
7c579f0a Help: Move CMAKE_MESSAGE_INDENT variable to correct section
4b021bd3

 message: Minor refactor to make code less verbose
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Merge-request: !3680
parents 001a1d26 7cf79f44
......@@ -59,12 +59,26 @@ The :manual:`curses interface <ccmake(1)>` shows ``STATUS`` to ``TRACE``
messages one at a time on a status line and other messages in an
interactive pop-up box. The ``--log-level`` command-line option to each of
these tools can be used to control which messages will be shown.
To make a log level persist between CMake runs, the
:variable:`CMAKE_MESSAGE_LOG_LEVEL` variable can be set instead.
Note that the command line option takes precedence over the cache variable.
Messages of log levels ``NOTICE`` and below will also have each line preceded
Messages of log levels ``NOTICE`` and below will have each line preceded
by the content of the :variable:`CMAKE_MESSAGE_INDENT` variable (converted to
a single string by concatenating its list items). For ``STATUS`` to ``TRACE``
messages, this indenting content will be inserted after the hyphens.
Messages of log levels ``NOTICE`` and below can also have each line preceded
with context of the form ``[some.context.example]``. The content between the
square brackets is obtained by converting the :variable:`CMAKE_MESSAGE_CONTEXT`
list variable to a dot-separated string. The message context will always
appear before any indenting content but after any automatically added leading
hyphens. By default, message context is not shown, it has to be explicitly
enabled by giving the :manual:`cmake <cmake(1)>` ``--log-context``
command-line option or by setting the :variable:`CMAKE_MESSAGE_CONTEXT_SHOW`
variable to true. See the :variable:`CMAKE_MESSAGE_CONTEXT` documentation for
usage examples.
CMake Warning and Error message text displays using a simple markup
language. Non-indented text is formatted in line-wrapped paragraphs
delimited by newlines. Indented text is considered pre-formatted.
......@@ -70,7 +70,6 @@ Variables that Provide Information
/variable/CMAKE_MAKE_PROGRAM
/variable/CMAKE_MATCH_COUNT
/variable/CMAKE_MATCH_n
/variable/CMAKE_MESSAGE_INDENT
/variable/CMAKE_MINIMUM_REQUIRED_VERSION
/variable/CMAKE_MINOR_VERSION
/variable/CMAKE_NETRC
......@@ -205,6 +204,10 @@ Variables that Change Behavior
/variable/CMAKE_LINK_DIRECTORIES_BEFORE
/variable/CMAKE_MFC_FLAG
/variable/CMAKE_MAXIMUM_RECURSION_DEPTH
/variable/CMAKE_MESSAGE_CONTEXT
/variable/CMAKE_MESSAGE_CONTEXT_SHOW
/variable/CMAKE_MESSAGE_INDENT
/variable/CMAKE_MESSAGE_LOG_LEVEL
/variable/CMAKE_MODULE_PATH
/variable/CMAKE_POLICY_DEFAULT_CMPNNNN
/variable/CMAKE_POLICY_WARNING_CMPNNNN
......
......@@ -206,9 +206,24 @@ Options
The :command:`message` command will only output messages of the specified
log level or higher. The default log level is ``STATUS``.
To make a log level persist between CMake runs, set
:variable:`CMAKE_MESSAGE_LOG_LEVEL` as a cache variable instead.
If both the command line option and the variable are given, the command line
option takes precedence.
For backward compatibility reasons, ``--loglevel`` is also accepted as a
synonym for this option.
``--log-context``
Enable the :command:`message` command outputting context attached to each
message.
This option turns on showing context for the current CMake run only.
To make showing the context persistent for all subsequent CMake runs, set
:variable:`CMAKE_MESSAGE_CONTEXT_SHOW` as a cache variable instead.
When this command line option is given, :variable:`CMAKE_MESSAGE_CONTEXT_SHOW`
is ignored.
``--debug-trycompile``
Do not delete the :command:`try_compile` build tree.
Only useful on one :command:`try_compile` at a time.
......
feature-CMAKE_MESSAGE_CONTEXT
-----------------------------
* The :variable:`CMAKE_MESSAGE_LOG_LEVEL` variable can now be used
to persist a log level between CMake runs, unlike the ``--log-level``
command line option which only applies to that particular run.
* The :command:`message` command learned to output context provided in
the :variable:`CMAKE_MESSAGE_CONTEXT` variable for log levels
``NOTICE`` and below. Enable this output with the new ``--log-context``
command-line option or :variable:`CMAKE_MESSAGE_CONTEXT_SHOW` variable.
CMAKE_MESSAGE_CONTEXT
---------------------
When enabled by the :manual:`cmake <cmake(1)>` ``--log-context`` command line
option or the :variable:`CMAKE_MESSAGE_CONTEXT_SHOW` variable, the
:command:`message` command converts the ``CMAKE_MESSAGE_CONTEXT`` list into a
dot-separated string surrounded by square brackets and prepends it to each line
for messages of log levels ``NOTICE`` and below.
For logging contexts to work effectively, projects should generally
``APPEND`` and ``POP_BACK`` an item to the current value of
``CMAKE_MESSAGE_CONTEXT`` rather than replace it.
Projects should not assume the message context at the top of the source tree
is empty, as there are scenarios where the context might have already been set
(e.g. hierarchical projects).
.. warning::
Valid context names are restricted to anything that could be used
as a CMake variable name. All names that begin with an underscore
or the string ``cmake_`` are also reserved for use by CMake and
should not be used by projects.
Example:
.. code-block:: cmake
function(bar)
list(APPEND CMAKE_MESSAGE_CONTEXT "bar")
message(VERBOSE "bar VERBOSE message")
endfunction()
function(baz)
list(APPEND CMAKE_MESSAGE_CONTEXT "baz")
message(DEBUG "baz DEBUG message")
endfunction()
function(foo)
list(APPEND CMAKE_MESSAGE_CONTEXT "foo")
bar()
message(TRACE "foo TRACE message")
baz()
endfunction()
list(APPEND CMAKE_MESSAGE_CONTEXT "top")
message(VERBOSE "Before `foo`")
foo()
message(VERBOSE "After `foo`")
list(POP_BACK CMAKE_MESSAGE_CONTEXT)
Which results in the following output:
.. code-block:: none
-- [top] Before `foo`
-- [top.foo.bar] bar VERBOSE message
-- [top.foo] foo TRACE message
-- [top.foo.baz] baz DEBUG message
-- [top] After `foo`
CMAKE_MESSAGE_CONTEXT_SHOW
--------------------------
Setting this variable to true enables showing a context with each line
logged by the :command:`message` command (see :variable:`CMAKE_MESSAGE_CONTEXT`
for how the context itself is specified).
This variable is an alternative to providing the ``--log-context`` option
on the :manual:`cmake <cmake(1)>` command line. Whereas the command line
option will apply only to that one CMake run, setting
``CMAKE_MESSAGE_CONTEXT_SHOW`` to true as a cache variable will ensure that
subsequent CMake runs will continue to show the message context.
Projects should not set ``CMAKE_MESSAGE_CONTEXT_SHOW``. It is intended for
users so that they may control whether or not to include context with messages.
CMAKE_MESSAGE_LOG_LEVEL
-----------------------
When set, this variable specifies the logging level used by the
:command:`message` command. Valid values are the same as those for the
``--log-level`` command line option of the :manual:`cmake(1)` program.
If this variable is set and the ``--log-level`` command line option is
given, the command line option takes precedence.
The main advantage to using this variable is to make a log level persist
between CMake runs. Setting it as a cache variable will ensure that
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.
......@@ -21,6 +21,9 @@ bool cmMessageCommand(std::vector<std::string> const& args,
status.SetError("called with incorrect number of arguments");
return false;
}
auto& mf = status.GetMakefile();
auto i = args.cbegin();
auto type = MessageType::MESSAGE;
......@@ -40,13 +43,12 @@ bool cmMessageCommand(std::vector<std::string> const& args,
level = cmake::LogLevel::LOG_WARNING;
++i;
} else if (*i == "AUTHOR_WARNING") {
if (status.GetMakefile().IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") &&
!status.GetMakefile().IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) {
if (mf.IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") &&
!mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) {
fatal = true;
type = MessageType::AUTHOR_ERROR;
level = cmake::LogLevel::LOG_ERROR;
} else if (!status.GetMakefile().IsOn(
"CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) {
} else if (!mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) {
type = MessageType::AUTHOR_WARNING;
level = cmake::LogLevel::LOG_WARNING;
} else {
......@@ -66,12 +68,12 @@ bool cmMessageCommand(std::vector<std::string> const& args,
level = cmake::LogLevel::LOG_TRACE;
++i;
} else if (*i == "DEPRECATION") {
if (status.GetMakefile().IsOn("CMAKE_ERROR_DEPRECATED")) {
if (mf.IsOn("CMAKE_ERROR_DEPRECATED")) {
fatal = true;
type = MessageType::DEPRECATION_ERROR;
level = cmake::LogLevel::LOG_ERROR;
} else if (!status.GetMakefile().IsSet("CMAKE_WARN_DEPRECATED") ||
status.GetMakefile().IsOn("CMAKE_WARN_DEPRECATED")) {
} else if (!mf.IsSet("CMAKE_WARN_DEPRECATED") ||
mf.IsOn("CMAKE_WARN_DEPRECATED")) {
type = MessageType::DEPRECATION_WARNING;
level = cmake::LogLevel::LOG_WARNING;
} else {
......@@ -89,10 +91,19 @@ bool cmMessageCommand(std::vector<std::string> const& args,
assert("Message log level expected to be set" &&
level != cmake::LogLevel::LOG_UNDEFINED);
auto desiredLevel = status.GetMakefile().GetCMakeInstance()->GetLogLevel();
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;
}
}
if (desiredLevel < level) {
// Suppress the message
return true;
......@@ -101,25 +112,32 @@ bool cmMessageCommand(std::vector<std::string> const& args,
auto message = cmJoin(cmMakeRange(i, args.cend()), "");
if (cmake::LogLevel::LOG_NOTICE <= level) {
// Check if any indentation has requested:
// `CMAKE_MESSAGE_INDENT` is a list of "padding" pieces
// to be joined and prepended to the message lines.
auto indent = cmJoin(cmExpandedList(status.GetMakefile().GetSafeDefinition(
"CMAKE_MESSAGE_INDENT")),
"");
// Make every line of the `message` indented
// NOTE Can't reuse `cmDocumentationFormatter::PrintPreformatted`
// here cuz it appends `\n` to the EOM ;-(
cmSystemTools::ReplaceString(message, "\n", "\n" + indent);
message = indent + message;
auto indent =
cmJoin(cmExpandedList(mf.GetSafeDefinition("CMAKE_MESSAGE_INDENT")), "");
if (!indent.empty()) {
cmSystemTools::ReplaceString(message, "\n", "\n" + indent);
message = indent + message;
}
const auto showContext = mf.GetCMakeInstance()->GetShowLogContext() ||
mf.IsOn("CMAKE_MESSAGE_CONTEXT_SHOW");
if (showContext) {
// Output the current context (if any)
auto context = cmJoin(
cmExpandedList(mf.GetSafeDefinition("CMAKE_MESSAGE_CONTEXT")), ".");
if (!context.empty()) {
context = "[" + context + "] ";
cmSystemTools::ReplaceString(message, "\n", "\n" + context);
message = context + message;
}
}
}
switch (level) {
case cmake::LogLevel::LOG_ERROR:
case cmake::LogLevel::LOG_WARNING:
// we've overridden the message type, above, so display it directly
status.GetMakefile().GetMessenger()->DisplayMessage(
type, message, status.GetMakefile().GetBacktrace());
mf.GetMessenger()->DisplayMessage(type, message, mf.GetBacktrace());
break;
case cmake::LogLevel::LOG_NOTICE:
......@@ -130,7 +148,7 @@ bool cmMessageCommand(std::vector<std::string> const& args,
case cmake::LogLevel::LOG_VERBOSE:
case cmake::LogLevel::LOG_DEBUG:
case cmake::LogLevel::LOG_TRACE:
status.GetMakefile().DisplayStatus(message, -1);
mf.DisplayStatus(message, -1);
break;
default:
......
......@@ -733,6 +733,7 @@ void cmake::SetArgs(const std::vector<std::string>& args)
return;
}
this->SetLogLevel(logLevel);
this->LogLevelWasSetViaCLI = true;
} else if (arg.find("--loglevel=", 0) == 0) {
// This is supported for backward compatibility. This option only
// appeared in the 3.15.x release series and was renamed to
......@@ -744,6 +745,9 @@ void cmake::SetArgs(const std::vector<std::string>& args)
return;
}
this->SetLogLevel(logLevel);
this->LogLevelWasSetViaCLI = true;
} else if (arg == "--log-context") {
this->SetShowLogContext(true);
} else if (arg.find("--trace-expand", 0) == 0) {
std::cout << "Running with expanded trace output on.\n";
this->SetTrace(true);
......
......@@ -380,6 +380,8 @@ public:
*/
cmFileTimeCache* GetFileTimeCache() { return this->FileTimeCache.get(); }
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; }
......@@ -389,6 +391,10 @@ public:
bool GetDebugOutput() { return this->DebugOutput; }
void SetDebugOutputOn(bool b) { this->DebugOutput = b; }
//! Should `message` command display context.
bool GetShowLogContext() const { return this->LogContext; }
void SetShowLogContext(bool b) { this->LogContext = b; }
//! Do we want trace output during the cmake run.
bool GetTrace() { return this->Trace; }
void SetTrace(bool b) { this->Trace = b; }
......@@ -587,6 +593,8 @@ private:
std::vector<std::string> TraceOnlyThisSources;
LogLevel MessageLogLevel = LogLevel::LOG_STATUS;
bool LogLevelWasSetViaCLI = false;
bool LogContext = false;
void UpdateConversionPathTable();
......
......@@ -73,6 +73,7 @@ const char* cmDocumentationOptions[][2] = {
{ "--log-level=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>",
"Set the verbosity of messages from CMake files. "
"--loglevel is also accepted for backward compatibility reasons." },
{ "--log-context", "Prepend log messages with context, if given" },
{ "--debug-trycompile",
"Do not delete the try_compile build tree. Only "
"useful on one try_compile at a time." },
......
......@@ -55,6 +55,11 @@ foreach(opt IN ITEMS loglevel log-level)
)
endforeach()
run_cmake_command(
message-log-level-override
${CMAKE_COMMAND} --log-level=debug -DCMAKE_MESSAGE_LOG_LEVEL=TRACE -P ${RunCMake_SOURCE_DIR}/message-all-loglevels.cmake
)
run_cmake_command(
message-indent
${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/message-indent.cmake
......@@ -63,3 +68,18 @@ run_cmake_command(
message-indent-multiline
${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/message-indent-multiline.cmake
)
run_cmake_command(
message-context-cli
${CMAKE_COMMAND} --log-level=trace --log-context -P ${RunCMake_SOURCE_DIR}/message-context.cmake
)
run_cmake_command(
message-context-cache
${CMAKE_COMMAND} -DCMAKE_MESSAGE_LOG_LEVEL=TRACE -DCMAKE_MESSAGE_CONTEXT_SHOW=ON -P ${RunCMake_SOURCE_DIR}/message-context.cmake
)
run_cmake_command(
message-context-cli-wins-cache
${CMAKE_COMMAND} --log-level=verbose --log-context -DCMAKE_MESSAGE_CONTEXT_SHOW=OFF -P ${RunCMake_SOURCE_DIR}/message-context.cmake
)
-- Begin context output test
-- \[top\] Top: before
-- \[top\.foo\.bar\] <-- indent -->bar VERBOSE message
-- \[top\.foo\] foo TRACE message
-- \[top\.foo\.baz\] This is the multi-line
\[top\.foo\.baz\] baz DEBUG message
-- \[top\] Top: after
-- End of context output test
-- Begin context output test
-- \[top\] Top: before
-- \[top\.foo\.bar\] <-- indent -->bar VERBOSE message
-- \[top\.foo\] foo TRACE message
-- \[top\.foo\.baz\] This is the multi-line
\[top\.foo\.baz\] baz DEBUG message
-- \[top\] Top: after
-- End of context output test
-- Begin context output test
-- \[top\] Top: before
-- \[top\.foo\.bar\] <-- indent -->bar VERBOSE message
-- \[top\] Top: after
-- End of context output test
function(bar)
list(APPEND CMAKE_MESSAGE_CONTEXT "bar")
list(APPEND CMAKE_MESSAGE_INDENT "<-- indent -->")
message(VERBOSE "bar VERBOSE message")
endfunction()
function(baz)
list(APPEND CMAKE_MESSAGE_CONTEXT "baz")
message(DEBUG "This is the multi-line\nbaz DEBUG message")
endfunction()
function(foo)
list(APPEND CMAKE_MESSAGE_CONTEXT "foo")
bar()
message(TRACE "foo TRACE message")
baz()
endfunction()
message(STATUS "Begin context output test")
list(APPEND CMAKE_MESSAGE_CONTEXT "top")
message(STATUS "Top: before")
foo()
message(STATUS "Top: after")
list(POP_BACK CMAKE_MESSAGE_CONTEXT)
message(STATUS "End of context output test")
-- STATUS message
-- VERBOSE message
-- DEBUG message
-- DEBUG message$
^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$
-- STATUS message
-- VERBOSE message
-- DEBUG message$
-- STATUS message
-- VERBOSE message
-- DEBUG message
-- TRACE message
-- TRACE message$
-- STATUS message
-- VERBOSE message
-- VERBOSE message$
-- STATUS message
-- VERBOSE message
-- DEBUG message
-- DEBUG message$
-- STATUS message
-- VERBOSE message
-- DEBUG message
-- TRACE message
-- TRACE message$
-- STATUS message
-- VERBOSE message
-- VERBOSE message$
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment