diff --git a/Help/manual/presets/errors-properties.rst b/Help/manual/presets/errors-properties.rst index af4118514f..3f1216f9b3 100644 --- a/Help/manual/presets/errors-properties.rst +++ b/Help/manual/presets/errors-properties.rst @@ -6,12 +6,9 @@ ``author`` .. presets-versionadded:: 12 - An optional boolean. Equivalent to passing - :cmake-option:`-Werror=author` or - :cmake-option:`-Wno-error=author` - on the command line. - This may not be set to ``true`` - if ``warnings.author`` is set to ``false``. + An optional boolean. Equivalent to passing :cmake-option:`-Werror=author` or + :cmake-option:`-Wno-error=author` on the command line. + This may not be set to ``true`` if ``warnings.author`` is set to ``false``. .. _`CMakePresets.configurePresets.errors.deprecated`: diff --git a/Help/manual/presets/schema.yaml b/Help/manual/presets/schema.yaml index 2a9a935214..fe56d73903 100644 --- a/Help/manual/presets/schema.yaml +++ b/Help/manual/presets/schema.yaml @@ -657,21 +657,9 @@ properties: .. include:: presets/warnings-properties.rst properties: - # These should be in lexicographical order, except for systemVars. - author: - since: 12 - type: boolean - description: - An optional boolean. Equivalent to passing -Wauthor or - -Wno-author on the command line. This may not be set - to false if errors.author is set to true. - sphinxDescription: | - An optional boolean. Equivalent to passing - :option:`-Wauthor ` or - :option:`-Wno-author ` - on the command line. - This may not be set to ``false`` - if ``errors.author`` is set to ``true``. + # This should ONLY have diagnostics whose definitions require + # special handling (i.e. anything from preset version < 12). Others + # will be generated from cmDiagnostics.h. deprecated: type: boolean description: @@ -725,8 +713,8 @@ properties: on the command line. This may not be set to ``false`` if ``errors.unusedCli`` is set to ``true``. - # systemVars should be last. systemVars: + sort: false type: boolean description: An optional boolean. Setting this to true is equivalent to @@ -746,22 +734,9 @@ properties: .. include:: presets/errors-properties.rst properties: - # These should be in lexicographical order. - author: - since: 12 - type: boolean - description: - An optional boolean. Equivalent to passing - -Werror=author or -Wno-error=author - on the command line. This may not be set - to true if warnings.author is set to false. - sphinxDescription: | - An optional boolean. Equivalent to passing - :cmake-option:`-Werror=author` or - :cmake-option:`-Wno-error=author` - on the command line. - This may not be set to ``true`` - if ``warnings.author`` is set to ``false``. + # This should ONLY have diagnostics whose definitions require + # special handling (i.e. anything from preset version < 12). Others + # will be generated from cmDiagnostics.h. deprecated: type: boolean description: diff --git a/Help/manual/presets/warnings-properties.rst b/Help/manual/presets/warnings-properties.rst index 8a897787da..acfce8ffe5 100644 --- a/Help/manual/presets/warnings-properties.rst +++ b/Help/manual/presets/warnings-properties.rst @@ -6,12 +6,9 @@ ``author`` .. presets-versionadded:: 12 - An optional boolean. Equivalent to passing - :option:`-Wauthor ` or - :option:`-Wno-author ` - on the command line. - This may not be set to ``false`` - if ``errors.author`` is set to ``true``. + An optional boolean. Equivalent to passing :option:`-Wauthor ` or + :option:`-Wno-author ` on the command line. + This may not be set to ``false`` if ``errors.author`` is set to ``true``. .. _`CMakePresets.configurePresets.warnings.deprecated`: diff --git a/Source/cmDiagnostics.h b/Source/cmDiagnostics.h index 1d0638240b..3ab2239e1f 100644 --- a/Source/cmDiagnostics.h +++ b/Source/cmDiagnostics.h @@ -16,11 +16,14 @@ // IWYU pragma: no_forward_declare cmDiagnostics::DiagnosticCategory // The list of diagnostic categories along with their associated data. -// Each entry is of the form `SELECT(ACTION, , , )`. +// Each entry is of the form: +// `SELECT(ACTION, , , , )` // Entries MUST appear in the order that a depth-first enumeration would // produce. // -// When this changes, Help/manual/presets/schema.json must also be updated. +// When changing this table, Utilities/Scripts/regenerate-presets.py must be +// run to update the JSON schema for CMake presets. If the _TABLE macro name +// changes, said script will also need to be updated with the new name. #define CM_FOR_EACH_DIAGNOSTIC_TABLE(ACTION, SELECT) \ SELECT(ACTION, Warn, CMD_NONE, CMD_AUTHOR, 12) \ diff --git a/Utilities/Scripts/regenerate-presets.py b/Utilities/Scripts/regenerate-presets.py index ccb5bab079..a0a1bf466b 100755 --- a/Utilities/Scripts/regenerate-presets.py +++ b/Utilities/Scripts/regenerate-presets.py @@ -14,7 +14,9 @@ Usage: python3 regenerate-presets.py """ import json +import re from copy import deepcopy +from dataclasses import dataclass from pathlib import Path from typing import Any, Self @@ -30,12 +32,45 @@ SCHEMA_JSON_FILENAME = 'schema.json' WORKSPACE = Path(__file__).parent.parent.parent.absolute() PRESETS = WORKSPACE / PRESETS_REL +DIAGNOSTICS = WORKSPACE / 'Source/cmDiagnostics.h' RST_BANNER = f"""\ .. This file was generated by {Path(__file__).relative_to(WORKSPACE)} from {PRESETS_REL}/{SCHEMA_YAML_FILENAME}. Do not edit. """ +DIAGNOSTIC_TABLE_MACRO = 'CM_FOR_EACH_DIAGNOSTIC_TABLE' +CONFIGURE_PRESET_PROPERTIES_PATH = ( + 'properties', 'configurePresets', 'items', 'properties' +) +WARNING_DESCRIPTION = ( + 'An optional boolean. ' + 'Equivalent to passing -W{c} or -Wno-{c} on the command line. ' + 'This may not be set to false if errors.{c} is set to true.' +) +WARNING_SPHINX_DESCRIPTION = """ +An optional boolean. Equivalent to passing :option:`-W{c} ` or +:option:`-Wno-{c} ` on the command line. +This may not be set to ``false`` if ``errors.{c}`` is set to ``true``. +""".strip() +ERROR_DESCRIPTION = ( + 'An optional boolean. ' + 'Equivalent to passing -Werror={c} or -Wno-error={c} on the command line. ' + 'This may not be set to true if warnings.{c} is set to false.' +) +ERROR_SPHINX_DESCRIPTION = """ +An optional boolean. Equivalent to passing :cmake-option:`-Werror={c}` or +:cmake-option:`-Wno-error={c}` on the command line. +This may not be set to ``true`` if ``warnings.{c}`` is set to ``false``. +""".strip() + + +# ============================================================================= +@dataclass +class Diagnostic: + name: str + since: int + # ============================================================================= class Value: @@ -596,6 +631,105 @@ def trimDescriptions(data: Any): return data +# ----------------------------------------------------------------------------- +def getPath(data: Any, path: tuple[str]) -> Any: + if len(path) == 0: + return data + return getPath(data[path[0]], path[1:]) + + +# ----------------------------------------------------------------------------- +def diagnosticPresetName(symbol: str) -> str: + sep = False + out = '' + for c in symbol[4:]: + if c == '_': + sep = True + elif sep: + out += c + sep = False + else: + out += c.lower() + return out + + +# ----------------------------------------------------------------------------- +def buildDiagnosticsSchema( + diagnostics: list[Diagnostic], + descriptionTemplate: str, + sphinxDescriptionTemplate: str, +) -> dict[str, dict[str, Any]]: + out = {} + + for d in diagnostics: + out[d.name] = { + 'since': d.since, + 'type': 'boolean', + 'description': descriptionTemplate.format(c=d.name), + 'sphinxDescription': sphinxDescriptionTemplate.format(c=d.name), + } + + return out + + +# ----------------------------------------------------------------------------- +def mergeDiagnostics( + generated: dict[str, dict[str, Any]], + extra: dict[str, dict[str, Any]], +) -> dict[str, dict[str, Any]]: + # Get unsorted names. + tail = {} + for k in list(extra.keys()): + s = extra[k].pop('sort', True) + if not s: + tail[k] = extra.pop(k) + + # Combine inputs and sort. + out = {} + combined = deepcopy(generated) + combined.update(extra) + for k in sorted(combined.keys()): + out[k] = combined[k] + + # Add unsorted items and return result. + out.update(tail) + return out + + +# ----------------------------------------------------------------------------- +def readDiagnostics(path: Path | str) -> list[Diagnostic]: + content = '' + + # Extract diagnostics table from header. + with open(path, 'r') as f: + extracting = False + for line in f: + if DIAGNOSTIC_TABLE_MACRO in line: + extracting = True + continue + + if extracting: + content += line.replace('\\\n', ' ') + if not line.strip().endswith('\\'): + break + + out = [] + while True: + m = re.match(r'\s*SELECT[(]([^)]+)[)]', content) + if m is None: + break + + args = [a.strip() for a in m.group(1).split(',')] + n = diagnosticPresetName(args[3]) + v = int(args[4]) + if v > 1: + out.append(Diagnostic(n, v)) + + content = content[m.span()[1]:] + + return out + + # ----------------------------------------------------------------------------- def main(): # Read the schema definition. @@ -611,6 +745,18 @@ def main(): # Remove whitespace around descriptions. schema = trimDescriptions(schema) + # Read diagnostics and update schema. + diagnostics = readDiagnostics(DIAGNOSTICS) + configureSchema = getPath(schema, CONFIGURE_PRESET_PROPERTIES_PATH) + configureSchema['warnings']['properties'] = mergeDiagnostics( + buildDiagnosticsSchema(diagnostics, WARNING_DESCRIPTION, + WARNING_SPHINX_DESCRIPTION), + configureSchema['warnings']['properties']) + configureSchema['errors']['properties'] = mergeDiagnostics( + buildDiagnosticsSchema(diagnostics, ERROR_DESCRIPTION, + ERROR_SPHINX_DESCRIPTION), + configureSchema['errors']['properties']) + # Extract global type definitions. global TYPES, REFS