Proposal: Diagnostic messages from CMake files
Doing some complex modules I've found the following pattern pretty usable:
function(my_debug)
if(MY_DEBUG)
message(STATUS ${ARGN})
endif()
endfunction()
function(blah)
...
my_debug("Going to do...")
...
endfunction()
# Some more code calling `my_debug` lot of times...
Example of the almost the same pattern can be found in FindBoost.cmake
.
It'll be nice to have an ability for the CMake files authors to have a unified and the standard way to provide some more diagnostic messages which could be viewed on demand. It could be solved via CLI option --verbose[=<N>]
and few (typically 4 is quite enough) verbosity levels.
The message
command could be extended w/ the following sub-command:
message(VERBOSE [LEVEL N] "<message>")
to print the message text if the current verbosity level equals or higher than assigned to the message.
Having a lot of modules printing various detailed messages could lead to problems w/ identifying the source of a message. To address this issue the diagnostic module I use privately provides a concept of context (or a channel). That is a prefix to be displayed before a message itself and its purpose to indicate a message's source. That could be done via a variable, say CMAKE_VERBOSE_CONTEXT
. Let it be a list of strings that would be dot-concatenated on output. Example:
function(bar)
list(APPEND CMAKE_VERBOSE_CONTEXT "bar")
message(VERBOSE LEVEL 3 "Level 3")
endfunction()
function(foo)
list(APPEND CMAKE_VERBOSE_CONTEXT "foo")
message(VERBOSE LEVEL 2 "Level 2: before")
bar()
message(VERBOSE LEVEL 2 "Level 2: after")
endfunction()
message(VERBOSE "Top: before")
foo()
message(VERBOSE "Top: after")
Sample output of cmake --verbose=3 -P test.cmake
:
Running with verbose level 3.
-- Top: before
-- [foo] Level 2: before
-- [foo.bar] Level 3
-- [foo] Level 2: after
-- Top: after
Also, mute some messages could be implemented as setting, say CMAKE_VERBOSE_FILTER
, a list variable w/ regex/fnmatch expressions to filter messages by a channel (context).
Example:
Imagine, that FindBoost.cmake
use message(VEBOSE ...)
instead of currently used boiler plate code:
if(Boost_DEBUG)
message(STATUS ...)
endif()
It may look like this:
...
list(APPEND CMAKE_VERBOSE_CONTEXT "Boost")
message(VEBOSE "Finding boost blah-blah... ${_boost_TEST_VERSIONS}")
...
message(VEBOSE "Searching 4 headers...")
...
message(VEBOSE "Searching 4 libs...")
...
list(POP_BACK CMAKE_VERBOSE_CONTEXT)
...
Then to filter only Boost finder related diagnostic messages one can run cmake
like this:
$ cmake --verbose=2 -DCMAKE_VERBOSE_FILTER=Boost.* ...
Having contexts as a list (of scoped strings) is good in my practice cuz it quickly gives info about the "backtrace" -- e.g. if you have the function bar
(w/ scoped context appended) and few other functions it has called from, it'll be easy to "trace" its usage "context" from a diagnostic message w/o using the "heavy artillery" (i.e. --trace
or --trace-expand
)...