diff --git a/Help/command/install.rst b/Help/command/install.rst index 0d6ccb6116..e69d266749 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -61,9 +61,8 @@ signatures that specify them. The common options are: but not recommended. .. versionadded:: 4.4 - The :ref:`CMD_INSTALL_ABSOLUTE_DESTINATION ` - diagnostic can be enabled to warn or error out when an absolute destination - is provided. + The :diagnostic:`CMD_INSTALL_ABSOLUTE_DESTINATION` diagnostic can be + enabled to warn or error out when an absolute destination is provided. When a relative path is given, it is interpreted relative to the value of the :variable:`CMAKE_INSTALL_PREFIX` variable. diff --git a/Help/diagnostic/CMD_AUTHOR.rst b/Help/diagnostic/CMD_AUTHOR.rst new file mode 100644 index 0000000000..c309f310a6 --- /dev/null +++ b/Help/diagnostic/CMD_AUTHOR.rst @@ -0,0 +1,16 @@ +CMD_AUTHOR +---------- + +.. diagnostic:: + :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). diff --git a/Help/diagnostic/CMD_DEPRECATED.rst b/Help/diagnostic/CMD_DEPRECATED.rst new file mode 100644 index 0000000000..dfb23d59fc --- /dev/null +++ b/Help/diagnostic/CMD_DEPRECATED.rst @@ -0,0 +1,9 @@ +CMD_DEPRECATED +-------------- + +.. diagnostic:: + :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/Help/diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION.rst b/Help/diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION.rst new file mode 100644 index 0000000000..90380d08a9 --- /dev/null +++ b/Help/diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION.rst @@ -0,0 +1,9 @@ +CMD_INSTALL_ABSOLUTE_DESTINATION +-------------------------------- + +.. diagnostic:: + :default: ignore + + Warn when an :command:`install` command specifies an absolute ``DESTINATION`` + path. Absolute destinations are typically undesirable because they prevent + the installation prefix from being overridden at install time. diff --git a/Help/diagnostic/CMD_UNINITIALIZED.rst b/Help/diagnostic/CMD_UNINITIALIZED.rst new file mode 100644 index 0000000000..8c2edc84e2 --- /dev/null +++ b/Help/diagnostic/CMD_UNINITIALIZED.rst @@ -0,0 +1,7 @@ +CMD_UNINITIALIZED +----------------- + +.. diagnostic:: + :default: ignore + + Warn if an uninitialized variable is dereferenced. diff --git a/Help/diagnostic/CMD_UNUSED_CLI.rst b/Help/diagnostic/CMD_UNUSED_CLI.rst new file mode 100644 index 0000000000..718598eb8d --- /dev/null +++ b/Help/diagnostic/CMD_UNUSED_CLI.rst @@ -0,0 +1,12 @@ +CMD_UNUSED_CLI +-------------- + +.. diagnostic:: + :default: ignore + + Warn about variables that are declared on the command line, but not used. + + This diagnostic is issued outside of the configuration / generation phases. + Accordingly, while the action of this warning category can be queried as + usual, the :command:`cmake_diagnostic` command is unable to affect the action + of this diagnostic. diff --git a/Help/manual/cmake-diagnostics.7.rst b/Help/manual/cmake-diagnostics.7.rst index 5612299aae..de2b7057ce 100644 --- a/Help/manual/cmake-diagnostics.7.rst +++ b/Help/manual/cmake-diagnostics.7.rst @@ -32,71 +32,62 @@ issues in several categories: * Issues that may impact the ability of the project to be built with newer versions of CMake. -Diagnostic Actions ------------------- +Controlling Diagnostics +======================= -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 :option:`-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. +Each diagnostic category has an associated action to be taken when that +diagnostic is triggered. Most categories will warn by default. The available +actions are described in the :command:`cmake_diagnostic` command documentation. + +CMake maintains a diagnostic state stack that is similar to the policy state. +The initial state of the stack is determined by four factors, which are, in +order of precedence: + +* The default action associated with the diagnostic. + +* The action associated with the diagnostic stored in the CMake variable cache, + which is used to persist the initial state between CMake runs. + +* The :preset:`configurePresets.warnings` and :preset:`configurePresets.errors` + fields of :manual:`CMake Presets `. + +* The :option:`-W[no-][error=] ` command line arguments. + +.. note:: + + Because command line arguments operate both recursively and in the order + specified, some combinations of diagnostic arguments may result in later + arguments completely overwriting the action of earlier arguments. For + example, ``-Wno-child -Wparent`` will result in the ``child`` warning being + enabled, because ``-Wparent`` promotes both ``parent`` and ``child`` to at + least ``WARN`` severity. CMake presets are evaluated in order from most + ancestral to least ancestral. + +During script execution, the :command:`cmake_diagnostic` command can be used to +query or alter the state, or to perform limited stack manipulations. + +When a diagnostic is issued at configure time (or during script execution, when +CMake is running in script mode), the current diagnostic state controls the +action. Diagnostics issued at generate time, or outside of the configuration +and generation phases must make use of recorded state information. While CMake +strives to preserve this information in a way that matches the recorded state +to the state as of the CMake command which ultimately causes a diagnostic to be +issued, CMake may sometimes fall back to the state when processing of a +subdirectory completed, or even the root state. This may limit the ability of +the :command:`cmake_diagnostic` command to control such diagnostics, especially +if called from a function or included file. This is especially the case for +diagnostics that are not directly coupled to a CMake command. Diagnostic Categories ===================== -The following categories are defined. +The following categories are defined: -``CMD_AUTHOR`` (``-Wauthor``) ------------------------------ +.. toctree:: + :maxdepth: 1 -: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)`. - -``CMD_UNINITIALIZED`` (``-Wuninitialized``) -------------------------------------------- - -:Default: Ignore - -Warn if an uninitialized variable is dereferenced. - -``CMD_UNUSED_CLI`` (``-Wunused-cli``) -------------------------------------- - -:Default: Warn - -Warn about variables that are declared on the command line, but not used. - -Although the action of this warning category can be queried as usual, changes -made using the :command:`cmake_diagnostic` command have no effect. - -.. _CMD_INSTALL_ABSOLUTE_DESTINATION: - -``CMD_INSTALL_ABSOLUTE_DESTINATION`` (``-Winstall-absolute-destination``) -------------------------------------------------------------------------- - -:Default: Ignore - -Warn when an :command:`install` command specifies an absolute -``DESTINATION`` path. Absolute destinations are typically undesirable -because they prevent the installation prefix from being overridden at -install time. + /diagnostic/CMD_AUTHOR + /diagnostic/CMD_DEPRECATED + /diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION + /diagnostic/CMD_UNINITIALIZED + /diagnostic/CMD_UNUSED_CLI diff --git a/Help/release/dev/install-absolute-dest-configure-time.rst b/Help/release/dev/install-absolute-dest-configure-time.rst index 25a9c0add3..580a46c9f1 100644 --- a/Help/release/dev/install-absolute-dest-configure-time.rst +++ b/Help/release/dev/install-absolute-dest-configure-time.rst @@ -3,8 +3,10 @@ install-absolute-dest-configure-time * CMake gained the ability to diagnose :command:`install` commands that specify an absolute ``DESTINATION`` path via the - :ref:`CMD_INSTALL_ABSOLUTE_DESTINATION ` - diagnostic category. This diagnostic may be controlled with the - :option:`-Winstall-absolute-destination ` command-line option, the - ``installAbsoluteDestination`` field in a :manual:`cmake-presets(7)` - ``warnings`` object, or the :command:`cmake_diagnostic` command. + :diagnostic:`CMD_INSTALL_ABSOLUTE_DESTINATION` diagnostic category. + This diagnostic may be controlled with the + :option:`-Winstall-absolute-destination ` command-line option, + the ``installAbsoluteDestination`` field in a + :manual:`CMake Presets ` + :preset:`configurePresets.warnings` object, + or the :command:`cmake_diagnostic` command. diff --git a/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst b/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst index 84a27e5fe0..5ee7bf3a10 100644 --- a/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst +++ b/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst @@ -11,8 +11,7 @@ running the script, it may get fatal error messages from the script. .. versionadded:: 4.4 - The :ref:`CMD_INSTALL_ABSOLUTE_DESTINATION ` - diagnostic can be used to to generate errors for absolute install destinations - at generate time. + The :diagnostic:`CMD_INSTALL_ABSOLUTE_DESTINATION` diagnostic can be used to + generate errors for absolute install destinations at generate time. See also :variable:`CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION`. diff --git a/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst b/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst index fac680ea95..ac40b3d8ab 100644 --- a/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst +++ b/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst @@ -10,8 +10,7 @@ warning messages from the script. .. versionadded:: 4.4 - The :ref:`CMD_INSTALL_ABSOLUTE_DESTINATION ` - diagnostic can be used to to generate warnings for absolute install destinations - at generate time. + The :diagnostic:`CMD_INSTALL_ABSOLUTE_DESTINATION` diagnostic can be used to + generate warnings for absolute install destinations at generate time. See also :variable:`CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION`. diff --git a/Utilities/Sphinx/cmake.py b/Utilities/Sphinx/cmake.py index ca0b7a1377..9cac302b32 100644 --- a/Utilities/Sphinx/cmake.py +++ b/Utilities/Sphinx/cmake.py @@ -234,6 +234,7 @@ class _cmake_index_entry: _cmake_index_objs = { 'command': _cmake_index_entry('command'), 'cpack_gen': _cmake_index_entry('cpack generator'), + 'diagnostic': _cmake_index_entry('diagnostic'), 'envvar': _cmake_index_entry('envvar'), 'generator': _cmake_index_entry('generator'), 'genex': _cmake_index_entry('genex'), @@ -523,6 +524,79 @@ class CMakeSignatureObject(CMakeObject): return super().run() +class CMakeDiagnosticObject(CMakeObject): + object_type = 'diagnostic' + required_arguments = 0 + optional_arguments = 0 + + DEFAULT_CHOICES = {'ignore', 'warn', 'error'} + + def default_option(argument): + return directives.choice( + argument, CMakeDiagnosticObject.DEFAULT_CHOICES) + + option_spec = { + 'default': default_option, + 'parent': directives.unchanged, + } + + def __init__(self, *args, **kwargs): + self.targetname = None + super().__init__(*args, **kwargs) + + def _preset_name(self) -> str: + sep = False + out = '' + for c in self.targetname[4:]: + if c == '_': + sep = True + elif sep: + out += c + sep = False + else: + out += c.lower() + return out + + def _build_field(self, name: str, content: str | list[Node]) -> Node: + if type(content) is not list: + content = self.parse_text_to_nodes(content) + + name_node = nodes.field_name(text=name) + body_node = nodes.field_body('', *content) + return nodes.field('', name_node, body_node) + + def _build_cli(self) -> list[Node]: + cname = self.targetname[4:].lower().replace('_', '-') + ctext = f'-W[no-][error=]{cname}' + return self.parse_text_to_nodes(f':option:`{ctext} `') + + def _build_preset_refs(self) -> list[Node]: + p = self._preset_name() + w = f':preset:`warnings.{p} `' + e = f':preset:`errors.{p} `' + return self.parse_text_to_nodes(f'{w}, {e}') + + def run(self) -> list[Node]: + self.domain, self.objtype = self.name.split(':', 1) + doc = self.state.document + self.targetname = doc.next_node(nodes.title).astext() + + default = self.options['default'].capitalize() + parent = self.options.get('parent') + + headers = nodes.field_list() + headers += self._build_field('Command Line', self._build_cli()) + headers += self._build_field('Presets', self._build_preset_refs()) + headers += self._build_field('Default', default) + + if parent: + parentRef = self.parse_text_to_nodes(f':diagnostic:`{parent}`') + headers += self._build_field('Parent', parentRef) + + content = self.parse_content_to_nodes() + return [headers] + content + + class CMakeReferenceRole: # See sphinx.util.nodes.explicit_title_re; \x00 escapes '<'. _re = re.compile(r'^(.+?)(\s*)(?$', re.DOTALL) @@ -680,6 +754,7 @@ class CMakeDomain(Domain): object_types = { 'command': ObjType('command', 'command'), 'cpack_gen': ObjType('cpack_gen', 'cpack_gen'), + 'diagnostic': ObjType('diagnostic', 'diagnostic'), 'envvar': ObjType('envvar', 'envvar'), 'generator': ObjType('generator', 'generator'), 'genex': ObjType('genex', 'genex'), @@ -700,6 +775,7 @@ class CMakeDomain(Domain): directives = { 'command': CMakeObject, 'envvar': CMakeObject, + 'diagnostic': CMakeDiagnosticObject, 'genex': CMakeGenexObject, 'signature': CMakeSignatureObject, 'variable': CMakeObject, @@ -710,6 +786,7 @@ class CMakeDomain(Domain): 'cref': CMakeCRefRole(), 'command': CMakeXRefRole(fix_parens=True, lowercase=True), 'cpack_gen': CMakeXRefRole(), + 'diagnostic': CMakeXRefRole(), 'envvar': CMakeXRefRole(), 'generator': CMakeXRefRole(), 'genex': CMakeXRefRole(),