mirror of
https://github.com/Kitware/CMake.git
synced 2025-12-31 19:00:54 -06:00
message: New message types to mark checks performed by CMake
Closes #19638. Co-Authored-By: Craig Scott <craig.scott@crascit.com>
This commit is contained in:
@@ -1,13 +1,33 @@
|
||||
message
|
||||
-------
|
||||
|
||||
Display a message to the user.
|
||||
Log a message.
|
||||
|
||||
Synopsis
|
||||
^^^^^^^^
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
`General messages`_
|
||||
message([<mode>] "message text" ...)
|
||||
|
||||
`Reporting checks`_
|
||||
message(<checkState> "message text" ...)
|
||||
|
||||
|
||||
General messages
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
message([<mode>] "message to display" ...)
|
||||
message([<mode>] "message text" ...)
|
||||
|
||||
The optional ``<mode>`` keyword determines the type of message:
|
||||
Record the specified message text in the log. If more than one message
|
||||
string is given, they are concatenated into a single message with no
|
||||
separator between the strings.
|
||||
|
||||
The optional ``<mode>`` keyword determines the type of message, which
|
||||
influences the way the message is handled:
|
||||
|
||||
``FATAL_ERROR``
|
||||
CMake Error, stop processing and generation.
|
||||
@@ -82,3 +102,81 @@ 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.
|
||||
|
||||
|
||||
Reporting checks
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
A common pattern in CMake output is a message indicating the start of some
|
||||
sort of check, followed by another message reporting the result of that check.
|
||||
For example:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
message(STATUS "Looking for someheader.h")
|
||||
#... do the checks, set checkSuccess with the result
|
||||
if(checkSuccess)
|
||||
message(STATUS "Looking for someheader.h - found")
|
||||
else()
|
||||
message(STATUS "Looking for someheader.h - not found")
|
||||
endif()
|
||||
|
||||
This can be more robustly and conveniently expressed using the ``CHECK_...``
|
||||
keyword form of the ``message()`` command:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
message(<checkState> "message" ...)
|
||||
|
||||
where ``<checkState>`` must be one of the following:
|
||||
|
||||
``CHECK_START``
|
||||
Record a concise message about the check about to be performed.
|
||||
|
||||
``CHECK_PASS``
|
||||
Record a successful result for a check.
|
||||
|
||||
``CHECK_FAIL``
|
||||
Record an unsuccessful result for a check.
|
||||
|
||||
When recording a check result, the command repeats the message from the most
|
||||
recently started check for which no result has yet been reported, then some
|
||||
separator characters and then the message text provided after the
|
||||
``CHECK_PASS`` or ``CHECK_FAIL`` keyword. Check messages are always reported
|
||||
at ``STATUS`` log level.
|
||||
|
||||
Checks may be nested and every ``CHECK_START`` should have exactly one
|
||||
matching ``CHECK_PASS`` or ``CHECK_FAIL``.
|
||||
The :variable:`CMAKE_MESSAGE_INDENT` variable can also be used to add
|
||||
indenting to nested checks if desired. For example:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
message(CHECK_START "Finding my things")
|
||||
list(APPEND CMAKE_MESSAGE_INDENT " ")
|
||||
unset(missingComponents)
|
||||
|
||||
message(CHECK_START "Finding partA")
|
||||
# ... do check, assume we find A
|
||||
message(CHECK_PASS "found")
|
||||
|
||||
message(CHECK_START "Finding partB")
|
||||
# ... do check, assume we don't find B
|
||||
list(APPEND missingComponents B)
|
||||
message(CHECK_FAIL "not found")
|
||||
|
||||
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
||||
if(missingComponents)
|
||||
message(CHECK_FAIL "missing components: ${missingComponents}")
|
||||
else()
|
||||
message(CHECK_PASS "all components found")
|
||||
endif()
|
||||
|
||||
Output from the above would appear something like the following::
|
||||
|
||||
-- Finding my things
|
||||
-- Finding partA
|
||||
-- Finding partA - found
|
||||
-- Finding partB
|
||||
-- Finding partB - not found
|
||||
-- Finding my things - missing components: B
|
||||
|
||||
5
Help/release/dev/new-message-types.rst
Normal file
5
Help/release/dev/new-message-types.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
new-message-types
|
||||
-----------------
|
||||
|
||||
* The :command:`message` command gained new keywords ``CHECK_START``,
|
||||
``CHECK_PASS`` and ``CHECK_FAIL``.
|
||||
@@ -3,6 +3,11 @@
|
||||
#include "cmMessageCommand.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
#include <cm/string_view>
|
||||
|
||||
#include "cm_static_string_view.hxx"
|
||||
|
||||
#include "cmExecutionStatus.h"
|
||||
#include "cmMakefile.h"
|
||||
@@ -13,6 +18,55 @@
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmake.h"
|
||||
|
||||
namespace {
|
||||
|
||||
enum class CheckingType
|
||||
{
|
||||
UNDEFINED,
|
||||
CHECK_START,
|
||||
CHECK_PASS,
|
||||
CHECK_FAIL
|
||||
};
|
||||
|
||||
std::string IndentText(std::string text, cmMakefile& mf)
|
||||
{
|
||||
auto indent =
|
||||
cmJoin(cmExpandedList(mf.GetSafeDefinition("CMAKE_MESSAGE_INDENT")), "");
|
||||
|
||||
const auto showContext = mf.GetCMakeInstance()->GetShowLogContext() ||
|
||||
mf.IsOn("CMAKE_MESSAGE_CONTEXT_SHOW");
|
||||
if (showContext) {
|
||||
auto context = cmJoin(
|
||||
cmExpandedList(mf.GetSafeDefinition("CMAKE_MESSAGE_CONTEXT")), ".");
|
||||
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,
|
||||
cmStrCat("Ignored "_s, what, " without CHECK_START"_s),
|
||||
mf.GetBacktrace());
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// cmLibraryCommand
|
||||
bool cmMessageCommand(std::vector<std::string> const& args,
|
||||
cmExecutionStatus& status)
|
||||
@@ -29,6 +83,7 @@ bool cmMessageCommand(std::vector<std::string> const& args,
|
||||
auto type = MessageType::MESSAGE;
|
||||
auto fatal = false;
|
||||
auto level = cmake::LogLevel::LOG_UNDEFINED;
|
||||
auto checkingType = CheckingType::UNDEFINED;
|
||||
if (*i == "SEND_ERROR") {
|
||||
type = MessageType::FATAL_ERROR;
|
||||
level = cmake::LogLevel::LOG_ERROR;
|
||||
@@ -55,6 +110,18 @@ bool cmMessageCommand(std::vector<std::string> const& args,
|
||||
return true;
|
||||
}
|
||||
++i;
|
||||
} else if (*i == "CHECK_START") {
|
||||
level = cmake::LogLevel::LOG_STATUS;
|
||||
checkingType = CheckingType::CHECK_START;
|
||||
++i;
|
||||
} else if (*i == "CHECK_PASS") {
|
||||
level = cmake::LogLevel::LOG_STATUS;
|
||||
checkingType = CheckingType::CHECK_PASS;
|
||||
++i;
|
||||
} else if (*i == "CHECK_FAIL") {
|
||||
level = cmake::LogLevel::LOG_STATUS;
|
||||
checkingType = CheckingType::CHECK_FAIL;
|
||||
++i;
|
||||
} else if (*i == "STATUS") {
|
||||
level = cmake::LogLevel::LOG_STATUS;
|
||||
++i;
|
||||
@@ -111,28 +178,6 @@ bool cmMessageCommand(std::vector<std::string> const& args,
|
||||
|
||||
auto message = cmJoin(cmMakeRange(i, args.cend()), "");
|
||||
|
||||
if (cmake::LogLevel::LOG_NOTICE <= level) {
|
||||
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:
|
||||
@@ -141,14 +186,34 @@ bool cmMessageCommand(std::vector<std::string> const& args,
|
||||
break;
|
||||
|
||||
case cmake::LogLevel::LOG_NOTICE:
|
||||
cmSystemTools::Message(message);
|
||||
cmSystemTools::Message(IndentText(message, mf));
|
||||
break;
|
||||
|
||||
case cmake::LogLevel::LOG_STATUS:
|
||||
switch (checkingType) {
|
||||
case CheckingType::CHECK_START:
|
||||
mf.DisplayStatus(IndentText(message, mf), -1);
|
||||
mf.GetCMakeInstance()->PushCheckInProgressMessage(message);
|
||||
break;
|
||||
|
||||
case CheckingType::CHECK_PASS:
|
||||
ReportCheckResult("CHECK_PASS"_s, message, mf);
|
||||
break;
|
||||
|
||||
case CheckingType::CHECK_FAIL:
|
||||
ReportCheckResult("CHECK_FAIL"_s, message, mf);
|
||||
break;
|
||||
|
||||
default:
|
||||
mf.DisplayStatus(IndentText(message, mf), -1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case cmake::LogLevel::LOG_VERBOSE:
|
||||
case cmake::LogLevel::LOG_DEBUG:
|
||||
case cmake::LogLevel::LOG_TRACE:
|
||||
mf.DisplayStatus(message, -1);
|
||||
mf.DisplayStatus(IndentText(message, mf), -1);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -5,12 +5,15 @@
|
||||
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "cmGeneratedFileStream.h"
|
||||
@@ -387,6 +390,25 @@ public:
|
||||
void SetLogLevel(LogLevel level) { this->MessageLogLevel = level; }
|
||||
static LogLevel StringToLogLevel(const std::string& levelStr);
|
||||
|
||||
bool HasCheckInProgress() const
|
||||
{
|
||||
return !this->CheckInProgressMessages.empty();
|
||||
}
|
||||
std::size_t GetCheckInProgressSize() const
|
||||
{
|
||||
return this->CheckInProgressMessages.size();
|
||||
}
|
||||
std::string GetTopCheckInProgressMessage()
|
||||
{
|
||||
auto message = this->CheckInProgressMessages.top();
|
||||
this->CheckInProgressMessages.pop();
|
||||
return message;
|
||||
}
|
||||
void PushCheckInProgressMessage(std::string message)
|
||||
{
|
||||
this->CheckInProgressMessages.emplace(std::move(message));
|
||||
}
|
||||
|
||||
//! Do we want debug output during the cmake run.
|
||||
bool GetDebugOutput() { return this->DebugOutput; }
|
||||
void SetDebugOutputOn(bool b) { this->DebugOutput = b; }
|
||||
@@ -596,6 +618,8 @@ private:
|
||||
bool LogLevelWasSetViaCLI = false;
|
||||
bool LogContext = false;
|
||||
|
||||
std::stack<std::string> CheckInProgressMessages;
|
||||
|
||||
void UpdateConversionPathTable();
|
||||
|
||||
//! Print a list of valid generators to stderr.
|
||||
|
||||
@@ -83,3 +83,8 @@ 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
|
||||
)
|
||||
|
||||
run_cmake_command(
|
||||
message-checks
|
||||
${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/message-checks.cmake
|
||||
)
|
||||
|
||||
3
Tests/RunCMake/message/message-checks-stderr.txt
Normal file
3
Tests/RunCMake/message/message-checks-stderr.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
^CMake Warning \(dev\) at.*/Tests/RunCMake/message/message-checks.cmake:13 \(message\):
|
||||
Ignored CHECK_FAIL without CHECK_START
|
||||
This warning is for project developers. Use -Wno-dev to suppress it.$
|
||||
10
Tests/RunCMake/message/message-checks-stdout.txt
Normal file
10
Tests/RunCMake/message/message-checks-stdout.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
-- Find `libfoo`
|
||||
-- Looking for `libfoo\.h`
|
||||
-- Looking for `libfoo\.h` - found \[/usr/include\]
|
||||
-- Looking for `libfoo\.so`
|
||||
-- Looking for `libfoo\.so` - found \[/usr/lib/libfoo\.so\]
|
||||
-- Getting `libfoo` version
|
||||
-- Looking for `libfoo/version\.h`
|
||||
-- Looking for `libfoo/version\.h` - found
|
||||
-- Getting `libfoo` version - 1\.2\.3
|
||||
-- Find `libfoo` - required version 4\.5\.6 but found 1\.2\.3
|
||||
13
Tests/RunCMake/message/message-checks.cmake
Normal file
13
Tests/RunCMake/message/message-checks.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
message(CHECK_START "Find `libfoo`")
|
||||
message(CHECK_START "Looking for `libfoo.h`")
|
||||
message(CHECK_PASS "found [/usr/include]")
|
||||
message(CHECK_START "Looking for `libfoo.so`")
|
||||
message(CHECK_PASS "found [/usr/lib/libfoo.so]")
|
||||
message(CHECK_START "Getting `libfoo` version")
|
||||
message(CHECK_START "Looking for `libfoo/version.h`")
|
||||
message(CHECK_PASS "found")
|
||||
message(CHECK_PASS "1.2.3")
|
||||
message(CHECK_FAIL "required version 4.5.6 but found 1.2.3")
|
||||
|
||||
# Should generate an error, no associated CHECK_START
|
||||
message(CHECK_FAIL "unmatched check fail case")
|
||||
Reference in New Issue
Block a user