From a0612aeb5db938f35225fc76a4b4984667b60b73 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Fri, 27 Feb 2026 16:56:14 -0500 Subject: [PATCH] Diagnostics: Add cmake_diagnostic command Add command to manipulate the new diagnostic stack/state introduced in the previous commit. Start adding documentation for the system as a whole. --- Help/command/cmake_diagnostic.rst | 156 ++++++++++++++++++ Help/index.rst | 1 + Help/manual/cmake-commands.7.rst | 1 + Help/manual/cmake-diagnostics.7.rst | 71 ++++++++ Source/CMakeLists.txt | 2 + Source/cmCMakeDiagnosticCommand.cxx | 130 +++++++++++++++ Source/cmCMakeDiagnosticCommand.h | 18 ++ Source/cmCommands.cxx | 2 + Source/cmDiagnostics.cxx | 51 ++++++ Source/cmDiagnostics.h | 10 ++ Tests/RunCMake/CMakeLists.txt | 2 + Tests/RunCMake/DiagnosticScope/Block.cmake | 22 +++ Tests/RunCMake/DiagnosticScope/CMakeLists.txt | 3 + .../DiagnosticScope/DiagnosticInclude.cmake | 6 + .../DiagnosticScope/IncludeWithScope.cmake | 7 + .../DiagnosticScope/IncludeWithoutScope.cmake | 7 + .../DiagnosticScope/NotClosed-result.txt | 1 + .../DiagnosticScope/NotClosed-stderr.txt | 4 + .../RunCMake/DiagnosticScope/NotClosed.cmake | 1 + .../DiagnosticScope/NotOpened-result.txt | 1 + .../DiagnosticScope/NotOpened-stderr.txt | 4 + .../RunCMake/DiagnosticScope/NotOpened.cmake | 1 + Tests/RunCMake/DiagnosticScope/PushPop.cmake | 22 +++ .../DiagnosticScope/RunCMakeTest.cmake | 8 + Tests/RunCMake/Diagnostics/Actions.cmake | 62 +++++++ Tests/RunCMake/Diagnostics/CMakeLists.txt | 3 + Tests/RunCMake/Diagnostics/RunCMakeTest.cmake | 3 + Utilities/Sphinx/cmake.py | 2 + bootstrap | 1 + 29 files changed, 602 insertions(+) create mode 100644 Help/command/cmake_diagnostic.rst create mode 100644 Help/manual/cmake-diagnostics.7.rst create mode 100644 Source/cmCMakeDiagnosticCommand.cxx create mode 100644 Source/cmCMakeDiagnosticCommand.h create mode 100644 Tests/RunCMake/DiagnosticScope/Block.cmake create mode 100644 Tests/RunCMake/DiagnosticScope/CMakeLists.txt create mode 100644 Tests/RunCMake/DiagnosticScope/DiagnosticInclude.cmake create mode 100644 Tests/RunCMake/DiagnosticScope/IncludeWithScope.cmake create mode 100644 Tests/RunCMake/DiagnosticScope/IncludeWithoutScope.cmake create mode 100644 Tests/RunCMake/DiagnosticScope/NotClosed-result.txt create mode 100644 Tests/RunCMake/DiagnosticScope/NotClosed-stderr.txt create mode 100644 Tests/RunCMake/DiagnosticScope/NotClosed.cmake create mode 100644 Tests/RunCMake/DiagnosticScope/NotOpened-result.txt create mode 100644 Tests/RunCMake/DiagnosticScope/NotOpened-stderr.txt create mode 100644 Tests/RunCMake/DiagnosticScope/NotOpened.cmake create mode 100644 Tests/RunCMake/DiagnosticScope/PushPop.cmake create mode 100644 Tests/RunCMake/DiagnosticScope/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/Diagnostics/Actions.cmake create mode 100644 Tests/RunCMake/Diagnostics/CMakeLists.txt create mode 100644 Tests/RunCMake/Diagnostics/RunCMakeTest.cmake diff --git a/Help/command/cmake_diagnostic.rst b/Help/command/cmake_diagnostic.rst new file mode 100644 index 0000000000..29b258917d --- /dev/null +++ b/Help/command/cmake_diagnostic.rst @@ -0,0 +1,156 @@ +cmake_diagnostic +---------------- + +.. versionadded:: 4.4 + +Manage CMake Diagnostic settings. See the :manual:`cmake-diagnostics(7)` +manual for a list of available categories. + +Synopsis +^^^^^^^^ + +.. parsed-literal:: + + `Setting Diagnostics`_ + cmake_diagnostic(`SET`_ [RECURSE]) + cmake_diagnostic(`PROMOTE`_ [NO_RECURSE]) + cmake_diagnostic(`DEMOTE`_ [NO_RECURSE]) + + `Checking Diagnostic Actions`_ + cmake_diagnostic(`GET`_ ) + + `CMake Diagnostic Stack`_ + cmake_diagnostic(`PUSH`_) + cmake_diagnostic(`POP`_) + +Setting Diagnostics +^^^^^^^^^^^^^^^^^^^ + +.. signature:: + cmake_diagnostic(SET CMD_ [RECURSE]) + cmake_diagnostic(PROMOTE CMD_ [NO_RECURSE]) + cmake_diagnostic(DEMOTE CMD_ [NO_RECURSE]) + :target: + SET + PROMOTE + DEMOTE + +Set or alter the action taken when a diagnostic belonging to a particular +category is triggered. + +The ``SET`` subcommand sets the action for the specified diagnostic category. +The ``PROMOTE`` subcommand increases the severity for the specified diagnostic +category, or does nothing if the action was already set to an equal or higher +severity. The ``DEMOTE`` subcommand decreases the severity for the specified +diagnostic category, or does nothing if the action was already set to an equal +or lower severity. + +The possible ````\ s (in order of severity) are: + +``IGNORE`` + Do nothing. + +``WARN`` + Report a warning and continue processing. + +``SEND_ERROR`` + Report an error, continue processing, but skip generation. + + The :manual:`cmake(1)` executable will return a non-zero + :ref:`exit code `. + +``FATAL_ERROR`` + Report an error, stop processing and generation. + + The :manual:`cmake(1)` executable will return a non-zero + :ref:`exit code `. + +Some diagnostic categories are hierarchical. The ``RECURSE`` and +``NO_RECURSE`` options determine whether changing the action for a diagnostic +category also modifies any child categories. By default, the ``PROMOTE`` and +``DEMOTE`` subcommands are recursive, while the ``SET`` subcommand is not. +Note that the alteration for child categories is independent of the prior +action set on any parents; that is, ``PROMOTE`` and ``DEMOTE``, when operating +recursively, will operate on all child categories even if a parent category's +action was not altered. + +Checking Diagnostic Actions +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. signature:: cmake_diagnostic(GET CMD_ ) + :target: GET + +Check what action is currently specified for a diagnostic category. +The output ```` value will be one of ``IGNORE``, ``WARN``, +``SEND_ERROR`` or ``FATAL_ERROR``. + +CMake Diagnostic Stack +^^^^^^^^^^^^^^^^^^^^^^ + +CMake keeps diagnostic settings on a stack, so changes made by the +``cmake_diagnostic`` command affect only the top of the stack. A new entry on +the diagnostic stack is managed automatically for each subdirectory to protect +its parents and siblings. CMake also manages a new entry for scripts loaded by +:command:`include` and :command:`find_package` commands except when invoked +with the ``NO_DIAGNOSTIC_SCOPE`` option. The ``cmake_diagnostic`` command +provides an interface to manage custom entries on the diagnostic stack: + +.. signature:: cmake_diagnostic(PUSH) + + Create a new entry on the diagnostic stack. + +.. signature:: cmake_diagnostic(POP) + + Remove the last diagnostic stack entry created with + ``cmake_diagnostic(PUSH)``. + +Each ``PUSH`` must have a matching ``POP`` to erase any changes. +This is useful to make temporary changes to diagnostic settings. +Calls to the :command:`cmake_diagnostic(SET)`, +:command:`cmake_diagnostic(PROMOTE)`, or :command:`cmake_diagnostic(DEMOTE)` +commands influence only the current top of the diagnostic stack. + +The :command:`block(SCOPE_FOR DIAGNOSTICS)` command offers a more flexible +and more secure way to manage the diagnostic stack. The pop action is done +automatically when leaving the block scope, so there is no need to +precede each :command:`return` with a call to :command:`cmake_diagnostic(POP)`. + +.. code-block:: cmake + + # stack management with cmake_diagnostic() + function(my_func) + cmake_diagnostic(PUSH) + cmake_diagnostic(SET ...) + if () + ... + cmake_diagnostic(POP) + return() + elseif() + ... + cmake_diagnostic(POP) + return() + endif() + ... + cmake_diagnostic(POP) + endfunction() + + # stack management with block()/endblock() + function(my_func) + block(SCOPE_FOR DIAGNOSTICS) + cmake_diagnostic(SET ...) + if () + ... + return() + elseif() + ... + return() + endif() + ... + endblock() + endfunction() + +Commands created by the :command:`function` and :command:`macro` commands +record diagnostic settings when they are created and use the pre-record +diagnostics when they are invoked. If the function or macro implementation +sets diagnostics, the changes automatically propagate up through callers until +they reach the closest nested diagnostic stack entry. diff --git a/Help/index.rst b/Help/index.rst index f9ffcbaf72..fa7d199858 100644 --- a/Help/index.rst +++ b/Help/index.rst @@ -60,6 +60,7 @@ Reference Manuals /manual/cmake-configure-log.7 /manual/cmake-cxxmodules.7 /manual/cmake-developer.7 + /manual/cmake-diagnostics.7 /manual/cmake-env-variables.7 /manual/cmake-file-api.7 /manual/cmake-generator-expressions.7 diff --git a/Help/manual/cmake-commands.7.rst b/Help/manual/cmake-commands.7.rst index 6f1a947ab1..695a6a798b 100644 --- a/Help/manual/cmake-commands.7.rst +++ b/Help/manual/cmake-commands.7.rst @@ -17,6 +17,7 @@ These commands are always available. /command/block /command/break + /command/cmake_diagnostic /command/cmake_host_system_information /command/cmake_language /command/cmake_minimum_required diff --git a/Help/manual/cmake-diagnostics.7.rst b/Help/manual/cmake-diagnostics.7.rst new file mode 100644 index 0000000000..e678a2d255 --- /dev/null +++ b/Help/manual/cmake-diagnostics.7.rst @@ -0,0 +1,71 @@ +.. cmake-manual-description: CMake Diagnostics Reference + +cmake-diagnostics(7) +******************** + +.. only:: html + + .. contents:: + +.. _cmake-diagnostics-intro: + +Introduction +============ + +CMake Diagnostics are the mechanism by which CMake categorizes and presents +certain advisory information about a project's configuration and the generation +of its build system. These diagnostics can be seen as the build system +equivalent of compiler warnings. Diagnostics provide feedback on potential +issues in several categories: + +* Issues that may impact the success of the build. + +* Issues that may impact the correctness of the build. + +* Issues that may impact the correctness of the project packaging. + +* Issues that may impact the ability of the project + to be built with newer versions of dependencies. + +* Issues that may impact the ability of the project + to be built with newer versions of CMake. + +Diagnostic Actions +------------------ + +The action taken when a particular diagnostic is triggered depends on the +diagnostic category. Most categories will warn by default. The +:command:`cmake_diagnostic` command and ``-W`` options can be +used to control what action occurs when a diagnostic of a particular category +is triggered. The possible actions are described in the documentation of the +same. + +Diagnostic Categories +===================== + +The following categories are defined. + +``CMD_AUTHOR`` (``-Wauthor``) +----------------------------- + +:Default: Warn + +Warn about a build system's incorrect use of CMake, or of a CMake interface +provided by a dependency. This is the category triggered by +:command:`message(AUTHOR_WARNING)`. It is also the ancestor of many other +diagnostic categories. + +The most important aspect of this category is that it represents issues with +a project's build system which typically require alteration to the same. This +is to say that users simply trying to build a project obtained elsewhere will +typically not be interested in these warnings, except to perhaps report them +to the project's developer(s). + +``CMD_DEPRECATED`` (``-Wdeprecated``) +------------------------------------- + +:Default: Warn +:Parent: ``CMD_AUTHOR`` + +Warn about use of a deprecated function or package. This is the category +triggered by :command:`message(DEPRECATION)`. diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index c8f91baefe..e06deba86e 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -582,6 +582,8 @@ add_library( cmBreakCommand.h cmBuildCommand.cxx cmBuildCommand.h + cmCMakeDiagnosticCommand.cxx + cmCMakeDiagnosticCommand.h cmCMakeHostSystemInformationCommand.cxx cmCMakeHostSystemInformationCommand.h cmCMakeLanguageCommand.cxx diff --git a/Source/cmCMakeDiagnosticCommand.cxx b/Source/cmCMakeDiagnosticCommand.cxx new file mode 100644 index 0000000000..3f094b1e3b --- /dev/null +++ b/Source/cmCMakeDiagnosticCommand.cxx @@ -0,0 +1,130 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmCMakeDiagnosticCommand.h" + +#include +#include + +#include "cmDiagnostics.h" +#include "cmExecutionStatus.h" +#include "cmMakefile.h" +#include "cmStringAlgorithms.h" + +namespace { +using AlterFunction = bool (cmMakefile::*)(cmDiagnostics::DiagnosticCategory, + cmDiagnostics::DiagnosticAction, + bool); + +bool HandleAlterMode(std::vector const& args, + cmExecutionStatus& status, AlterFunction function, + bool recursive) +{ + if (args.size() < 3 || args.size() > 4) { + status.SetError(cmStrCat( + args[0], " must be given exactly 2 or 3 additional arguments."_s)); + return false; + } + + if (args.size() == 4) { + if (args[3] == "RECURSE") { + recursive = true; + } else if (args[3] == "NO_RECURSE") { + recursive = false; + } else { + status.SetError( + cmStrCat(args[0], " given unknown option \""_s, args[3], "\"."_s)); + return false; + } + } + + cm::optional const action = + cmDiagnostics::GetDiagnosticAction(args[2]); + if (!action) { + status.SetError(cmStrCat( + args[0], " given unrecognized diagnostic action \"", args[2], "\"."_s)); + return false; + } + + cm::optional const category = + cmDiagnostics::GetDiagnosticCategory(args[1]); + if (!category) { + status.SetError(cmStrCat(args[0], + " given unrecognized diagnostic category \"", + args[1], "\"."_s)); + return false; + } + + if (!(status.GetMakefile().*function)(*category, *action, recursive)) { + status.SetError(cmStrCat(args[0], " failed to set diagnostic action."_s)); + return false; + } + return true; +} + +bool HandleGetMode(std::vector const& args, + cmExecutionStatus& status) +{ + if (args.size() != 3) { + status.SetError("GET must be given exactly 2 additional arguments."); + return false; + } + + cm::optional const category = + cmDiagnostics::GetDiagnosticCategory(args[1]); + if (!category) { + status.SetError(cmStrCat(args[0], + " given unrecognized diagnostic category \"", + args[1], "\"."_s)); + return false; + } + + cmDiagnostics::DiagnosticAction const action = + status.GetMakefile().GetDiagnosticAction(*category); + + status.GetMakefile().AddDefinition(args[2], + cmDiagnostics::GetActionString(action)); + return true; +} +} + +// cmCMakeDiagnosticCommand +bool cmCMakeDiagnosticCommand(std::vector const& args, + cmExecutionStatus& status) +{ + if (args.empty()) { + status.SetError("requires at least one argument."); + return false; + } + + if (args[0] == "SET") { + return HandleAlterMode(args, status, &cmMakefile::SetDiagnostic, false); + } + if (args[0] == "PROMOTE") { + return HandleAlterMode(args, status, &cmMakefile::PromoteDiagnostic, true); + } + if (args[0] == "DEMOTE") { + return HandleAlterMode(args, status, &cmMakefile::DemoteDiagnostic, true); + } + if (args[0] == "GET") { + return HandleGetMode(args, status); + } + if (args[0] == "PUSH") { + if (args.size() > 1) { + status.SetError("PUSH may not be given additional arguments."); + return false; + } + status.GetMakefile().PushDiagnostic(); + return true; + } + if (args[0] == "POP") { + if (args.size() > 1) { + status.SetError("POP may not be given additional arguments."); + return false; + } + status.GetMakefile().PopDiagnostic(); + return true; + } + + status.SetError(cmStrCat("given unknown first argument \"", args[0], '"')); + return false; +} diff --git a/Source/cmCMakeDiagnosticCommand.h b/Source/cmCMakeDiagnosticCommand.h new file mode 100644 index 0000000000..c5d6e8aba4 --- /dev/null +++ b/Source/cmCMakeDiagnosticCommand.h @@ -0,0 +1,18 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include +#include + +class cmExecutionStatus; + +/** + * \brief Set how CMake should handle diagnostics + * + * cmCMakeDiagnosticCommand sets how CMake should deal with diagnostics. + */ +bool cmCMakeDiagnosticCommand(std::vector const& args, + cmExecutionStatus& status); diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index 498c1b5d6b..480809c0e4 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -17,6 +17,7 @@ #include "cmBlockCommand.h" #include "cmBreakCommand.h" #include "cmBuildCommand.h" +#include "cmCMakeDiagnosticCommand.h" #include "cmCMakeLanguageCommand.h" #include "cmCMakeMinimumRequired.h" #include "cmCMakePathCommand.h" @@ -124,6 +125,7 @@ void GetScriptingCommands(cmState* state) state->AddFlowControlCommand("while", cmWhileCommand); state->AddFlowControlCommand("block", cmBlockCommand); + state->AddBuiltinCommand("cmake_diagnostic", cmCMakeDiagnosticCommand); state->AddBuiltinCommand("cmake_language", cmCMakeLanguageCommand); state->AddBuiltinCommand("cmake_minimum_required", cmCMakeMinimumRequired); state->AddBuiltinCommand("cmake_path", cmCMakePathCommand); diff --git a/Source/cmDiagnostics.cxx b/Source/cmDiagnostics.cxx index 9c0192b6b2..5cb1011230 100644 --- a/Source/cmDiagnostics.cxx +++ b/Source/cmDiagnostics.cxx @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -41,6 +42,56 @@ constexpr cmDiagnostics::DiagnosticCategoryInformation cmDiagnostics::CategoryInfo[cmDiagnostics::CategoryCount]; #endif +cm::string_view cmDiagnostics::GetActionString(DiagnosticAction action) +{ + switch (action) { + case Ignore: + return "IGNORE"_s; + case Warn: + return "WARN"_s; + case SendError: + return "SEND_ERROR"_s; + case FatalError: + return "FATAL_ERROR"_s; + default: + return {}; + } +} + +cm::string_view cmDiagnostics::GetCategoryString(DiagnosticCategory category) +{ + static cm::string_view const names[CategoryCount] = { + {}, // CMD_NONE +#define CATEGORY_NAME(C) #C ""_s, + CM_FOR_EACH_DIAGNOSTIC_CATEGORY(CATEGORY_NAME) +#undef CATEGORY_MAP + }; + + if (category < CategoryCount) { + return names[category]; + } + return {}; +} + +cm::optional +cmDiagnostics::GetDiagnosticAction(cm::string_view name) +{ + if (name == "IGNORE"_s) { + return DiagnosticAction::Ignore; + } + if (name == "WARN"_s) { + return DiagnosticAction::Warn; + } + if (name == "SEND_ERROR"_s) { + return DiagnosticAction::SendError; + } + if (name == "FATAL_ERROR"_s) { + return DiagnosticAction::FatalError; + } + + return cm::nullopt; +} + cm::optional cmDiagnostics::GetDiagnosticCategory(cm::string_view name) { diff --git a/Source/cmDiagnostics.h b/Source/cmDiagnostics.h index 623b7d75d0..8f44eaa361 100644 --- a/Source/cmDiagnostics.h +++ b/Source/cmDiagnostics.h @@ -77,6 +77,16 @@ public: #undef DIAGNOSTIC_CATEGORY_INFO }; + //! convert an action identifier into a string + static cm::string_view GetActionString(DiagnosticAction); + + //! convert a category identifier into a string + static cm::string_view GetCategoryString(DiagnosticCategory); + + //! Convert a string action into an identifier + static cm::optional GetDiagnosticAction( + cm::string_view name); + //! Convert a string category into an identifier static cm::optional GetDiagnosticCategory( cm::string_view name); diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index a2a6bdc42c..78e3e40f05 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -643,6 +643,8 @@ endif() add_RunCMake_test(CompileFeatures -DCMake_NO_C_STANDARD=${CMake_NO_C_STANDARD} -DCMake_NO_CXX_STANDARD=${CMake_NO_CXX_STANDARD}) add_RunCMake_test(Policy) add_RunCMake_test(PolicyScope) +add_RunCMake_test(Diagnostics) +add_RunCMake_test(DiagnosticScope) add_RunCMake_test(WriteBasicConfigVersionFile) add_RunCMake_test(WriteCompilerDetectionHeader) add_RunCMake_test(SourceProperties) diff --git a/Tests/RunCMake/DiagnosticScope/Block.cmake b/Tests/RunCMake/DiagnosticScope/Block.cmake new file mode 100644 index 0000000000..a69324db7f --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/Block.cmake @@ -0,0 +1,22 @@ +cmake_diagnostic(SET CMD_AUTHOR IGNORE) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "IGNORE") + message(SEND_ERROR "failed to set diagnostic state") +endif() + +block(SCOPE_FOR DIAGNOSTICS) + +cmake_diagnostic(SET CMD_AUTHOR SEND_ERROR) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "SEND_ERROR") + message(SEND_ERROR "failed to set diagnostic state") +endif() + +endblock() + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "IGNORE") + message(SEND_ERROR "failed to restore diagnostic state") +endif() diff --git a/Tests/RunCMake/DiagnosticScope/CMakeLists.txt b/Tests/RunCMake/DiagnosticScope/CMakeLists.txt new file mode 100644 index 0000000000..d383479892 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.3) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake NO_DIAGNOSTIC_SCOPE) diff --git a/Tests/RunCMake/DiagnosticScope/DiagnosticInclude.cmake b/Tests/RunCMake/DiagnosticScope/DiagnosticInclude.cmake new file mode 100644 index 0000000000..1d9d748da2 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/DiagnosticInclude.cmake @@ -0,0 +1,6 @@ +cmake_diagnostic(SET CMD_AUTHOR SEND_ERROR) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "SEND_ERROR") + message(SEND_ERROR "failed to set diagnostic state") +endif() diff --git a/Tests/RunCMake/DiagnosticScope/IncludeWithScope.cmake b/Tests/RunCMake/DiagnosticScope/IncludeWithScope.cmake new file mode 100644 index 0000000000..2de0db5478 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/IncludeWithScope.cmake @@ -0,0 +1,7 @@ +cmake_diagnostic(SET CMD_AUTHOR WARN) +include(DiagnosticInclude.cmake) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "WARN") + message(SEND_ERROR "include unexpectedly leaked diagnostic state") +endif() diff --git a/Tests/RunCMake/DiagnosticScope/IncludeWithoutScope.cmake b/Tests/RunCMake/DiagnosticScope/IncludeWithoutScope.cmake new file mode 100644 index 0000000000..fa71e3f143 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/IncludeWithoutScope.cmake @@ -0,0 +1,7 @@ +cmake_diagnostic(SET CMD_AUTHOR WARN) +include(DiagnosticInclude.cmake NO_DIAGNOSTIC_SCOPE) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "SEND_ERROR") + message(SEND_ERROR "include unexpectedly encapsulated diagnostic state") +endif() diff --git a/Tests/RunCMake/DiagnosticScope/NotClosed-result.txt b/Tests/RunCMake/DiagnosticScope/NotClosed-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/NotClosed-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/DiagnosticScope/NotClosed-stderr.txt b/Tests/RunCMake/DiagnosticScope/NotClosed-stderr.txt new file mode 100644 index 0000000000..27dc157c1e --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/NotClosed-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error in NotClosed\.cmake: + cmake_diagnostic PUSH without matching POP +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/DiagnosticScope/NotClosed.cmake b/Tests/RunCMake/DiagnosticScope/NotClosed.cmake new file mode 100644 index 0000000000..17d62bc358 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/NotClosed.cmake @@ -0,0 +1 @@ +cmake_diagnostic(PUSH) diff --git a/Tests/RunCMake/DiagnosticScope/NotOpened-result.txt b/Tests/RunCMake/DiagnosticScope/NotOpened-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/NotOpened-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/DiagnosticScope/NotOpened-stderr.txt b/Tests/RunCMake/DiagnosticScope/NotOpened-stderr.txt new file mode 100644 index 0000000000..bdd1003a7d --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/NotOpened-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at NotOpened\.cmake:[0-9]+ \(cmake_diagnostic\): + cmake_diagnostic POP without matching PUSH +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/DiagnosticScope/NotOpened.cmake b/Tests/RunCMake/DiagnosticScope/NotOpened.cmake new file mode 100644 index 0000000000..be5e5f8b07 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/NotOpened.cmake @@ -0,0 +1 @@ +cmake_diagnostic(POP) diff --git a/Tests/RunCMake/DiagnosticScope/PushPop.cmake b/Tests/RunCMake/DiagnosticScope/PushPop.cmake new file mode 100644 index 0000000000..055506b017 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/PushPop.cmake @@ -0,0 +1,22 @@ +cmake_diagnostic(SET CMD_AUTHOR IGNORE) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "IGNORE") + message(SEND_ERROR "failed to set diagnostic state") +endif() + +cmake_diagnostic(PUSH) + +cmake_diagnostic(SET CMD_AUTHOR SEND_ERROR) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "SEND_ERROR") + message(SEND_ERROR "failed to set diagnostic state") +endif() + +cmake_diagnostic(POP) + +cmake_diagnostic(GET CMD_AUTHOR action) +if(NOT "${action}" STREQUAL "IGNORE") + message(SEND_ERROR "failed to restore diagnostic state") +endif() diff --git a/Tests/RunCMake/DiagnosticScope/RunCMakeTest.cmake b/Tests/RunCMake/DiagnosticScope/RunCMakeTest.cmake new file mode 100644 index 0000000000..ed6a4a0b76 --- /dev/null +++ b/Tests/RunCMake/DiagnosticScope/RunCMakeTest.cmake @@ -0,0 +1,8 @@ +include(RunCMake) + +run_cmake(PushPop) +run_cmake(Block) +run_cmake(IncludeWithScope) +run_cmake(IncludeWithoutScope) +run_cmake(NotClosed) +run_cmake(NotOpened) diff --git a/Tests/RunCMake/Diagnostics/Actions.cmake b/Tests/RunCMake/Diagnostics/Actions.cmake new file mode 100644 index 0000000000..0574a50612 --- /dev/null +++ b/Tests/RunCMake/Diagnostics/Actions.cmake @@ -0,0 +1,62 @@ +function(run_test ACTION INIT TARGET EXPECTED) + cmake_diagnostic(SET CMD_AUTHOR ${INIT}) + + cmake_diagnostic(GET CMD_AUTHOR action) + if(NOT "${action}" STREQUAL "${INIT}") + message(SEND_ERROR "failed to set diagnostic state") + endif() + + cmake_diagnostic(${ACTION} CMD_AUTHOR ${TARGET}) + + cmake_diagnostic(GET CMD_AUTHOR action) + if(NOT "${action}" STREQUAL "${EXPECTED}") + message(SEND_ERROR + "failed to change diagnostic state" + " (expected '${EXPECTED}', actual '${action}')" + ) + endif() +endfunction() + +run_test(SET IGNORE WARN WARN) +run_test(SET IGNORE SEND_ERROR SEND_ERROR) +run_test(SET IGNORE FATAL_ERROR FATAL_ERROR) + +run_test(PROMOTE IGNORE IGNORE IGNORE) +run_test(PROMOTE IGNORE WARN WARN) +run_test(PROMOTE IGNORE SEND_ERROR SEND_ERROR) +run_test(PROMOTE IGNORE FATAL_ERROR FATAL_ERROR) + +run_test(PROMOTE WARN IGNORE WARN) +run_test(PROMOTE WARN WARN WARN) +run_test(PROMOTE WARN SEND_ERROR SEND_ERROR) +run_test(PROMOTE WARN FATAL_ERROR FATAL_ERROR) + +run_test(PROMOTE SEND_ERROR IGNORE SEND_ERROR) +run_test(PROMOTE SEND_ERROR WARN SEND_ERROR) +run_test(PROMOTE SEND_ERROR SEND_ERROR SEND_ERROR) +run_test(PROMOTE SEND_ERROR FATAL_ERROR FATAL_ERROR) + +run_test(PROMOTE FATAL_ERROR IGNORE FATAL_ERROR) +run_test(PROMOTE FATAL_ERROR WARN FATAL_ERROR) +run_test(PROMOTE FATAL_ERROR SEND_ERROR FATAL_ERROR) +run_test(PROMOTE FATAL_ERROR FATAL_ERROR FATAL_ERROR) + +run_test(DEMOTE IGNORE IGNORE IGNORE) +run_test(DEMOTE IGNORE WARN IGNORE) +run_test(DEMOTE IGNORE SEND_ERROR IGNORE) +run_test(DEMOTE IGNORE FATAL_ERROR IGNORE) + +run_test(DEMOTE WARN IGNORE IGNORE) +run_test(DEMOTE WARN WARN WARN) +run_test(DEMOTE WARN SEND_ERROR WARN) +run_test(DEMOTE WARN FATAL_ERROR WARN) + +run_test(DEMOTE SEND_ERROR IGNORE IGNORE) +run_test(DEMOTE SEND_ERROR WARN WARN) +run_test(DEMOTE SEND_ERROR SEND_ERROR SEND_ERROR) +run_test(DEMOTE SEND_ERROR FATAL_ERROR SEND_ERROR) + +run_test(DEMOTE FATAL_ERROR IGNORE IGNORE) +run_test(DEMOTE FATAL_ERROR WARN WARN) +run_test(DEMOTE FATAL_ERROR SEND_ERROR SEND_ERROR) +run_test(DEMOTE FATAL_ERROR FATAL_ERROR FATAL_ERROR) diff --git a/Tests/RunCMake/Diagnostics/CMakeLists.txt b/Tests/RunCMake/Diagnostics/CMakeLists.txt new file mode 100644 index 0000000000..d383479892 --- /dev/null +++ b/Tests/RunCMake/Diagnostics/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.3) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake NO_DIAGNOSTIC_SCOPE) diff --git a/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake b/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake new file mode 100644 index 0000000000..e81246388c --- /dev/null +++ b/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake @@ -0,0 +1,3 @@ +include(RunCMake) + +run_cmake(Actions) diff --git a/Utilities/Sphinx/cmake.py b/Utilities/Sphinx/cmake.py index 93bd67db5f..62ebd4a4b4 100644 --- a/Utilities/Sphinx/cmake.py +++ b/Utilities/Sphinx/cmake.py @@ -461,6 +461,8 @@ class CMakeSignatureObject(CMakeObject): sigargs = self.targetnames[sig] else: def extract_keywords(params): + if params[-1].endswith(')'): + params[-1] = params[-1][:-1] for p in params: if p[0].isalpha(): yield p diff --git a/bootstrap b/bootstrap index 023403c6d2..29be509f95 100755 --- a/bootstrap +++ b/bootstrap @@ -311,6 +311,7 @@ CMAKE_CXX_SOURCES="\ cmCMakeLanguageCommand \ cmCMakeMinimumRequired \ cmList \ + cmCMakeDiagnosticCommand \ cmCMakePath \ cmCMakePathCommand \ cmCMakePolicyCommand \