| CMake Diagnostics Guide |
| *********************** |
| |
| The following is a guide on how CMake should issue diagnostics. |
| See documentation on `CMake Development`_ for more information. |
| |
| .. _`CMake Development`: README.rst |
| |
| TL;DR Porting Guide (from CMake < 4.4) |
| ====================================== |
| |
| I want to write ``IssueMessage(MessageType::AUTHOR_WARNING, ...``: |
| Write ``IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, ...`` instead. |
| |
| I want to write ``IssueMessage(MessageType::DEPRECATION_WARNING, ...``: |
| Write ``IssueDiagnostic(cmDiagnostics::CMD_DEPRECATED, ...`` instead. |
| |
| I want to write ``IssueMessage(MessageType::AUTHOR_ERROR, ...``: |
| No, you don't. Under the old system, there isn't a meaningful difference |
| between ``AUTHOR_WARNING`` and ``AUTHOR_ERROR``; at least, not as arguments |
| to ``IssueMessage``. |
| |
| I want to write ``IssueMessage(MessageType::DEPRECATION_ERROR, ...``: |
| See ``AUTHOR_ERROR``. |
| |
| History |
| ======= |
| |
| Prior to CMake 4.4, CMake did not have a formal system for issuing diagnostics. |
| The messaging system had message types for author diagnostics and deprecation |
| diagnostics which conflated category and severity. Worse, although these types |
| *purported* to allow the caller to specify severity, this information is not |
| actually honored. Other diagnostics existed which did not use these types. |
| |
| More importantly, the mechanisms for managing severity differed depending on |
| the diagnostic. Variables to control a diagnostic might be documented, |
| undocumented, or non-existent. Spelling of command-line options to control |
| diagnostics was inconsistent, as was the ability to make diagnostics fatal. |
| Adding new categories required significant effort, as the logic to handle a |
| diagnostic was bespoke at every layer. |
| |
| CMake Diagnostic Framework |
| ========================== |
| |
| CMake 4.4 introduced a structured framework for issuing and managing |
| diagnostics. The ``cmDiagnostics.h`` header enumerates all available |
| diagnostic categories. All code to manage diagnostics leverages either this |
| table or helper code generated from the same, such that it is the *only* |
| non-documentation location that needs to be updated to add a new diagnostic |
| category. The framework is also responsible for determining the severity of a |
| diagnostic, based on the combination of category defaults and user preferences, |
| and for presenting the user with a consistent set of control mechanisms. |
| |
| Numeric identifiers associated with diagnostic categories are only used |
| internally and are never persisted. This means that numbers can be reassigned |
| at any time. This also permits ensuring that numbers are consecutive, which |
| allows iterating over all categories using a simple ``for`` loop. (Note that |
| such loops will typically start from ``1``, not ``0``, as ``0`` is reserved |
| for the 'NONE' category which is used primarily as a virtual parent.) Where |
| necessary, macros provided by ``cmDiagnostics.h`` can generate code for each |
| diagnostic category. |
| |
| Refer to ``Help/manual/cmake-diagnostics.7.rst`` for user-facing documentation. |
| |
| Issuing Diagnostics |
| =================== |
| |
| Diagnostics are the CMake equivalent of compiler warnings, with categorization |
| providing both additional information and additional response control to users. |
| |
| Diagnostics are issued via the ``IssueDiagnostic`` method. This method is |
| provided on various objects, with ``cmMakefile`` being the most prominent, and |
| typically exists alongside an ``IssueMessage`` method. The latter is not |
| deprecated, and should still be used to issue messages outside of the |
| diagnostic categories, such as hard errors or informational messages. |
| |
| The historic ``{AUTHOR,DEPRECATION}_{WARNING,ERROR}`` message types, however, |
| have been removed. These are replaced by ``IssueDiagnostic`` and the |
| ``CMD_{AUTHOR,DEPRECATED}`` diagnostic categories. Because the methods have |
| similar signatures, porting in many cases can be accomplished by replacing the |
| method name and first argument (the former message type, newly the diagnostic |
| category). For example, where prior versions of CMake wrote: |
| |
| .. code:: c++ |
| |
| mf->IssueMessage(MessageType::AUTHOR_WARNING, ...); |
| |
| CMake 4.4 and later should write: |
| |
| .. code:: c++ |
| |
| mf->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, ...); |
| |
| (Note that the ``"cmDiagnostics.h"`` header may need to be newly included.) |
| |
| As previously mentioned, the purported severity was ignored by ``cmMessenger``, |
| which instead used its own internal state to decide whether a message should |
| be a warning or an error. This logic overrode the severity component of the |
| message type as passed into the method. As a result, although the new method |
| does not accept a severity, this is actually consistent with the historic |
| behavior and does not represent any loss of functionality. |
| |
| .. note:: |
| |
| When issuing diagnostics targeted at build-system authors, please consult the |
| complete list of available diagnostics, as a more specific category may be |
| available. In some cases, it may even make sense to add a new category. |
| |
| Adding Diagnostic Categories |
| ============================ |
| |
| Adding new diagnostic categories involves four steps. |
| |
| 1. The new category must be added to ``cmDiagnostics.h``. The diagnostics |
| table looks like this: |
| |
| .. code:: c++ |
| |
| #define CM_FOR_EACH_DIAGNOSTIC_TABLE(ACTION, SELECT) \ |
| SELECT(ACTION, Warn, CMD_NONE, CMD_AUTHOR, 12) \ |
| SELECT(ACTION, Warn, CMD_AUTHOR, CMD_DEPRECATED, 1) \ |
| ... |
| |
| Add a new entry. All entries must begin with ``SELECT(ACTION,``. The |
| second argument is the *default* state of the diagnostic and should |
| typically be ``Warn``, but may be ``Ignore`` if the diagnostic should be |
| opt-in. Defaulting to ``SendError`` would be exceptional, and defaulting |
| to ``FatalError`` is not recommended under any circumstances. The third |
| argument is the *parent* of the diagnostic (see below). The fourth is the |
| actual name of the diagnostic. The fifth and final argument is the CMake |
| preset version corresponding to the introduction of the argument, which |
| should be the latest preset version as used by the version of CMake under |
| development. |
| |
| .. note:: |
| |
| In the event that the development version has not already introduced a |
| new preset version, adding a new diagnostic will additionally require |
| introducing a new preset version. |
| |
| Diagnostics are hierarchical and **must** be listed in order of a |
| depth-first search. (If you imagine the order of items in a typical tree |
| view which has been fully expanded, that is depth-first order.) Each |
| diagnostic has a direct parent. Recursive alterations to a diagnostic will |
| also affect all descendant diagnostics. |
| |
| 2. A documentation page for the new diagnostic must be created under |
| ``Help/diagnostic``, and a link to the same added to the Diagnostics |
| manual, ``Help/manual/cmake-diagnostics.7.rst``. |
| |
| 3. The new presets fields must be mentioned in the CMake presets version |
| history, found at the bottom of ``Help/manual/cmake-presets.7.rst``. Note |
| that the documentation of the fields themselves is generated. |
| |
| 4. The ``Utilities/Scripts/regenerate-presets.py`` script must be run to |
| generate the updated presets schema and documentation. Don't forget to |
| review and commit the changes made by this script. |