Skip to content

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

Edited by Alex Turbov
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information