diff options
95 files changed, 3667 insertions, 390 deletions
diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst index 3ab0a62..9968239 100644 --- a/Help/manual/cmake-presets.7.rst +++ b/Help/manual/cmake-presets.7.rst @@ -71,6 +71,14 @@ The root object recognizes the following fields: An optional array of `Configure Preset`_ objects. +``buildPresets`` + + An optional array of `Build Preset`_ objects. + +``testPresets`` + + An optional array of `Test Preset`_ objects. + Configure Preset ^^^^^^^^^^^^^^^^ @@ -289,6 +297,489 @@ that may contain the following fields: An optional boolean. Setting this to ``true`` is equivalent to passing ``--debug-find`` on the command line. +Build Preset +^^^^^^^^^^^^ + +Each entry of the ``buildPresets`` array is a JSON object +that may contain the following fields: + +``name`` + + A required string representing the machine-friendly name of the preset. + This identifier is used in the ``--preset`` argument. There must not be + two presets (configure, build, or test) in the union of + ``CMakePresets.json`` and ``CMakeUserPresets.json`` in the same + directory with the same name. + +``hidden`` + + An optional boolean specifying whether or not a preset should be hidden. + If a preset is hidden, it cannot be used in the ``--preset`` argument + and does not have to have a valid ``configurePreset``, even from + inheritance. ``hidden`` presets are intended to be used as a base for + other presets to inherit via the ``inherits`` field. + +``inherits`` + + An optional array of strings representing the names of presets to + inherit from. The preset will inherit all of the fields from the + ``inherits`` presets by default (except ``name``, ``hidden``, + ``inherits``, ``description``, and ``displayName``), but can override + them as desired. If multiple ``inherits`` presets provide conflicting + values for the same field, the earlier preset in the ``inherits`` list + will be preferred. Presets in ``CMakePresets.json`` may not inherit from + presets in ``CMakeUserPresets.json``. + + This field can also be a string, which is equivalent to an array + containing one string. + +``vendor`` + + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map + if it does exist. However, it should follow the same conventions as the + root-level ``vendor`` field. If vendors use their own per-preset + ``vendor`` field, they should implement inheritance in a sensible manner + when appropriate. + +``displayName`` + + An optional string with a human-friendly name of the preset. + +``description`` + + An optional string with a human-friendly description of the preset. + +``environment`` + + An optional map of environment variables. The key is the variable name + (which may not be an empty string), and the value is either ``null`` or + a string representing the value of the variable. Each variable is set + regardless of whether or not a value was given to it by the process's + environment. This field supports macro expansion, and environment + variables in this map may reference each other, and may be listed in any + order, as long as such references do not cause a cycle (for example, if + ``ENV_1`` is ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``.) + + Environment variables are inherited through the ``inherits`` field, and + the preset's environment will be the union of its own ``environment`` + and the ``environment`` from all its parents. If multiple presets in + this union define the same variable, the standard rules of ``inherits`` + are applied. Setting a variable to ``null`` causes it to not be set, + even if a value was inherited from another preset. + +``configurePreset`` + + An optional string specifying the name of a configure preset to + associate with this build preset. If ``configurePreset`` is not + specified, it must be inherited from the inherits preset (unless this + preset is hidden). The build tree directory is inferred from the + configure preset. + +``inheritConfigureEnvironment`` + + An optional boolean that defaults to true. If true, the environment + variables from the associated configure preset are inherited after all + inherited build preset environments, but before environment variables + explicitly specified in this build preset. + +``jobs`` + + An optional integer. Equivalent to passing ``--parallel`` or ``-j`` on + the command line. + +``targets`` + + An optional string or array of strings. Equivalent to passing + ``--target`` or ``-t`` on the command line. Vendors may ignore the + targets property or hide build presets that explicitly specify targets. + This field supports macro expansion. + +``configuration`` + + An optional string. Equivalent to passing ``--config`` on the command + line. + +``cleanFirst`` + + An optional bool. If true, equivalent to passing ``--clean-first`` on + the command line. + +``verbose`` + + An optional bool. If true, equivalent to passing ``--verbose`` on the + command line. + +``nativeToolOptions`` + + An optional array of strings. Equivalent to passing options after ``--`` + on the command line. The array values support macro expansion. + +Test Preset +^^^^^^^^^^^ + +Each entry of the ``testPresets`` array is a JSON object +that may contain the following fields: + +``name`` + + A required string representing the machine-friendly name of the preset. + This identifier is used in the ``--preset`` argument. There must not be + two presets (configure, build, or test) in the union of + ``CMakePresets.json`` and ``CMakeUserPresets.json`` in the same + directory with the same name. + +``hidden`` + + An optional boolean specifying whether or not a preset should be hidden. + If a preset is hidden, it cannot be used in the ``--preset`` argument + and does not have to have a valid ``configurePreset``, even from + inheritance. ``hidden`` presets are intended to be used as a base for + other presets to inherit via the ``inherits`` field. + +``inherits`` + + An optional array of strings representing the names of presets to + inherit from. The preset will inherit all of the fields from the + ``inherits`` presets by default (except ``name``, ``hidden``, + ``inherits``, ``description``, and ``displayName``), but can override + them as desired. If multiple ``inherits`` presets provide conflicting + values for the same field, the earlier preset in the ``inherits`` list + will be preferred. Presets in ``CMakePresets.json`` may not inherit from + presets in ``CMakeUserPresets.json``. + + This field can also be a string, which is equivalent to an array + containing one string. + +``vendor`` + + An optional map containing vendor-specific information. CMake does not + interpret the contents of this field except to verify that it is a map + if it does exist. However, it should follow the same conventions as the + root-level ``vendor`` field. If vendors use their own per-preset + ``vendor`` field, they should implement inheritance in a sensible manner + when appropriate. + +``displayName`` + + An optional string with a human-friendly name of the preset. + +``description`` + + An optional string with a human-friendly description of the preset. + +``environment`` + + An optional map of environment variables. The key is the variable name + (which may not be an empty string), and the value is either ``null`` or + a string representing the value of the variable. Each variable is set + regardless of whether or not a value was given to it by the process's + environment. This field supports macro expansion, and environment + variables in this map may reference each other, and may be listed in any + order, as long as such references do not cause a cycle (for example, if + ``ENV_1`` is ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``.) + + Environment variables are inherited through the ``inherits`` field, and + the preset's environment will be the union of its own ``environment`` + and the ``environment`` from all its parents. If multiple presets in + this union define the same variable, the standard rules of ``inherits`` + are applied. Setting a variable to ``null`` causes it to not be set, + even if a value was inherited from another preset. + +``configurePreset`` + + An optional string specifying the name of a configure preset to + associate with this test preset. If ``configurePreset`` is not + specified, it must be inherited from the inherits preset (unless this + preset is hidden). The build tree directory is inferred from the + configure preset. + +``inheritConfigureEnvironment`` + + An optional boolean that defaults to true. If true, the environment + variables from the associated configure preset are inherited after all + inherited test preset environments, but before environment variables + explicitly specified in this test preset. + +``configuration`` + + An optional string. Equivalent to passing ``--build-config`` on the + command line. + +``overwriteConfigurationFile`` + + An optional array of configuration options to overwrite options + specified in the CTest configuration file. Equivalent to passing + ``--overwrite`` for each value in the array. The array values + support macro expansion. + +``output`` + + An optional object specifying output options. The object may contain the + following fields. + + ``shortProgress`` + + An optional bool. If true, equivalent to passing ``--progress`` on the + command line. + + ``verbosity`` + + An optional string specifying verbosity level. Must be one of the + following: + + ``default`` + + Equivalent to passing no verbosity flags on the command line. + + ``verbose`` + + Equivalent to passing ``--verbose`` on the command line. + + ``extra`` + + Equivalent to passing ``--extra-verbose`` on the command line. + + ``debug`` + + An optional bool. If true, equivalent to passing ``--debug`` on the + command line. + + ``outputOnFailure`` + + An optional bool. If true, equivalent to passing + ``--output-on-failure`` on the command line. + + ``quiet`` + + An optional bool. If true, equivalent to passing ``--quiet`` on the + command line. + + ``outputLogFile`` + + An optional string specifying a path to a log file. Equivalent to + passing ``--output-log`` on the command line. This field supports + macro expansion. + + ``labelSummary`` + + An optional bool. If false, equivalent to passing + ``--no-label-summary`` on the command line. + + ``subprojectSummary`` + + An optional bool. If false, equivalent to passing + ``--no-subproject-summary`` on the command line. + + ``maxPassedTestOutputSize`` + + An optional integer specifying the maximum output for passed tests in + bytes. Equivalent to passing ``--test-output-size-passed`` on the + command line. + + ``maxFailedTestOutputSize`` + + An optional integer specifying the maximum output for failed tests in + bytes. Equivalent to passing ``--test-output-size-failed`` on the + command line. + + ``maxTestNameWidth`` + + An optional integer specifying the maximum width of a test name to + output. Equivalent to passing ``--max-width`` on the command line. + +``filter`` + + An optional object specifying how to filter the tests to run. The object + may contain the following fields. + + ``include`` + + An optional object specifying which tests to include. The object may + contain the following fields. + + ``name`` + + An optional string specifying a regex for test names. Equivalent to + passing ``--tests-regex`` on the command line. This field supports + macro expansion. + + + ``label`` + + An optional string specifying a regex for test labels. Equivalent to + passing ``--label-regex`` on the command line. This field supports + macro expansion. + + ``useUnion`` + + An optional bool. Equivalent to passing ``--union`` on the command + line. + + ``index`` + + An optional object specifying tests to include by test index. The + object may contain the following fields. Can also be an optional + string specifying a file with the command line syntax for + ``--tests-information``. If specified as a string, this field + supports macro expansion. + + ``start`` + + An optional integer specifying a test index to start testing at. + + ``end`` + + An optional integer specifying a test index to stop testing at. + + ``stride`` + + An optional integer specifying the increment. + + ``specificTests`` + + An optional array of integers specifying specific test indices to + run. + + ``exclude`` + + An optional object specifying which tests to exclude. The object may + contain the following fields. + + ``name`` + + An optional string specifying a regex for test names. Equivalent to + passing ``--exclude-regex`` on the command line. This field supports + macro expansion. + + ``label`` + + An optional string specifying a regex for test labels. Equivalent to + passing ``--label-exclude`` on the command line. This field supports + macro expansion. + + ``fixtures`` + + An optional object specifying which fixtures to exclude from adding + tests. The object may contain the following fields. + + ``any`` + + An optional string specifying a regex for text fixtures to exclude + from adding any tests. Equivalent to ``--fixture-exclude-any`` on + the command line. This field supports macro expansion. + + ``setup`` + + An optional string specifying a regex for text fixtures to exclude + from adding setup tests. Equivalent to ``--fixture-exclude-setup`` + on the command line. This field supports macro expansion. + + ``cleanup`` + + An optional string specifying a regex for text fixtures to exclude + from adding cleanup tests. Equivalent to + ``--fixture-exclude-cleanup`` on the command line. This field + supports macro expansion. + +``execution`` + + An optional object specifying options for test execution. The object may + contain the following fields. + + ``stopOnFailure`` + + An optional bool. If true, equivalent to passing ``--stop-on-failure`` + on the command line. + + ``enableFailover`` + + An optional bool. If true, equivalent to passing ``-F`` on the command + line. + + ``jobs`` + + An optional integer. Equivalent to passing ``--parallel`` on the + command line. + + ``resourceSpecFile`` + + An optional string. Equivalent to passing ``--resource-spec-file`` on + the command line. This field supports macro expansion. + + ``testLoad`` + + An optional integer. Equivalent to passing ``--test-load`` on the + command line. + + ``showOnly`` + + An optional string. Equivalent to passing ``--show-only`` on the + command line. The string must be one of the following values: + + ``human`` + + ``json-v1`` + + ``rerunFailed`` + + An optional bool. If true, equivalent to passing ``--rerun-failed`` on + the command line. + + ``repeat`` + + An optional object specifying how to repeat tests. Equivalent to + passing ``--repeat`` on the command line. The object must have the + following fields. + + ``mode`` + + A required string. Must be one of the following values: + + ``until-fail`` + + ``until-pass`` + + ``after-timeout`` + + ``count`` + + A required integer. + + ``interactiveDebugging`` + + An optional bool. If true, equivalent to passing + ``--interactive-debug-mode 1`` on the command line. If false, + equivalent to passing ``--interactive-debug-mode 0`` on the command + line. + + ``scheduleRandom`` + + An optional bool. If true, equivalent to passing ``--schedule-random`` + on the command line. + + ``timeout`` + + An optional integer. Equivalent to passing ``--timeout`` on the + command line. + + ``noTestsAction`` + + An optional string specifying the behavior if no tests are found. Must + be one of the following values: + + ``default`` + + Equivalent to not passing any value on the command line. + + ``error`` + + Equivalent to passing ``--no-tests=error`` on the command line. + + ``ignore`` + + Equivalent to passing ``--no-tests=ignore`` on the command line. + Macro Expansion ^^^^^^^^^^^^^^^ @@ -326,7 +817,9 @@ Recognized macros include: ``${generator}`` - Generator specified in the preset's ``generator`` field. + Generator specified in the preset's ``generator`` field. For build and + test presets, this will evaluate to the generator specified by + ``configurePreset``. ``${dollar}`` diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index 7efe0cd..b2bd1ed 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst @@ -382,7 +382,8 @@ Options ``<path-to-source>/CMakePresets.json`` and ``<path-to-source>/CMakeUserPresets.json``. The preset specifies the generator and the build directory, and optionally a list of variables and - other arguments to pass to CMake. The :manual:`CMake GUI <cmake-gui(1)>` can + other arguments to pass to CMake. The current working directory must contain + CMake preset files. The :manual:`CMake GUI <cmake-gui(1)>` can also recognize ``CMakePresets.json`` and ``CMakeUserPresets.json`` files. For full details on these files, see :manual:`cmake-presets(7)`. @@ -392,6 +393,10 @@ Options a variable called ``MYVAR`` to ``1``, but the user sets it to ``2`` with a ``-D`` argument, the value ``2`` is preferred. +``--list-presets, --list-presets=<[configure | build | test | all]>`` + Lists the available presets. If no option is specified only configure presets + will be listed. The current working directory must contain CMake preset files. + .. _`Build Tool Mode`: Build a Project @@ -402,13 +407,24 @@ project binary tree: .. code-block:: shell - cmake --build <dir> [<options>] [-- <build-tool-options>] + cmake --build [<dir> | --preset <preset>] [<options>] [-- <build-tool-options>] This abstracts a native build tool's command-line interface with the following options: ``--build <dir>`` - Project binary directory to be built. This is required and must be first. + Project binary directory to be built. This is required (unless a preset + is specified) and must be first. + +``--preset <preset>`` + Use a build preset to specify build options. The project binary directory + is inferred from the ``configurePreset`` key. The current working directory + must contain CMake preset files. + See :manual:`preset <cmake-presets(7)>` for more details. + +``--list-presets`` + Lists the available build presets. The current working directory must + contain CMake preset files. ``--parallel [<jobs>], -j [<jobs>]`` The maximum number of concurrent processes to use when building. diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst index 16afcec..43157b6 100644 --- a/Help/manual/ctest.1.rst +++ b/Help/manual/ctest.1.rst @@ -28,6 +28,16 @@ This program will run the tests and report results. Options ======= +``--preset <preset>`` + Use a test preset to specify test options. The project binary directory + is inferred from the ``configurePreset`` key. The current working directory + must contain CMake preset files. + See :manual:`preset <cmake-presets(7)>` for more details. + +``--list-presets`` + Lists the available test presets. The current working directory must contain + CMake preset files. + ``-C <cfg>, --build-config <cfg>`` Choose configuration to test. diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json index 57b063e..ffbd7fe 100644 --- a/Help/manual/presets/schema.json +++ b/Help/manual/presets/schema.json @@ -2,13 +2,38 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "description": "The presets specify the generator and the build directory, and optionally a list of variables and other arguments to pass to CMake.", - "properties": { - "version": { - "type": "integer", - "description": "A required integer representing the version of the JSON schema. Currently, the only supported version is 1.", - "minimum": 1, - "maximum": 1 + "oneOf": [ + { + "properties": { + "version": { + "const": 1, + "description": "A required integer representing the version of the JSON schema." + }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"}, + "vendor": { "$ref": "#/definitions/vendor" }, + "configurePresets": { "$ref": "#/definitions/configurePresets"} + }, + "additionalProperties": false }, + { + "properties": { + "version": { + "const": 2, + "description": "A required integer representing the version of the JSON schema." + }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"}, + "vendor": { "$ref": "#/definitions/vendor" }, + "configurePresets": { "$ref": "#/definitions/configurePresets"}, + "buildPresets": { "$ref": "#/definitions/buildPresets"}, + "testPresets": { "$ref": "#/definitions/testPresets"} + }, + "additionalProperties": false + } + ], + "required": [ + "version" + ], + "definitions": { "cmakeMinimumRequired": { "type": "object", "description": "An optional object representing the minimum version of CMake needed to build this project.", @@ -47,7 +72,7 @@ }, "hidden": { "type": "boolean", - "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset= argument, will not show up in the CMake GUI, and does not have to have a valid generator or binaryDir, even from inheritance. hidden presets are intended to be used as a base for other presets to inherit via the inherits field." + "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset= argument, will not show up in the CMake GUI, and does not have to have a valid generator or binaryDir, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field." }, "inherits": { "anyOf": [ @@ -58,7 +83,7 @@ }, { "type": "array", - "description": "An optional array of strings representing the names of presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and longDescription), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json may not inherit from presets in CMakeUserPresets.json.", + "description": "An optional array of strings representing the names of presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json may not inherit from presets in CMakeUserPresets.json.", "items": { "type": "string", "description": "An optional string representing the name of the preset to inherit from.", @@ -283,10 +308,444 @@ ], "additionalProperties": false } + }, + "buildPresets": { + "type": "array", + "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 2 and higher.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the --preset argument. There must not be two presets (configure, build, or test) in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name.", + "minLength": 1 + }, + "hidden": { + "type": "boolean", + "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument, will not show up in the CMake GUI, and does not have to have a valid configurePreset, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field." + }, + "inherits": { + "anyOf": [ + { + "type": "string", + "description": "An optional string representing the name of the build preset to inherit from.", + "minLength": 1 + }, + { + "type": "array", + "description": "An optional array of strings representing the names of build presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json may not inherit from presets in CMakeUserPresets.json.", + "items": { + "type": "string", + "description": "An optional string representing the name of the preset to inherit from.", + "minLength": 1 + } + } + ] + }, + "configurePreset": { + "type": "string", + "description": "An optional string specifying the name of a configure preset to associate with this build preset. If configurePreset is not specified, it must be inherited from the inherits preset (unless this preset is hidden). The build tree directory is inferred from the configure preset.", + "minLength": 1 + }, + "vendor": { + "type": "object", + "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, it should follow the same conventions as the root-level vendor field. If vendors use their own per-preset vendor field, they should implement inheritance in a sensible manner when appropriate.", + "properties": {} + }, + "displayName": { + "type": "string", + "description": "An optional string with a human-friendly name of the preset." + }, + "description": { + "type": "string", + "description": "An optional string with a human-friendly description of the preset." + }, + "inheritConfigureEnvironment": { + "type": "boolean", + "description": "An optional Boolean that defaults to true. If true, the environment variables from the associated configure preset are inherited after all inherited build preset environments, but before environment variables explicitly specified in this build preset." + }, + "environment": { + "type": "object", + "description": "An optional map of environment variables. The key is the variable name (which must not be an empty string). Each variable is set regardless of whether or not a value was given to it by the process's environment. This field supports macro expansion, and environment variables in this map may reference each other, and may be listed in any order, as long as such references do not cause a cycle (for example,if ENV_1 is $env{ENV_2}, ENV_2 may not be $env{ENV_1}.) Environment variables are inherited through the inherits field, and the preset's environment will be the union of its own environment and the environment from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied. Setting a variable to null causes it to not be set, even if a value was inherited from another preset.", + "properties": {}, + "additionalProperties": { + "anyOf": [ + { + "type": "null", + "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." + }, + { + "type": "string", + "description": "A string representing the value of the variable." + } + ] + }, + "propertyNames": { + "pattern": "^.+$" + } + }, + "jobs": { + "type": "integer", + "description": "An optional integer. Equivalent to passing --parallel or -j on the command line." + }, + "targets": { + "anyOf": [ + { + "type": "string", + "description": "An optional string. Equivalent to passing --target or -t on the command line. Vendors may ignore the targets property or hide build presets that explicitly specify targets." + }, + { + "type": "array", + "description": "An optional array of strings. Equivalent to passing --target or -t on the command line. Vendors may ignore the targets property or hide build presets that explicitly specify targets.", + "items": { + "type": "string", + "description": "An optional string. Equivalent to passing --target or -t on the command line. Vendors may ignore the targets property or hide build presets that explicitly specify targets." + } + } + ] + }, + "configuration": { + "type": "string", + "description": "An optional string. Equivalent to passing --config on the command line." + }, + "cleanFirst": { + "type": "boolean", + "description": "An optional bool. If true, equivalent to passing --clean-first on the command line." + }, + "verbose": { + "type": "boolean", + "description": "An optional bool. If true, equivalent to passing --verbose on the command line." + }, + "nativeToolOptions": { + "type": "array", + "description": "An optional array of strings. Equivalent to passing options after -- on the command line.", + "items": { + "type": "string", + "description": "An optional string representing an option to pass after -- on the command line." + } + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "testPresets": { + "type": "array", + "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 2 and higher.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the --preset argument. There must not be two presets (configure, build, or test) in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name.", + "minLength": 1 + }, + "hidden": { + "type": "boolean", + "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument, will not show up in the CMake GUI, and does not have to have a valid configurePreset, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field." + }, + "inherits": { + "anyOf": [ + { + "type": "string", + "description": "An optional string representing the name of the test preset to inherit from.", + "minLength": 1 + }, + { + "type": "array", + "description": "An optional array of strings representing the names of test presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json may not inherit from presets in CMakeUserPresets.json.", + "items": { + "type": "string", + "description": "An optional string representing the name of the preset to inherit from.", + "minLength": 1 + } + } + ] + }, + "configurePreset": { + "type": "string", + "description": "An optional string specifying the name of a configure preset to associate with this test preset. If configurePreset is not specified, it must be inherited from the inherits preset (unless this preset is hidden). The build tree directory is inferred from the configure preset.", + "minLength": 1 + }, + "vendor": { + "type": "object", + "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, it should follow the same conventions as the root-level vendor field. If vendors use their own per-preset vendor field, they should implement inheritance in a sensible manner when appropriate.", + "properties": {} + }, + "displayName": { + "type": "string", + "description": "An optional string with a human-friendly name of the preset." + }, + "description": { + "type": "string", + "description": "An optional string with a human-friendly description of the preset." + }, + "inheritConfigureEnvironment": { + "type": "boolean", + "description": "An optional Boolean that defaults to true. If true, the environment variables from the associated configure preset are inherited after all inherited test preset environments, but before environment variables explicitly specified in this test preset." + }, + "environment": { + "type": "object", + "description": "An optional map of environment variables. The key is the variable name (which must not be an empty string). Each variable is set regardless of whether or not a value was given to it by the process's environment. This field supports macro expansion, and environment variables in this map may reference each other, and may be listed in any order, as long as such references do not cause a cycle (for example,if ENV_1 is $env{ENV_2}, ENV_2 may not be $env{ENV_1}.) Environment variables are inherited through the inherits field, and the preset's environment will be the union of its own environment and the environment from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied. Setting a variable to null causes it to not be set, even if a value was inherited from another preset.", + "properties": {}, + "additionalProperties": { + "anyOf": [ + { + "type": "null", + "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." + }, + { + "type": "string", + "description": "A string representing the value of the variable." + } + ] + }, + "propertyNames": { + "pattern": "^.+$" + } + }, + "configuration": { + "type": "string", + "description": "An optional string. Equivalent to passing --build-config on the command line." + }, + "overwriteConfigurationFile": { + "type": "array", + "description": "An optional array of configuration options to overwrite options specified in the CTest configuration file. Equivalent to passing ``--overwrite`` for each value in the array.", + "items": { + "type": "string", + "description": "An option written as a key-value pair in the form \"key=value\"." + } + }, + "output": { + "type": "object", + "description": "An optional object specifying output options.", + "properties": { + "shortProgress": { + "type": "boolean", + "description": "An optional bool. If true, equivalent to passing --progress on the command line." + }, + "verbosity": { + "type": "string", + "description": "An optional string specifying verbosity level. Valid values are \"default\" (equivalent to passing no verbosity flags on the command line), \"verbose\" (equivalent to passing --verbose on the command line), and \"extra\" (equivalent to passing --extra-verbose on the command line).", + "enum": [ + "default", "verbose", "extra" + ] + }, + "debug": { + "type": "boolean", + "description": "An optional bool. If true, equivalent to passing --debug on the command line." + }, + "outputOnFailure": { + "type": "boolean", + "description": "An optional bool. If true, equivalent to passing --output-on-failure on the command line." + }, + "quiet": { + "type": "boolean", + "description": "An optional bool. If true, equivalent to passing --quiet on the command line." + }, + "outputLogFile": { + "type": "string", + "description": "An optional string specifying a path to a log file. Equivalent to passing --output-log on the command line." + }, + "labelSummary": { + "type": "boolean", + "description": "An optional bool. If false, equivalent to passing --no-label-summary on the command line." + }, + "subprojectSummary": { + "type": "boolean", + "description": "An optional bool. If false, equivalent to passing --no-subproject-summary on the command line." + }, + "maxPassedTestOutputSize": { + "type": "integer", + "description": "An optional integer specifying the maximum output for passed tests in bytes. Equivalent to passing --test-output-size-passed on the command line." + }, + "maxFailedTestOutputSize": { + "type": "integer", + "description": "An optional integer specifying the maximum output for failed tests in bytes. Equivalent to passing --test-output-size-failed on the command line." + }, + "maxTestNameWidth": { + "type": "integer", + "description": "An optional integer specifying the maximum width of a test name to output. Equivalent to passing --max-width on the command line." + } + }, + "additionalProperties": false + }, + "filter": { + "type": "object", + "description": "An optional object specifying how to filter the tests to run.", + "properties": { + "include": { + "type": "object", + "description": "An optional object specifying which tests to include.", + "properties": { + "name": { + "type": "string", + "description": "An optional string specifying a regex for test names. Equivalent to passing --tests-regex on the command line." + }, + "label": { + "type": "string", + "description": "An optional string specifying a regex for test labels. Equivalent to passing --label-regex on the command line." + }, + "index": { + "anyOf": [ + { + "type": "object", + "description": "An optional object specifying tests to include by test index.", + "properties": { + "start": { + "type": "integer", + "description": "An optional integer specifying a test index to start testing at." + }, + "end": { + "type": "integer", + "description": "An optional integer specifying a test index to stop testing at." + }, + "stride": { + "type": "integer", + "description": "An optional integer specifying the increment." + }, + "specificTests": { + "type": "array", + "description": "An optional array of integers specifying specific test indices to run.", + "items": { + "type": "integer", + "description": "An integer specifying the test to run by index." + } + } + }, + "additionalProperties": false + }, + { + "type": "string", + "description": "An optional string specifying a file with the command line syntax for --tests-information." + } + ] + }, + "useUnion": { + "type": "boolean", + "description": "An optional bool. Equivalent to passing --union on the command line." + } + }, + "additionalProperties": false + }, + "exclude": { + "type": "object", + "description": "An optional object specifying which tests to exclude.", + "properties": { + "name": { + "type": "string", + "description": "An optional string specifying a regex for test names. Equivalent to passing --exclude-regex on the command line." + }, + "label": { + "type": "string", + "description": "An optional string specifying a regex for test labels. Equivalent to passing --label-exclude on the command line." + }, + "fixtures": { + "type": "object", + "description": "An optional object specifying which fixtures to exclude from adding tests.", + "properties": { + "any": { + "type": "string", + "description": "An optional string specifying a regex for text fixtures to exclude from adding any tests. Equivalent to --fixture-exclude-any on the command line." + }, + "setup": { + "type": "string", + "description": "An optional string specifying a regex for text fixtures to exclude from adding setup tests. Equivalent to --fixture-exclude-setup on the command line." + }, + "cleanup": { + "type": "string", + "description": "An optional string specifying a regex for text fixtures to exclude from adding cleanup tests. Equivalent to --fixture-exclude-cleanup on the command line." + } + }, + "additionalProperties": false + } + } + } + }, + "additionalProperties": false + }, + "execution": { + "type": "object", + "description": "An optional object specifying options for test execution.", + "properties": { + "stopOnFailure": { + "type": "boolean", + "description": "An optional bool. If true, equivalent to passing --stop-on-failure on the command line." + }, + "enableFailover": { + "type": "boolean", + "description": "An optional bool. If true, equivalent to passing -F on the command line." + }, + "jobs": { + "type": "integer", + "description": "An optional integer. Equivalent to passing --parallel on the command line." + }, + "resourceSpecFile": { + "type": "string", + "description": "An optional string. Equivalent to passing --resource-spec-file on the command line." + }, + "testLoad": { + "type": "integer", + "description": "An optional integer. Equivalent to passing --test-load on the command line." + }, + "showOnly": { + "type": "string", + "description": "An optional string. Equivalent to passing --show-only on the command line. Value must be \"human\" or \"json-v1\".", + "enum": [ + "human", "json-v1" + ] + }, + "repeat": { + "type": "object", + "description": "An optional object specifying how to repeat tests. Equivalent to passing --repeat on the command line.", + "properties": { + "mode": { + "type": "string", + "description": "A required string. Must be one of the following values: \"until-fail\", \"until-pass\", or \"after-timeout\".", + "enum": [ + "until-fail", "until-pass", "after-timeout" + ] + }, + "count": { + "type": "integer", + "description": "A required integer." + } + }, + "required": [ + "mode", "count" + ], + "additionalProperties": false + }, + "interactiveDebugging": { + "type": "boolean", + "description": "An optional bool. If true, equivalent to passing --interactive-debug-mode 1 on the command line. If false, equivalent to passing --interactive-debug-mode 0 on the command line." + }, + "scheduleRandom": { + "type": "boolean", + "description": "An optional bool. If true, equivalent to passing --schedule-random on the command line." + }, + "timeout": { + "type": "integer", + "description": "An optional integer. Equivalent to passing --timeout on the command line." + }, + "noTestsAction": { + "type": "string", + "description": "An optional string specifying the behavior if no tests are found. Must be one of the following values: \"default\" (equivalent to not passing any value on the command line), \"error\" (equivalent to passing --no-tests=error on the command line), or \"ignore\" (equivalent to passing --no-tests-ignore on the command line).", + "enum": [ + "default", "error", "ignore" + ] + } + }, + "additionalProperties": false + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } } - }, - "required": [ - "version" - ], - "additionalProperties": false + } } diff --git a/Help/release/dev/build-test-presets.rst b/Help/release/dev/build-test-presets.rst new file mode 100644 index 0000000..71d14bf --- /dev/null +++ b/Help/release/dev/build-test-presets.rst @@ -0,0 +1,4 @@ +build-test-presets +------------------ + +* :manual:`cmake-presets(7)` gained support for build and test presets. diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index 2f41f70..f593f83 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx @@ -68,9 +68,9 @@ QCMake::QCMake(QObject* p) connect(&this->LoadPresetsTimer, &QTimer::timeout, this, [this]() { this->loadPresets(); if (!this->PresetName.isEmpty() && - this->CMakePresetsFile.Presets.find( + this->CMakePresetsFile.ConfigurePresets.find( std::string(this->PresetName.toLocal8Bit())) == - this->CMakePresetsFile.Presets.end()) { + this->CMakePresetsFile.ConfigurePresets.end()) { this->setPreset(QString{}); } }); @@ -158,7 +158,7 @@ void QCMake::setPreset(const QString& name, bool setBinary) if (!name.isNull()) { std::string presetName(name.toLocal8Bit()); auto const& expandedPreset = - this->CMakePresetsFile.Presets[presetName].Expanded; + this->CMakePresetsFile.ConfigurePresets[presetName].Expanded; if (expandedPreset) { if (setBinary) { QString binaryDir = @@ -420,7 +420,8 @@ QCMakePropertyList QCMake::properties() const if (!this->PresetName.isNull()) { std::string presetName(this->PresetName.toLocal8Bit()); - auto const& p = this->CMakePresetsFile.Presets.at(presetName).Expanded; + auto const& p = + this->CMakePresetsFile.ConfigurePresets.at(presetName).Expanded; if (p) { for (auto const& v : p->CacheVariables) { if (!v.second) { @@ -535,8 +536,8 @@ void QCMake::loadPresets() this->LastLoadPresetsResult = result; QVector<QCMakePreset> presets; - for (auto const& name : this->CMakePresetsFile.PresetOrder) { - auto const& it = this->CMakePresetsFile.Presets[name]; + for (auto const& name : this->CMakePresetsFile.ConfigurePresetOrder) { + auto const& it = this->CMakePresetsFile.ConfigurePresets[name]; auto const& p = it.Unexpanded; if (p.Hidden) { continue; diff --git a/Source/cmCMakePresetsFile.cxx b/Source/cmCMakePresetsFile.cxx index 8762158..e5557e6 100644 --- a/Source/cmCMakePresetsFile.cxx +++ b/Source/cmCMakePresetsFile.cxx @@ -5,6 +5,7 @@ #include <algorithm> #include <cstdlib> #include <functional> +#include <iostream> #include <iterator> #include <utility> @@ -20,6 +21,26 @@ #include "cmSystemTools.h" #include "cmVersion.h" +#define CHECK_OK(expr) \ + { \ + auto _result = expr; \ + if (_result != ReadFileResult::READ_OK) \ + return _result; \ + } + +#define CHECK_EXPAND(out, field, expanders) \ + { \ + switch (ExpandMacros(field, expanders)) { \ + case ExpandMacroResult::Error: \ + return false; \ + case ExpandMacroResult::Ignore: \ + out.reset(); \ + return true; \ + case ExpandMacroResult::Ok: \ + break; \ + } \ + } + namespace { enum class CycleStatus { @@ -30,12 +51,13 @@ enum class CycleStatus using ReadFileResult = cmCMakePresetsFile::ReadFileResult; using CacheVariable = cmCMakePresetsFile::CacheVariable; -using UnexpandedPreset = cmCMakePresetsFile::UnexpandedPreset; -using ExpandedPreset = cmCMakePresetsFile::ExpandedPreset; +using ConfigurePreset = cmCMakePresetsFile::ConfigurePreset; +using BuildPreset = cmCMakePresetsFile::BuildPreset; +using TestPreset = cmCMakePresetsFile::TestPreset; using ArchToolsetStrategy = cmCMakePresetsFile::ArchToolsetStrategy; constexpr int MIN_VERSION = 1; -constexpr int MAX_VERSION = 1; +constexpr int MAX_VERSION = 2; struct CMakeVersion { @@ -47,7 +69,9 @@ struct CMakeVersion struct RootPresets { CMakeVersion CMakeMinimumRequired; - std::vector<cmCMakePresetsFile::UnexpandedPreset> Presets; + std::vector<cmCMakePresetsFile::ConfigurePreset> ConfigurePresets; + std::vector<cmCMakePresetsFile::BuildPreset> BuildPresets; + std::vector<cmCMakePresetsFile::TestPreset> TestPresets; }; cmJSONHelper<std::nullptr_t, ReadFileResult> VendorHelper(ReadFileResult error) @@ -183,35 +207,43 @@ auto const PresetOptionalBoolHelper = cmJSONOptionalHelper<bool, ReadFileResult>(ReadFileResult::READ_OK, PresetBoolHelper); +auto const PresetIntHelper = cmJSONIntHelper<ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET); + +auto const PresetOptionalIntHelper = cmJSONOptionalHelper<int, ReadFileResult>( + ReadFileResult::READ_OK, PresetIntHelper); + +auto const PresetVectorIntHelper = cmJSONVectorHelper<int, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, PresetIntHelper); + auto const PresetWarningsHelper = - cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>( + cmJSONObjectHelper<ConfigurePreset, ReadFileResult>( ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) - .Bind("dev"_s, &UnexpandedPreset::WarnDev, PresetOptionalBoolHelper, false) - .Bind("deprecated"_s, &UnexpandedPreset::WarnDeprecated, + .Bind("dev"_s, &ConfigurePreset::WarnDev, PresetOptionalBoolHelper, false) + .Bind("deprecated"_s, &ConfigurePreset::WarnDeprecated, PresetOptionalBoolHelper, false) - .Bind("uninitialized"_s, &UnexpandedPreset::WarnUninitialized, + .Bind("uninitialized"_s, &ConfigurePreset::WarnUninitialized, PresetOptionalBoolHelper, false) - .Bind("unusedCli"_s, &UnexpandedPreset::WarnUnusedCli, + .Bind("unusedCli"_s, &ConfigurePreset::WarnUnusedCli, PresetOptionalBoolHelper, false) - .Bind("systemVars"_s, &UnexpandedPreset::WarnSystemVars, + .Bind("systemVars"_s, &ConfigurePreset::WarnSystemVars, PresetOptionalBoolHelper, false); auto const PresetErrorsHelper = - cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>( + cmJSONObjectHelper<ConfigurePreset, ReadFileResult>( ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) - .Bind("dev"_s, &UnexpandedPreset::ErrorDev, PresetOptionalBoolHelper, - false) - .Bind("deprecated"_s, &UnexpandedPreset::ErrorDeprecated, + .Bind("dev"_s, &ConfigurePreset::ErrorDev, PresetOptionalBoolHelper, false) + .Bind("deprecated"_s, &ConfigurePreset::ErrorDeprecated, PresetOptionalBoolHelper, false); auto const PresetDebugHelper = - cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>( + cmJSONObjectHelper<ConfigurePreset, ReadFileResult>( ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) - .Bind("output"_s, &UnexpandedPreset::DebugOutput, PresetOptionalBoolHelper, + .Bind("output"_s, &ConfigurePreset::DebugOutput, PresetOptionalBoolHelper, false) - .Bind("tryCompile"_s, &UnexpandedPreset::DebugTryCompile, + .Bind("tryCompile"_s, &ConfigurePreset::DebugTryCompile, PresetOptionalBoolHelper, false) - .Bind("find"_s, &UnexpandedPreset::DebugFind, PresetOptionalBoolHelper, + .Bind("find"_s, &ConfigurePreset::DebugFind, PresetOptionalBoolHelper, false); ReadFileResult ArchToolsetStrategyHelper( @@ -239,18 +271,18 @@ ReadFileResult ArchToolsetStrategyHelper( return ReadFileResult::INVALID_PRESET; } -std::function<ReadFileResult(UnexpandedPreset&, const Json::Value*)> +std::function<ReadFileResult(ConfigurePreset&, const Json::Value*)> ArchToolsetHelper( - std::string UnexpandedPreset::*valueField, - cm::optional<ArchToolsetStrategy> UnexpandedPreset::*strategyField) + std::string ConfigurePreset::*valueField, + cm::optional<ArchToolsetStrategy> ConfigurePreset::*strategyField) { auto const objectHelper = - cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>( + cmJSONObjectHelper<ConfigurePreset, ReadFileResult>( ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) .Bind("value", valueField, PresetStringHelper, false) .Bind("strategy", strategyField, ArchToolsetStrategyHelper, false); return [valueField, strategyField, objectHelper]( - UnexpandedPreset& out, const Json::Value* value) -> ReadFileResult { + ConfigurePreset& out, const Json::Value* value) -> ReadFileResult { if (!value) { (out.*valueField).clear(); out.*strategyField = cm::nullopt; @@ -272,41 +304,397 @@ ArchToolsetHelper( } auto const ArchitectureHelper = ArchToolsetHelper( - &UnexpandedPreset::Architecture, &UnexpandedPreset::ArchitectureStrategy); + &ConfigurePreset::Architecture, &ConfigurePreset::ArchitectureStrategy); auto const ToolsetHelper = ArchToolsetHelper( - &UnexpandedPreset::Toolset, &UnexpandedPreset::ToolsetStrategy); + &ConfigurePreset::Toolset, &ConfigurePreset::ToolsetStrategy); -auto const PresetHelper = - cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>( +auto const ConfigurePresetHelper = + cmJSONObjectHelper<ConfigurePreset, ReadFileResult>( ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) - .Bind("name"_s, &UnexpandedPreset::Name, PresetStringHelper) - .Bind("inherits"_s, &UnexpandedPreset::Inherits, PresetInheritsHelper, + .Bind("name"_s, &ConfigurePreset::Name, PresetStringHelper) + .Bind("inherits"_s, &ConfigurePreset::Inherits, PresetInheritsHelper, false) - .Bind("hidden"_s, &UnexpandedPreset::Hidden, PresetBoolHelper, false) + .Bind("hidden"_s, &ConfigurePreset::Hidden, PresetBoolHelper, false) .Bind<std::nullptr_t>("vendor"_s, nullptr, VendorHelper(ReadFileResult::INVALID_PRESET), false) - .Bind("displayName"_s, &UnexpandedPreset::DisplayName, PresetStringHelper, + .Bind("displayName"_s, &ConfigurePreset::DisplayName, PresetStringHelper, false) - .Bind("description"_s, &UnexpandedPreset::Description, PresetStringHelper, + .Bind("description"_s, &ConfigurePreset::Description, PresetStringHelper, false) - .Bind("generator"_s, &UnexpandedPreset::Generator, PresetStringHelper, + .Bind("generator"_s, &ConfigurePreset::Generator, PresetStringHelper, false) .Bind("architecture"_s, ArchitectureHelper, false) .Bind("toolset"_s, ToolsetHelper, false) - .Bind("binaryDir"_s, &UnexpandedPreset::BinaryDir, PresetStringHelper, + .Bind("binaryDir"_s, &ConfigurePreset::BinaryDir, PresetStringHelper, false) .Bind<std::string>("cmakeExecutable"_s, nullptr, PresetStringHelper, false) - .Bind("cacheVariables"_s, &UnexpandedPreset::CacheVariables, + .Bind("cacheVariables"_s, &ConfigurePreset::CacheVariables, VariablesHelper, false) - .Bind("environment"_s, &UnexpandedPreset::Environment, - EnvironmentMapHelper, false) + .Bind("environment"_s, &ConfigurePreset::Environment, EnvironmentMapHelper, + false) .Bind("warnings"_s, PresetWarningsHelper, false) .Bind("errors"_s, PresetErrorsHelper, false) .Bind("debug"_s, PresetDebugHelper, false); -auto const PresetsHelper = - cmJSONVectorHelper<UnexpandedPreset, ReadFileResult>( - ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS, PresetHelper); +auto const BuildPresetHelper = + cmJSONObjectHelper<BuildPreset, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) + .Bind("name"_s, &BuildPreset::Name, PresetStringHelper) + .Bind("inherits"_s, &BuildPreset::Inherits, PresetInheritsHelper, false) + .Bind("hidden"_s, &BuildPreset::Hidden, PresetBoolHelper, false) + .Bind<std::nullptr_t>("vendor"_s, nullptr, + VendorHelper(ReadFileResult::INVALID_PRESET), false) + .Bind("displayName"_s, &BuildPreset::DisplayName, PresetStringHelper, + false) + .Bind("description"_s, &BuildPreset::Description, PresetStringHelper, + false) + .Bind("environment"_s, &BuildPreset::Environment, EnvironmentMapHelper, + false) + .Bind("configurePreset"_s, &BuildPreset::ConfigurePreset, + PresetStringHelper, false) + .Bind("inheritConfigureEnvironment"_s, + &BuildPreset::InheritConfigureEnvironment, PresetOptionalBoolHelper, + false) + .Bind("jobs"_s, &BuildPreset::Jobs, PresetOptionalIntHelper, false) + .Bind("targets"_s, &BuildPreset::Targets, PresetVectorStringHelper, false) + .Bind("configuration"_s, &BuildPreset::Configuration, PresetStringHelper, + false) + .Bind("cleanFirst"_s, &BuildPreset::CleanFirst, PresetOptionalBoolHelper, + false) + .Bind("verbose"_s, &BuildPreset::Verbose, PresetOptionalBoolHelper, false) + .Bind("nativeToolOptions"_s, &BuildPreset::NativeToolOptions, + PresetVectorStringHelper, false); + +ReadFileResult TestPresetOutputVerbosityHelper( + TestPreset::OutputOptions::VerbosityEnum& out, const Json::Value* value) +{ + if (!value) { + out = TestPreset::OutputOptions::VerbosityEnum::Default; + return ReadFileResult::READ_OK; + } + + if (!value->isString()) { + return ReadFileResult::INVALID_PRESET; + } + + if (value->asString() == "default") { + out = TestPreset::OutputOptions::VerbosityEnum::Default; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "verbose") { + out = TestPreset::OutputOptions::VerbosityEnum::Verbose; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "extra") { + out = TestPreset::OutputOptions::VerbosityEnum::Extra; + return ReadFileResult::READ_OK; + } + + return ReadFileResult::INVALID_PRESET; +} + +auto const TestPresetOptionalOutputVerbosityHelper = + cmJSONOptionalHelper<TestPreset::OutputOptions::VerbosityEnum, + ReadFileResult>(ReadFileResult::READ_OK, + TestPresetOutputVerbosityHelper); + +auto const TestPresetOptionalOutputHelper = + cmJSONOptionalHelper<TestPreset::OutputOptions, ReadFileResult>( + ReadFileResult::READ_OK, + cmJSONObjectHelper<TestPreset::OutputOptions, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) + .Bind("shortProgress"_s, &TestPreset::OutputOptions::ShortProgress, + PresetOptionalBoolHelper, false) + .Bind("verbosity"_s, &TestPreset::OutputOptions::Verbosity, + TestPresetOptionalOutputVerbosityHelper, false) + .Bind("debug"_s, &TestPreset::OutputOptions::Debug, + PresetOptionalBoolHelper, false) + .Bind("outputOnFailure"_s, &TestPreset::OutputOptions::OutputOnFailure, + PresetOptionalBoolHelper, false) + .Bind("quiet"_s, &TestPreset::OutputOptions::Quiet, + PresetOptionalBoolHelper, false) + .Bind("outputLogFile"_s, &TestPreset::OutputOptions::OutputLogFile, + PresetStringHelper, false) + .Bind("labelSummary"_s, &TestPreset::OutputOptions::LabelSummary, + PresetOptionalBoolHelper, false) + .Bind("subprojectSummary"_s, + &TestPreset::OutputOptions::SubprojectSummary, + PresetOptionalBoolHelper, false) + .Bind("maxPassedTestOutputSize"_s, + &TestPreset::OutputOptions::MaxPassedTestOutputSize, + PresetOptionalIntHelper, false) + .Bind("maxFailedTestOutputSize"_s, + &TestPreset::OutputOptions::MaxFailedTestOutputSize, + PresetOptionalIntHelper, false) + .Bind("maxTestNameWidth"_s, &TestPreset::OutputOptions::MaxTestNameWidth, + PresetOptionalIntHelper, false)); + +auto const TestPresetOptionalFilterIncludeIndexObjectHelper = + cmJSONOptionalHelper<TestPreset::IncludeOptions::IndexOptions, + ReadFileResult>( + ReadFileResult::READ_OK, + cmJSONObjectHelper<TestPreset::IncludeOptions::IndexOptions, + ReadFileResult>(ReadFileResult::READ_OK, + ReadFileResult::INVALID_PRESET) + .Bind("start"_s, &TestPreset::IncludeOptions::IndexOptions::Start, + PresetOptionalIntHelper, false) + .Bind("end"_s, &TestPreset::IncludeOptions::IndexOptions::End, + PresetOptionalIntHelper, false) + .Bind("stride"_s, &TestPreset::IncludeOptions::IndexOptions::Stride, + PresetOptionalIntHelper, false) + .Bind("specificTests"_s, + &TestPreset::IncludeOptions::IndexOptions::SpecificTests, + PresetVectorIntHelper, false)); + +ReadFileResult TestPresetOptionalFilterIncludeIndexHelper( + cm::optional<TestPreset::IncludeOptions::IndexOptions>& out, + const Json::Value* value) +{ + if (!value) { + out = cm::nullopt; + return ReadFileResult::READ_OK; + } + + if (value->isString()) { + out.emplace(); + out->IndexFile = value->asString(); + return ReadFileResult::READ_OK; + } + + if (value->isObject()) { + return TestPresetOptionalFilterIncludeIndexObjectHelper(out, value); + } + + return ReadFileResult::INVALID_PRESET; +} + +auto const TestPresetOptionalFilterIncludeHelper = + cmJSONOptionalHelper<TestPreset::IncludeOptions, ReadFileResult>( + ReadFileResult::READ_OK, + cmJSONObjectHelper<TestPreset::IncludeOptions, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET) + .Bind("name"_s, &TestPreset::IncludeOptions::Name, PresetStringHelper, + false) + .Bind("label"_s, &TestPreset::IncludeOptions::Label, PresetStringHelper, + false) + .Bind("index"_s, &TestPreset::IncludeOptions::Index, + TestPresetOptionalFilterIncludeIndexHelper, false) + .Bind("useUnion"_s, &TestPreset::IncludeOptions::UseUnion, + PresetOptionalBoolHelper, false)); + +auto const TestPresetOptionalFilterExcludeFixturesHelper = + cmJSONOptionalHelper<TestPreset::ExcludeOptions::FixturesOptions, + ReadFileResult>( + ReadFileResult::READ_OK, + cmJSONObjectHelper<TestPreset::ExcludeOptions::FixturesOptions, + ReadFileResult>(ReadFileResult::READ_OK, + ReadFileResult::INVALID_PRESET) + .Bind("any"_s, &TestPreset::ExcludeOptions::FixturesOptions::Any, + PresetStringHelper, false) + .Bind("setup"_s, &TestPreset::ExcludeOptions::FixturesOptions::Setup, + PresetStringHelper, false) + .Bind("cleanup"_s, &TestPreset::ExcludeOptions::FixturesOptions::Cleanup, + PresetStringHelper, false)); + +auto const TestPresetOptionalFilterExcludeHelper = + cmJSONOptionalHelper<TestPreset::ExcludeOptions, ReadFileResult>( + ReadFileResult::READ_OK, + cmJSONObjectHelper<TestPreset::ExcludeOptions, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET) + .Bind("name"_s, &TestPreset::ExcludeOptions::Name, PresetStringHelper, + false) + .Bind("label"_s, &TestPreset::ExcludeOptions::Label, PresetStringHelper, + false) + .Bind("fixtures"_s, &TestPreset::ExcludeOptions::Fixtures, + TestPresetOptionalFilterExcludeFixturesHelper, false)); + +ReadFileResult TestPresetExecutionShowOnlyHelper( + TestPreset::ExecutionOptions::ShowOnlyEnum& out, const Json::Value* value) +{ + if (!value || !value->isString()) { + return ReadFileResult::INVALID_PRESET; + } + + if (value->asString() == "human") { + out = TestPreset::ExecutionOptions::ShowOnlyEnum::Human; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "json-v1") { + out = TestPreset::ExecutionOptions::ShowOnlyEnum::JsonV1; + return ReadFileResult::READ_OK; + } + + return ReadFileResult::INVALID_PRESET; +} + +auto const TestPresetOptionalExecutionShowOnlyHelper = + cmJSONOptionalHelper<TestPreset::ExecutionOptions::ShowOnlyEnum, + ReadFileResult>(ReadFileResult::READ_OK, + TestPresetExecutionShowOnlyHelper); + +ReadFileResult TestPresetExecutionModeHelper( + TestPreset::ExecutionOptions::RepeatOptions::ModeEnum& out, + const Json::Value* value) +{ + if (!value) { + return ReadFileResult::READ_OK; + } + + if (!value->isString()) { + return ReadFileResult::INVALID_PRESET; + } + + if (value->asString() == "until-fail") { + out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::UntilFail; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "until-pass") { + out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::UntilPass; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "after-timeout") { + out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::AfterTimeout; + return ReadFileResult::READ_OK; + } + + return ReadFileResult::INVALID_PRESET; +} + +auto const TestPresetOptionalExecutionRepeatHelper = + cmJSONOptionalHelper<TestPreset::ExecutionOptions::RepeatOptions, + ReadFileResult>( + ReadFileResult::READ_OK, + cmJSONObjectHelper<TestPreset::ExecutionOptions::RepeatOptions, + ReadFileResult>(ReadFileResult::READ_OK, + ReadFileResult::INVALID_PRESET) + .Bind("mode"_s, &TestPreset::ExecutionOptions::RepeatOptions::Mode, + TestPresetExecutionModeHelper, true) + .Bind("count"_s, &TestPreset::ExecutionOptions::RepeatOptions::Count, + PresetIntHelper, true)); + +ReadFileResult TestPresetExecutionNoTestsActionHelper( + TestPreset::ExecutionOptions::NoTestsActionEnum& out, + const Json::Value* value) +{ + if (!value) { + out = TestPreset::ExecutionOptions::NoTestsActionEnum::Default; + return ReadFileResult::READ_OK; + } + + if (!value->isString()) { + return ReadFileResult::INVALID_PRESET; + } + + if (value->asString() == "default") { + out = TestPreset::ExecutionOptions::NoTestsActionEnum::Default; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "error") { + out = TestPreset::ExecutionOptions::NoTestsActionEnum::Error; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "ignore") { + out = TestPreset::ExecutionOptions::NoTestsActionEnum::Ignore; + return ReadFileResult::READ_OK; + } + + return ReadFileResult::INVALID_PRESET; +} + +auto const TestPresetOptionalExecutionNoTestsActionHelper = + cmJSONOptionalHelper<TestPreset::ExecutionOptions::NoTestsActionEnum, + ReadFileResult>(ReadFileResult::READ_OK, + TestPresetExecutionNoTestsActionHelper); + +auto const TestPresetExecutionHelper = + cmJSONOptionalHelper<TestPreset::ExecutionOptions, ReadFileResult>( + ReadFileResult::READ_OK, + cmJSONObjectHelper<TestPreset::ExecutionOptions, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET) + .Bind("stopOnFailure"_s, &TestPreset::ExecutionOptions::StopOnFailure, + PresetOptionalBoolHelper, false) + .Bind("enableFailover"_s, &TestPreset::ExecutionOptions::EnableFailover, + PresetOptionalBoolHelper, false) + .Bind("jobs"_s, &TestPreset::ExecutionOptions::Jobs, + PresetOptionalIntHelper, false) + .Bind("resourceSpecFile"_s, + &TestPreset::ExecutionOptions::ResourceSpecFile, + PresetStringHelper, false) + .Bind("testLoad"_s, &TestPreset::ExecutionOptions::TestLoad, + PresetOptionalIntHelper, false) + .Bind("showOnly"_s, &TestPreset::ExecutionOptions::ShowOnly, + TestPresetOptionalExecutionShowOnlyHelper, false) + .Bind("rerunFailed"_s, &TestPreset::ExecutionOptions::RerunFailed, + PresetOptionalBoolHelper, false) + .Bind("repeat"_s, &TestPreset::ExecutionOptions::Repeat, + TestPresetOptionalExecutionRepeatHelper, false) + .Bind("interactiveDebugging"_s, + &TestPreset::ExecutionOptions::InteractiveDebugging, + PresetOptionalBoolHelper, false) + .Bind("scheduleRandom"_s, &TestPreset::ExecutionOptions::ScheduleRandom, + PresetOptionalBoolHelper, false) + .Bind("timeout"_s, &TestPreset::ExecutionOptions::Timeout, + PresetOptionalIntHelper, false) + .Bind("noTestsAction"_s, &TestPreset::ExecutionOptions::NoTestsAction, + TestPresetOptionalExecutionNoTestsActionHelper, false)); + +auto const TestPresetFilterHelper = + cmJSONOptionalHelper<TestPreset::FilterOptions, ReadFileResult>( + ReadFileResult::READ_OK, + cmJSONObjectHelper<TestPreset::FilterOptions, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET) + .Bind("include"_s, &TestPreset::FilterOptions::Include, + TestPresetOptionalFilterIncludeHelper, false) + .Bind("exclude"_s, &TestPreset::FilterOptions::Exclude, + TestPresetOptionalFilterExcludeHelper, false)); + +auto const TestPresetHelper = + cmJSONObjectHelper<TestPreset, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) + .Bind("name"_s, &TestPreset::Name, PresetStringHelper) + .Bind("inherits"_s, &TestPreset::Inherits, PresetInheritsHelper, false) + .Bind("hidden"_s, &TestPreset::Hidden, PresetBoolHelper, false) + .Bind<std::nullptr_t>("vendor"_s, nullptr, + VendorHelper(ReadFileResult::INVALID_PRESET), false) + .Bind("displayName"_s, &TestPreset::DisplayName, PresetStringHelper, false) + .Bind("description"_s, &TestPreset::Description, PresetStringHelper, false) + .Bind("environment"_s, &TestPreset::Environment, EnvironmentMapHelper, + false) + .Bind("configurePreset"_s, &TestPreset::ConfigurePreset, + PresetStringHelper, false) + .Bind("inheritConfigureEnvironment"_s, + &TestPreset::InheritConfigureEnvironment, PresetOptionalBoolHelper, + false) + .Bind("configuration"_s, &TestPreset::Configuration, PresetStringHelper, + false) + .Bind("overwriteConfigurationFile"_s, + &TestPreset::OverwriteConfigurationFile, PresetVectorStringHelper, + false) + .Bind("output"_s, &TestPreset::Output, TestPresetOptionalOutputHelper, + false) + .Bind("filter"_s, &TestPreset::Filter, TestPresetFilterHelper, false) + .Bind("execution"_s, &TestPreset::Execution, TestPresetExecutionHelper, + false); + +auto const ConfigurePresetsHelper = + cmJSONVectorHelper<ConfigurePreset, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS, + ConfigurePresetHelper); + +auto const BuildPresetsHelper = + cmJSONVectorHelper<BuildPreset, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS, + BuildPresetHelper); + +auto const TestPresetsHelper = cmJSONVectorHelper<TestPreset, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS, TestPresetHelper); auto const CMakeVersionUIntHelper = cmJSONUIntHelper<ReadFileResult>( ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION); @@ -322,7 +710,11 @@ auto const RootPresetsHelper = cmJSONObjectHelper<RootPresets, ReadFileResult>( ReadFileResult::READ_OK, ReadFileResult::INVALID_ROOT, false) .Bind<int>("version"_s, nullptr, VersionHelper) - .Bind("configurePresets"_s, &RootPresets::Presets, PresetsHelper, false) + .Bind("configurePresets"_s, &RootPresets::ConfigurePresets, + ConfigurePresetsHelper, false) + .Bind("buildPresets"_s, &RootPresets::BuildPresets, BuildPresetsHelper, + false) + .Bind("testPresets"_s, &RootPresets::TestPresets, TestPresetsHelper, false) .Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired, CMakeVersionHelper, false) .Bind<std::nullptr_t>("vendor"_s, nullptr, @@ -335,23 +727,33 @@ void InheritString(std::string& child, const std::string& parent) } } -void InheritOptionalBool(cm::optional<bool>& child, - const cm::optional<bool>& parent) +template <typename T> +void InheritOptionalValue(cm::optional<T>& child, + const cm::optional<T>& parent) { if (!child) { child = parent; } } +template <typename T> +void InheritVector(std::vector<T>& child, const std::vector<T>& parent) +{ + if (child.empty()) { + child = parent; + } +} + /** * Check preset inheritance for cycles (using a DAG check algorithm) while * also bubbling up fields through the inheritance hierarchy, then verify * that each preset has the required fields, either directly or through * inheritance. */ +template <class T> ReadFileResult VisitPreset( - std::map<std::string, cmCMakePresetsFile::PresetPair>& presets, - UnexpandedPreset& preset, std::map<std::string, CycleStatus> cycleStatus) + T& preset, std::map<std::string, cmCMakePresetsFile::PresetPair<T>>& presets, + std::map<std::string, CycleStatus> cycleStatus) { switch (cycleStatus[preset.Name]) { case CycleStatus::InProgress: @@ -364,80 +766,44 @@ ReadFileResult VisitPreset( cycleStatus[preset.Name] = CycleStatus::InProgress; - if (preset.CacheVariables.count("") != 0) { - return ReadFileResult::INVALID_PRESET; - } if (preset.Environment.count("") != 0) { return ReadFileResult::INVALID_PRESET; } + CHECK_OK(preset.VisitPresetBeforeInherit()) + for (auto const& i : preset.Inherits) { auto parent = presets.find(i); if (parent == presets.end()) { return ReadFileResult::INVALID_PRESET; } - if (!preset.User && parent->second.Unexpanded.User) { + auto& parentPreset = parent->second.Unexpanded; + if (!preset.User && parentPreset.User) { return ReadFileResult::USER_PRESET_INHERITANCE; } - auto result = VisitPreset(presets, parent->second.Unexpanded, cycleStatus); + auto result = VisitPreset(parentPreset, presets, cycleStatus); if (result != ReadFileResult::READ_OK) { return result; } - InheritString(preset.Generator, parent->second.Unexpanded.Generator); - InheritString(preset.Architecture, parent->second.Unexpanded.Architecture); - InheritString(preset.Toolset, parent->second.Unexpanded.Toolset); - if (!preset.ArchitectureStrategy) { - preset.ArchitectureStrategy = - parent->second.Unexpanded.ArchitectureStrategy; - } - if (!preset.ToolsetStrategy) { - preset.ToolsetStrategy = parent->second.Unexpanded.ToolsetStrategy; - } - InheritString(preset.BinaryDir, parent->second.Unexpanded.BinaryDir); - InheritOptionalBool(preset.WarnDev, parent->second.Unexpanded.WarnDev); - InheritOptionalBool(preset.ErrorDev, parent->second.Unexpanded.ErrorDev); - InheritOptionalBool(preset.WarnDeprecated, - parent->second.Unexpanded.WarnDeprecated); - InheritOptionalBool(preset.ErrorDeprecated, - parent->second.Unexpanded.ErrorDeprecated); - InheritOptionalBool(preset.WarnUninitialized, - parent->second.Unexpanded.WarnUninitialized); - InheritOptionalBool(preset.WarnUnusedCli, - parent->second.Unexpanded.WarnUnusedCli); - InheritOptionalBool(preset.WarnSystemVars, - parent->second.Unexpanded.WarnSystemVars); - for (auto const& v : parent->second.Unexpanded.CacheVariables) { - preset.CacheVariables.insert(v); - } - for (auto const& v : parent->second.Unexpanded.Environment) { + CHECK_OK(preset.VisitPresetInherit(parentPreset)) + + for (auto const& v : parentPreset.Environment) { preset.Environment.insert(v); } } - if (!preset.Hidden) { - if (preset.Generator.empty()) { - return ReadFileResult::INVALID_PRESET; - } - if (preset.BinaryDir.empty()) { - return ReadFileResult::INVALID_PRESET; - } - if (preset.WarnDev == false && preset.ErrorDev == true) { - return ReadFileResult::INVALID_PRESET; - } - if (preset.WarnDeprecated == false && preset.ErrorDeprecated == true) { - return ReadFileResult::INVALID_PRESET; - } - } + CHECK_OK(preset.VisitPresetAfterInherit()) cycleStatus[preset.Name] = CycleStatus::Verified; return ReadFileResult::READ_OK; } +template <class T> ReadFileResult ComputePresetInheritance( - std::map<std::string, cmCMakePresetsFile::PresetPair>& presets) + std::map<std::string, cmCMakePresetsFile::PresetPair<T>>& presets) { std::map<std::string, CycleStatus> cycleStatus; for (auto const& it : presets) { @@ -445,7 +811,7 @@ ReadFileResult ComputePresetInheritance( } for (auto& it : presets) { - auto result = VisitPreset(presets, it.second.Unexpanded, cycleStatus); + auto result = VisitPreset<T>(it.second.Unexpanded, presets, cycleStatus); if (result != ReadFileResult::READ_OK) { return result; } @@ -482,65 +848,186 @@ enum class ExpandMacroResult Error, }; -ExpandMacroResult VisitEnv(const cmCMakePresetsFile& file, - cmCMakePresetsFile::ExpandedPreset& preset, - std::map<std::string, CycleStatus>& envCycles, - std::string& value, CycleStatus& status); -ExpandMacroResult ExpandMacros(const cmCMakePresetsFile& file, - cmCMakePresetsFile::ExpandedPreset& preset, - std::map<std::string, CycleStatus>& envCycles, - std::string& out); -ExpandMacroResult ExpandMacro(const cmCMakePresetsFile& file, - cmCMakePresetsFile::ExpandedPreset& preset, - std::map<std::string, CycleStatus>& envCycles, - std::string& out, - const std::string& macroNamespace, - const std::string& macroName); +using MacroExpander = std::function<ExpandMacroResult( + const std::string&, const std::string&, std::string&)>; + +ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status, + const std::vector<MacroExpander>& macroExpanders); +ExpandMacroResult ExpandMacros( + std::string& out, const std::vector<MacroExpander>& macroExpanders); +ExpandMacroResult ExpandMacro( + std::string& out, const std::string& macroNamespace, + const std::string& macroName, + const std::vector<MacroExpander>& macroExpanders); bool ExpandMacros(const cmCMakePresetsFile& file, - const UnexpandedPreset& preset, - cm::optional<ExpandedPreset>& out) + const ConfigurePreset& preset, + cm::optional<ConfigurePreset>& out, + const std::vector<MacroExpander>& macroExpanders) { - out = preset; + std::string binaryDir = preset.BinaryDir; + CHECK_EXPAND(out, binaryDir, macroExpanders) - std::map<std::string, CycleStatus> envCycles; - for (auto const& v : out->Environment) { - envCycles[v.first] = CycleStatus::Unvisited; + if (!cmSystemTools::FileIsFullPath(binaryDir)) { + binaryDir = cmStrCat(file.SourceDir, '/', binaryDir); } + out->BinaryDir = cmSystemTools::CollapseFullPath(binaryDir); + cmSystemTools::ConvertToUnixSlashes(out->BinaryDir); - for (auto& v : out->Environment) { - if (v.second) { - switch (VisitEnv(file, *out, envCycles, *v.second, envCycles[v.first])) { - case ExpandMacroResult::Error: - return false; - case ExpandMacroResult::Ignore: - out.reset(); - return true; - case ExpandMacroResult::Ok: - break; + for (auto& variable : out->CacheVariables) { + if (variable.second) { + CHECK_EXPAND(out, variable.second->Value, macroExpanders) + } + } + + return true; +} + +bool ExpandMacros(const cmCMakePresetsFile&, const BuildPreset&, + cm::optional<BuildPreset>& out, + const std::vector<MacroExpander>& macroExpanders) +{ + for (auto& target : out->Targets) { + CHECK_EXPAND(out, target, macroExpanders) + } + + for (auto& nativeToolOption : out->NativeToolOptions) { + CHECK_EXPAND(out, nativeToolOption, macroExpanders) + } + + return true; +} + +bool ExpandMacros(const cmCMakePresetsFile&, const TestPreset&, + cm::optional<TestPreset>& out, + const std::vector<MacroExpander>& macroExpanders) +{ + for (auto& overwrite : out->OverwriteConfigurationFile) { + CHECK_EXPAND(out, overwrite, macroExpanders); + } + + if (out->Output) { + CHECK_EXPAND(out, out->Output->OutputLogFile, macroExpanders) + } + + if (out->Filter) { + if (out->Filter->Include) { + CHECK_EXPAND(out, out->Filter->Include->Name, macroExpanders) + CHECK_EXPAND(out, out->Filter->Include->Label, macroExpanders) + + if (out->Filter->Include->Index) { + CHECK_EXPAND(out, out->Filter->Include->Index->IndexFile, + macroExpanders); + } + } + + if (out->Filter->Exclude) { + CHECK_EXPAND(out, out->Filter->Exclude->Name, macroExpanders) + CHECK_EXPAND(out, out->Filter->Exclude->Label, macroExpanders) + + if (out->Filter->Exclude->Fixtures) { + CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Any, macroExpanders) + CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Setup, + macroExpanders) + CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Cleanup, + macroExpanders) } } } - std::string binaryDir = preset.BinaryDir; - switch (ExpandMacros(file, *out, envCycles, binaryDir)) { - case ExpandMacroResult::Error: - return false; - case ExpandMacroResult::Ignore: - out.reset(); - return true; - case ExpandMacroResult::Ok: - break; + if (out->Execution) { + CHECK_EXPAND(out, out->Execution->ResourceSpecFile, macroExpanders) } - if (!cmSystemTools::FileIsFullPath(binaryDir)) { - binaryDir = cmStrCat(file.SourceDir, '/', binaryDir); + + return true; +} + +template <class T> +bool ExpandMacros(const cmCMakePresetsFile& file, const T& preset, + cm::optional<T>& out) +{ + out.emplace(preset); + + std::map<std::string, CycleStatus> envCycles; + for (auto const& v : out->Environment) { + envCycles[v.first] = CycleStatus::Unvisited; } - out->BinaryDir = cmSystemTools::CollapseFullPath(binaryDir); - cmSystemTools::ConvertToUnixSlashes(out->BinaryDir); - for (auto& variable : out->CacheVariables) { - if (variable.second) { - switch (ExpandMacros(file, *out, envCycles, variable.second->Value)) { + std::vector<MacroExpander> macroExpanders; + + MacroExpander defaultMacroExpander = + [&file, &preset](const std::string& macroNamespace, + const std::string& macroName, + std::string& macroOut) -> ExpandMacroResult { + if (macroNamespace.empty()) { + if (macroName == "sourceDir") { + macroOut += file.SourceDir; + return ExpandMacroResult::Ok; + } + if (macroName == "sourceParentDir") { + macroOut += cmSystemTools::GetParentDirectory(file.SourceDir); + return ExpandMacroResult::Ok; + } + if (macroName == "sourceDirName") { + macroOut += cmSystemTools::GetFilenameName(file.SourceDir); + return ExpandMacroResult::Ok; + } + if (macroName == "presetName") { + macroOut += preset.Name; + return ExpandMacroResult::Ok; + } + if (macroName == "generator") { + // Generator only makes sense if preset is not hidden. + if (!preset.Hidden) { + macroOut += file.GetGeneratorForPreset(preset.Name); + } + return ExpandMacroResult::Ok; + } + if (macroName == "dollar") { + macroOut += '$'; + return ExpandMacroResult::Ok; + } + } + + return ExpandMacroResult::Ignore; + }; + + MacroExpander environmentMacroExpander = + [¯oExpanders, &out, &envCycles]( + const std::string& macroNamespace, const std::string& macroName, + std::string& result) -> ExpandMacroResult { + if (macroNamespace == "env" && !macroName.empty() && out) { + auto v = out->Environment.find(macroName); + if (v != out->Environment.end() && v->second) { + auto e = VisitEnv(*v->second, envCycles[macroName], macroExpanders); + if (e != ExpandMacroResult::Ok) { + return e; + } + result += *v->second; + return ExpandMacroResult::Ok; + } + } + + if (macroNamespace == "env" || macroNamespace == "penv") { + if (macroName.empty()) { + return ExpandMacroResult::Error; + } + const char* value = std::getenv(macroName.c_str()); + if (value) { + result += value; + } + return ExpandMacroResult::Ok; + } + + return ExpandMacroResult::Ignore; + }; + + macroExpanders.push_back(defaultMacroExpander); + macroExpanders.push_back(environmentMacroExpander); + + for (auto& v : out->Environment) { + if (v.second) { + switch (VisitEnv(*v.second, envCycles[v.first], macroExpanders)) { case ExpandMacroResult::Error: return false; case ExpandMacroResult::Ignore: @@ -552,13 +1039,11 @@ bool ExpandMacros(const cmCMakePresetsFile& file, } } - return true; + return ExpandMacros(file, preset, out, macroExpanders); } -ExpandMacroResult VisitEnv(const cmCMakePresetsFile& file, - cmCMakePresetsFile::ExpandedPreset& preset, - std::map<std::string, CycleStatus>& envCycles, - std::string& value, CycleStatus& status) +ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status, + const std::vector<MacroExpander>& macroExpanders) { if (status == CycleStatus::Verified) { return ExpandMacroResult::Ok; @@ -568,7 +1053,7 @@ ExpandMacroResult VisitEnv(const cmCMakePresetsFile& file, } status = CycleStatus::InProgress; - auto e = ExpandMacros(file, preset, envCycles, value); + auto e = ExpandMacros(value, macroExpanders); if (e != ExpandMacroResult::Ok) { return e; } @@ -576,10 +1061,8 @@ ExpandMacroResult VisitEnv(const cmCMakePresetsFile& file, return ExpandMacroResult::Ok; } -ExpandMacroResult ExpandMacros(const cmCMakePresetsFile& file, - cmCMakePresetsFile::ExpandedPreset& preset, - std::map<std::string, CycleStatus>& envCycles, - std::string& out) +ExpandMacroResult ExpandMacros( + std::string& out, const std::vector<MacroExpander>& macroExpanders) { std::string result; std::string macroNamespace; @@ -626,8 +1109,8 @@ ExpandMacroResult ExpandMacros(const cmCMakePresetsFile& file, case State::MacroName: if (c == '}') { - auto e = ExpandMacro(file, preset, envCycles, result, macroNamespace, - macroName); + auto e = + ExpandMacro(result, macroNamespace, macroName, macroExpanders); if (e != ExpandMacroResult::Ok) { return e; } @@ -656,70 +1139,232 @@ ExpandMacroResult ExpandMacros(const cmCMakePresetsFile& file, return ExpandMacroResult::Ok; } -ExpandMacroResult ExpandMacro(const cmCMakePresetsFile& file, - cmCMakePresetsFile::ExpandedPreset& preset, - std::map<std::string, CycleStatus>& envCycles, - std::string& out, +ExpandMacroResult ExpandMacro(std::string& out, const std::string& macroNamespace, - const std::string& macroName) + const std::string& macroName, + const std::vector<MacroExpander>& macroExpanders) { - if (macroNamespace.empty()) { - if (macroName == "sourceDir") { - out += file.SourceDir; - return ExpandMacroResult::Ok; + for (auto const& macroExpander : macroExpanders) { + auto result = macroExpander(macroNamespace, macroName, out); + if (result != ExpandMacroResult::Ignore) { + return result; } - if (macroName == "sourceParentDir") { - out += cmSystemTools::GetParentDirectory(file.SourceDir); - return ExpandMacroResult::Ok; + } + + if (macroNamespace == "vendor") { + return ExpandMacroResult::Ignore; + } + + return ExpandMacroResult::Error; +} +} + +cmCMakePresetsFile::ReadFileResult +cmCMakePresetsFile::ConfigurePreset::VisitPresetInherit( + const cmCMakePresetsFile::Preset& parentPreset) +{ + auto& preset = *this; + const ConfigurePreset& parent = + static_cast<const ConfigurePreset&>(parentPreset); + InheritString(preset.Generator, parent.Generator); + InheritString(preset.Architecture, parent.Architecture); + InheritString(preset.Toolset, parent.Toolset); + if (!preset.ArchitectureStrategy) { + preset.ArchitectureStrategy = parent.ArchitectureStrategy; + } + if (!preset.ToolsetStrategy) { + preset.ToolsetStrategy = parent.ToolsetStrategy; + } + InheritString(preset.BinaryDir, parent.BinaryDir); + InheritOptionalValue(preset.WarnDev, parent.WarnDev); + InheritOptionalValue(preset.ErrorDev, parent.ErrorDev); + InheritOptionalValue(preset.WarnDeprecated, parent.WarnDeprecated); + InheritOptionalValue(preset.ErrorDeprecated, parent.ErrorDeprecated); + InheritOptionalValue(preset.WarnUninitialized, parent.WarnUninitialized); + InheritOptionalValue(preset.WarnUnusedCli, parent.WarnUnusedCli); + InheritOptionalValue(preset.WarnSystemVars, parent.WarnSystemVars); + + for (auto const& v : parent.CacheVariables) { + preset.CacheVariables.insert(v); + } + + return ReadFileResult::READ_OK; +} + +cmCMakePresetsFile::ReadFileResult +cmCMakePresetsFile::ConfigurePreset::VisitPresetBeforeInherit() +{ + auto& preset = *this; + if (preset.Environment.count("") != 0) { + return ReadFileResult::INVALID_PRESET; + } + + return ReadFileResult::READ_OK; +} + +cmCMakePresetsFile::ReadFileResult +cmCMakePresetsFile::ConfigurePreset::VisitPresetAfterInherit() +{ + auto& preset = *this; + if (!preset.Hidden) { + if (preset.Generator.empty()) { + return ReadFileResult::INVALID_PRESET; } - if (macroName == "sourceDirName") { - out += cmSystemTools::GetFilenameName(file.SourceDir); - return ExpandMacroResult::Ok; + if (preset.BinaryDir.empty()) { + return ReadFileResult::INVALID_PRESET; } - if (macroName == "presetName") { - out += preset.Name; - return ExpandMacroResult::Ok; + if (preset.WarnDev == false && preset.ErrorDev == true) { + return ReadFileResult::INVALID_PRESET; } - if (macroName == "generator") { - out += preset.Generator; - return ExpandMacroResult::Ok; + if (preset.WarnDeprecated == false && preset.ErrorDeprecated == true) { + return ReadFileResult::INVALID_PRESET; } - if (macroName == "dollar") { - out += '$'; - return ExpandMacroResult::Ok; + if (preset.CacheVariables.count("") != 0) { + return ReadFileResult::INVALID_PRESET; } } - if (macroNamespace == "env" && !macroName.empty()) { - auto v = preset.Environment.find(macroName); - if (v != preset.Environment.end() && v->second) { - auto e = - VisitEnv(file, preset, envCycles, *v->second, envCycles[macroName]); - if (e != ExpandMacroResult::Ok) { - return e; - } - out += *v->second; - return ExpandMacroResult::Ok; + return ReadFileResult::READ_OK; +} + +cmCMakePresetsFile::ReadFileResult +cmCMakePresetsFile::BuildPreset::VisitPresetInherit( + const cmCMakePresetsFile::Preset& parentPreset) +{ + auto& preset = *this; + const BuildPreset& parent = static_cast<const BuildPreset&>(parentPreset); + + InheritString(preset.ConfigurePreset, parent.ConfigurePreset); + InheritOptionalValue(preset.InheritConfigureEnvironment, + parent.InheritConfigureEnvironment); + InheritOptionalValue(preset.Jobs, parent.Jobs); + InheritVector(preset.Targets, parent.Targets); + InheritString(preset.Configuration, parent.Configuration); + InheritOptionalValue(preset.CleanFirst, parent.CleanFirst); + InheritOptionalValue(preset.Verbose, parent.Verbose); + InheritVector(preset.NativeToolOptions, parent.NativeToolOptions); + + return ReadFileResult::READ_OK; +} + +cmCMakePresetsFile::ReadFileResult +cmCMakePresetsFile::BuildPreset::VisitPresetAfterInherit() +{ + auto& preset = *this; + if (!preset.Hidden && preset.ConfigurePreset.empty()) { + return ReadFileResult::INVALID_PRESET; + } + return ReadFileResult::READ_OK; +} + +cmCMakePresetsFile::ReadFileResult +cmCMakePresetsFile::TestPreset::VisitPresetInherit( + const cmCMakePresetsFile::Preset& parentPreset) +{ + auto& preset = *this; + const TestPreset& parent = static_cast<const TestPreset&>(parentPreset); + + InheritString(preset.ConfigurePreset, parent.ConfigurePreset); + InheritOptionalValue(preset.InheritConfigureEnvironment, + parent.InheritConfigureEnvironment); + InheritString(preset.Configuration, parent.Configuration); + InheritVector(preset.OverwriteConfigurationFile, + parent.OverwriteConfigurationFile); + + if (parent.Output) { + if (preset.Output) { + auto& output = preset.Output.value(); + const auto& parentOutput = parent.Output.value(); + InheritOptionalValue(output.ShortProgress, parentOutput.ShortProgress); + InheritOptionalValue(output.Verbosity, parentOutput.Verbosity); + InheritOptionalValue(output.Debug, parentOutput.Debug); + InheritOptionalValue(output.OutputOnFailure, + parentOutput.OutputOnFailure); + InheritString(output.OutputLogFile, parentOutput.OutputLogFile); + InheritOptionalValue(output.LabelSummary, parentOutput.LabelSummary); + InheritOptionalValue(output.SubprojectSummary, + parentOutput.SubprojectSummary); + InheritOptionalValue(output.MaxPassedTestOutputSize, + parentOutput.MaxPassedTestOutputSize); + InheritOptionalValue(output.MaxFailedTestOutputSize, + parentOutput.MaxFailedTestOutputSize); + InheritOptionalValue(output.MaxTestNameWidth, + parentOutput.MaxTestNameWidth); + } else { + preset.Output = parent.Output; } } - if (macroNamespace == "env" || macroNamespace == "penv") { - if (macroName.empty()) { - return ExpandMacroResult::Error; + if (parent.Filter) { + if (parent.Filter->Include) { + if (preset.Filter && preset.Filter->Include) { + auto& include = *preset.Filter->Include; + const auto& parentInclude = *parent.Filter->Include; + InheritString(include.Name, parentInclude.Name); + InheritString(include.Label, parentInclude.Label); + InheritOptionalValue(include.Index, parentInclude.Index); + } else { + if (!preset.Filter) { + preset.Filter.emplace(); + } + preset.Filter->Include = parent.Filter->Include; + } } - const char* value = std::getenv(macroName.c_str()); - if (value) { - out += value; + + if (parent.Filter->Exclude) { + if (preset.Filter && preset.Filter->Exclude) { + auto& exclude = *preset.Filter->Exclude; + const auto& parentExclude = *parent.Filter->Exclude; + InheritString(exclude.Name, parentExclude.Name); + InheritString(exclude.Label, parentExclude.Label); + InheritOptionalValue(exclude.Fixtures, parentExclude.Fixtures); + } else { + if (!preset.Filter) { + preset.Filter.emplace(); + } + preset.Filter->Exclude = parent.Filter->Exclude; + } } - return ExpandMacroResult::Ok; } - if (macroNamespace == "vendor") { - return ExpandMacroResult::Ignore; + if (parent.Execution) { + if (preset.Execution) { + auto& execution = *preset.Execution; + const auto& parentExecution = *parent.Execution; + InheritOptionalValue(execution.StopOnFailure, + parentExecution.StopOnFailure); + InheritOptionalValue(execution.EnableFailover, + parentExecution.EnableFailover); + InheritOptionalValue(execution.Jobs, parentExecution.Jobs); + InheritString(execution.ResourceSpecFile, + parentExecution.ResourceSpecFile); + InheritOptionalValue(execution.TestLoad, parentExecution.TestLoad); + InheritOptionalValue(execution.ShowOnly, parentExecution.ShowOnly); + InheritOptionalValue(execution.RerunFailed, parentExecution.RerunFailed); + InheritOptionalValue(execution.Repeat, parentExecution.Repeat); + InheritOptionalValue(execution.InteractiveDebugging, + parentExecution.InteractiveDebugging); + InheritOptionalValue(execution.ScheduleRandom, + parentExecution.ScheduleRandom); + InheritOptionalValue(execution.Timeout, parentExecution.Timeout); + InheritOptionalValue(execution.NoTestsAction, + parentExecution.NoTestsAction); + } else { + preset.Execution = parent.Execution; + } } - return ExpandMacroResult::Error; + return ReadFileResult::READ_OK; } + +cmCMakePresetsFile::ReadFileResult +cmCMakePresetsFile::TestPreset::VisitPresetAfterInherit() +{ + auto& preset = *this; + if (!preset.Hidden && preset.ConfigurePreset.empty()) { + return ReadFileResult::INVALID_PRESET; + } + return ReadFileResult::READ_OK; } std::string cmCMakePresetsFile::GetFilename(const std::string& sourceDir) @@ -735,17 +1380,25 @@ std::string cmCMakePresetsFile::GetUserFilename(const std::string& sourceDir) cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadProjectPresets( const std::string& sourceDir, bool allowNoFiles) { - bool haveOneFile = false; this->SourceDir = sourceDir; - this->Presets.clear(); - this->PresetOrder.clear(); + this->ClearPresets(); - std::vector<std::string> presetOrder; - std::map<std::string, PresetPair> presetMap; + auto result = this->ReadProjectPresetsInternal(allowNoFiles); + if (result != ReadFileResult::READ_OK) { + this->ClearPresets(); + } + + return result; +} + +cmCMakePresetsFile::ReadFileResult +cmCMakePresetsFile::ReadProjectPresetsInternal(bool allowNoFiles) +{ + bool haveOneFile = false; std::string filename = GetUserFilename(this->SourceDir); if (cmSystemTools::FileExists(filename)) { - auto result = this->ReadJSONFile(filename, presetOrder, presetMap, true); + auto result = this->ReadJSONFile(filename, true); if (result != ReadFileResult::READ_OK) { return result; } @@ -754,7 +1407,7 @@ cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadProjectPresets( filename = GetFilename(this->SourceDir); if (cmSystemTools::FileExists(filename)) { - auto result = this->ReadJSONFile(filename, presetOrder, presetMap, false); + auto result = this->ReadJSONFile(filename, false); if (result != ReadFileResult::READ_OK) { return result; } @@ -766,19 +1419,50 @@ cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadProjectPresets( : ReadFileResult::FILE_NOT_FOUND; } - auto result = ComputePresetInheritance(presetMap); - if (result != ReadFileResult::READ_OK) { - return result; + CHECK_OK(ComputePresetInheritance(this->ConfigurePresets)) + CHECK_OK(ComputePresetInheritance(this->BuildPresets)) + CHECK_OK(ComputePresetInheritance(this->TestPresets)) + + for (auto& it : this->ConfigurePresets) { + if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { + return ReadFileResult::INVALID_MACRO_EXPANSION; + } } - for (auto& it : presetMap) { + for (auto& it : this->BuildPresets) { + if (!it.second.Unexpanded.Hidden) { + const auto configurePreset = + this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset); + if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true) && + configurePreset != this->ConfigurePresets.end()) { + it.second.Unexpanded.Environment.insert( + configurePreset->second.Unexpanded.Environment.begin(), + configurePreset->second.Unexpanded.Environment.end()); + } + } + + if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { + return ReadFileResult::INVALID_MACRO_EXPANSION; + } + } + + for (auto& it : this->TestPresets) { + if (!it.second.Unexpanded.Hidden) { + const auto configurePreset = + this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset); + if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true) && + configurePreset != this->ConfigurePresets.end()) { + it.second.Unexpanded.Environment.insert( + configurePreset->second.Unexpanded.Environment.begin(), + configurePreset->second.Unexpanded.Environment.end()); + } + } + if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { return ReadFileResult::INVALID_MACRO_EXPANSION; } } - this->PresetOrder = std::move(presetOrder); - this->Presets = std::move(presetMap); return ReadFileResult::READ_OK; } @@ -817,14 +1501,16 @@ const char* cmCMakePresetsFile::ResultToString(ReadFileResult result) return "Project preset inherits from user preset"; case ReadFileResult::INVALID_MACRO_EXPANSION: return "Invalid macro expansion"; + case ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED: + return "File version must be 2 or higher for build and test preset " + "support."; } return "Unknown error"; } cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile( - const std::string& filename, std::vector<std::string>& presetOrder, - std::map<std::string, PresetPair>& presetMap, bool user) + const std::string& filename, bool user) { cmsys::ifstream fin(filename.c_str()); if (!fin) { @@ -848,6 +1534,12 @@ cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile( return ReadFileResult::UNRECOGNIZED_VERSION; } + // Support for build and test presets added in version 2. + if (v < 2 && + (root.isMember("buildPresets") || root.isMember("testPresets"))) { + return ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED; + } + RootPresets presets; if ((result = RootPresetsHelper(presets, &root)) != ReadFileResult::READ_OK) { @@ -866,16 +1558,158 @@ cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile( return ReadFileResult::UNRECOGNIZED_CMAKE_VERSION; } - for (auto& preset : presets.Presets) { + for (auto& preset : presets.ConfigurePresets) { preset.User = user; if (preset.Name.empty()) { return ReadFileResult::INVALID_PRESET; } - if (!presetMap.insert({ preset.Name, { preset, cm::nullopt } }).second) { + + PresetPair<ConfigurePreset> presetPair; + presetPair.Unexpanded = preset; + presetPair.Expanded = cm::nullopt; + if (!this->ConfigurePresets + .emplace(std::make_pair(preset.Name, presetPair)) + .second) { return ReadFileResult::DUPLICATE_PRESETS; } - presetOrder.push_back(preset.Name); + this->ConfigurePresetOrder.push_back(preset.Name); + } + + for (auto& preset : presets.BuildPresets) { + preset.User = user; + if (preset.Name.empty()) { + return ReadFileResult::INVALID_PRESET; + } + + PresetPair<BuildPreset> presetPair; + presetPair.Unexpanded = preset; + presetPair.Expanded = cm::nullopt; + if (!this->BuildPresets.emplace(preset.Name, presetPair).second) { + return ReadFileResult::DUPLICATE_PRESETS; + } + this->BuildPresetOrder.push_back(preset.Name); + } + + for (auto& preset : presets.TestPresets) { + preset.User = user; + if (preset.Name.empty()) { + return ReadFileResult::INVALID_PRESET; + } + + PresetPair<TestPreset> presetPair; + presetPair.Unexpanded = preset; + presetPair.Expanded = cm::nullopt; + if (!this->TestPresets.emplace(preset.Name, presetPair).second) { + return ReadFileResult::DUPLICATE_PRESETS; + } + this->TestPresetOrder.push_back(preset.Name); } return ReadFileResult::READ_OK; } + +void cmCMakePresetsFile::ClearPresets() +{ + this->ConfigurePresets.clear(); + this->BuildPresets.clear(); + this->TestPresets.clear(); + + this->ConfigurePresetOrder.clear(); + this->BuildPresetOrder.clear(); + this->TestPresetOrder.clear(); +} + +void cmCMakePresetsFile::PrintPresets( + const std::vector<const cmCMakePresetsFile::Preset*>& presets) +{ + if (presets.empty()) { + return; + } + + auto longestPresetName = + std::max_element(presets.begin(), presets.end(), + [](const cmCMakePresetsFile::Preset* a, + const cmCMakePresetsFile::Preset* b) { + return a->Name.length() < b->Name.length(); + }); + auto longestLength = (*longestPresetName)->Name.length(); + + for (const auto* preset : presets) { + std::cout << " \"" << preset->Name << '"'; + const auto& description = preset->DisplayName; + if (!description.empty()) { + for (std::size_t i = 0; i < longestLength - preset->Name.length(); ++i) { + std::cout << ' '; + } + std::cout << " - " << description; + } + std::cout << '\n'; + } +} + +void cmCMakePresetsFile::PrintConfigurePresetList() const +{ + PrintConfigurePresetList([](const ConfigurePreset&) { return true; }); +} + +void cmCMakePresetsFile::PrintConfigurePresetList( + const std::function<bool(const ConfigurePreset&)>& filter) const +{ + std::vector<const cmCMakePresetsFile::Preset*> presets; + for (auto const& p : this->ConfigurePresetOrder) { + auto const& preset = this->ConfigurePresets.at(p); + if (!preset.Unexpanded.Hidden && preset.Expanded && + filter(preset.Unexpanded)) { + presets.push_back( + static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded)); + } + } + + if (!presets.empty()) { + std::cout << "Available configure presets:\n\n"; + cmCMakePresetsFile::PrintPresets(presets); + } +} + +void cmCMakePresetsFile::PrintBuildPresetList() const +{ + std::vector<const cmCMakePresetsFile::Preset*> presets; + for (auto const& p : this->BuildPresetOrder) { + auto const& preset = this->BuildPresets.at(p); + if (!preset.Unexpanded.Hidden && preset.Expanded) { + presets.push_back( + static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded)); + } + } + + if (!presets.empty()) { + std::cout << "Available build presets:\n\n"; + cmCMakePresetsFile::PrintPresets(presets); + } +} + +void cmCMakePresetsFile::PrintTestPresetList() const +{ + std::vector<const cmCMakePresetsFile::Preset*> presets; + for (auto const& p : this->TestPresetOrder) { + auto const& preset = this->TestPresets.at(p); + if (!preset.Unexpanded.Hidden && preset.Expanded) { + presets.push_back( + static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded)); + } + } + + if (!presets.empty()) { + std::cout << "Available test presets:\n\n"; + cmCMakePresetsFile::PrintPresets(presets); + } +} + +void cmCMakePresetsFile::PrintAllPresets() const +{ + this->PrintConfigurePresetList(); + std::cout << std::endl; + this->PrintBuildPresetList(); + std::cout << std::endl; + this->PrintTestPresetList(); +} diff --git a/Source/cmCMakePresetsFile.h b/Source/cmCMakePresetsFile.h index f6b159a..26810ad 100644 --- a/Source/cmCMakePresetsFile.h +++ b/Source/cmCMakePresetsFile.h @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once +#include <functional> #include <map> #include <string> #include <utility> @@ -12,6 +13,27 @@ class cmCMakePresetsFile { public: + enum class ReadFileResult + { + READ_OK, + FILE_NOT_FOUND, + JSON_PARSE_ERROR, + INVALID_ROOT, + NO_VERSION, + INVALID_VERSION, + UNRECOGNIZED_VERSION, + INVALID_CMAKE_VERSION, + UNRECOGNIZED_CMAKE_VERSION, + INVALID_PRESETS, + INVALID_PRESET, + INVALID_VARIABLE, + DUPLICATE_PRESETS, + CYCLIC_PRESET_INHERITANCE, + USER_PRESET_INHERITANCE, + INVALID_MACRO_EXPANSION, + BUILD_TEST_PRESETS_UNSUPPORTED, + }; + enum class ArchToolsetStrategy { Set, @@ -29,25 +51,51 @@ public: { public: #if __cplusplus < 201703L && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L) - Preset() = default; - Preset(const Preset& /*other*/) = default; - Preset(Preset&& /*other*/) = default; - - Preset& operator=(const Preset& /*other*/) = default; - // The move assignment operators for several STL classes did not become // noexcept until C++17, which causes some tools to warn about this move // assignment operator throwing an exception when it shouldn't. Disable the // move assignment operator until C++17 is enabled. - Preset& operator=(Preset&& /*other*/) = delete; + // Explicitly defining a copy assignment operator prevents the compiler + // from automatically generating a move assignment operator. + Preset& operator=(const Preset& /*other*/) = default; #endif + virtual ~Preset() = default; + std::string Name; std::vector<std::string> Inherits; bool Hidden; bool User; std::string DisplayName; std::string Description; + + std::map<std::string, cm::optional<std::string>> Environment; + + virtual ReadFileResult VisitPresetInherit(const Preset& parent) = 0; + virtual ReadFileResult VisitPresetBeforeInherit() + { + return ReadFileResult::READ_OK; + } + + virtual ReadFileResult VisitPresetAfterInherit() + { + return ReadFileResult::READ_OK; + } + }; + + class ConfigurePreset : public Preset + { + public: +#if __cplusplus < 201703L && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L) + // The move assignment operators for several STL classes did not become + // noexcept until C++17, which causes some tools to warn about this move + // assignment operator throwing an exception when it shouldn't. Disable the + // move assignment operator until C++17 is enabled. + // Explicitly defining a copy assignment operator prevents the compiler + // from automatically generating a move assignment operator. + ConfigurePreset& operator=(const ConfigurePreset& /*other*/) = default; +#endif + std::string Generator; std::string Architecture; cm::optional<ArchToolsetStrategy> ArchitectureStrategy; @@ -56,7 +104,6 @@ public: std::string BinaryDir; std::map<std::string, cm::optional<CacheVariable>> CacheVariables; - std::map<std::string, cm::optional<std::string>> Environment; cm::optional<bool> WarnDev; cm::optional<bool> ErrorDev; @@ -69,70 +116,183 @@ public: cm::optional<bool> DebugOutput; cm::optional<bool> DebugTryCompile; cm::optional<bool> DebugFind; + + ReadFileResult VisitPresetInherit(const Preset& parent) override; + ReadFileResult VisitPresetBeforeInherit() override; + ReadFileResult VisitPresetAfterInherit() override; }; - class UnexpandedPreset : public Preset + class BuildPreset : public Preset { public: - using Preset::Preset; +#if __cplusplus < 201703L && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L) + // The move assignment operators for several STL classes did not become + // noexcept until C++17, which causes some tools to warn about this move + // assignment operator throwing an exception when it shouldn't. Disable the + // move assignment operator until C++17 is enabled. + // Explicitly defining a copy assignment operator prevents the compiler + // from automatically generating a move assignment operator. + BuildPreset& operator=(const BuildPreset& /*other*/) = default; +#endif - UnexpandedPreset() = default; - UnexpandedPreset(const Preset& preset) - : Preset(preset) - { - } - UnexpandedPreset(Preset&& preset) - : Preset(std::move(preset)) - { - } + std::string ConfigurePreset; + cm::optional<bool> InheritConfigureEnvironment; + cm::optional<int> Jobs; + std::vector<std::string> Targets; + std::string Configuration; + cm::optional<bool> CleanFirst; + cm::optional<bool> Verbose; + std::vector<std::string> NativeToolOptions; + + ReadFileResult VisitPresetInherit(const Preset& parent) override; + ReadFileResult VisitPresetAfterInherit() override; }; - class ExpandedPreset : public Preset + class TestPreset : public Preset { public: - using Preset::Preset; +#if __cplusplus < 201703L && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L) + // The move assignment operators for several STL classes did not become + // noexcept until C++17, which causes some tools to warn about this move + // assignment operator throwing an exception when it shouldn't. Disable the + // move assignment operator until C++17 is enabled. + // Explicitly defining a copy assignment operator prevents the compiler + // from automatically generating a move assignment operator. + TestPreset& operator=(const TestPreset& /*other*/) = default; +#endif - ExpandedPreset() = default; - ExpandedPreset(const Preset& preset) - : Preset(preset) + struct OutputOptions { - } - ExpandedPreset(Preset&& preset) - : Preset(std::move(preset)) + enum class VerbosityEnum + { + Default, + Verbose, + Extra + }; + + cm::optional<bool> ShortProgress; + cm::optional<VerbosityEnum> Verbosity; + cm::optional<bool> Debug; + cm::optional<bool> OutputOnFailure; + cm::optional<bool> Quiet; + std::string OutputLogFile; + cm::optional<bool> LabelSummary; + cm::optional<bool> SubprojectSummary; + cm::optional<int> MaxPassedTestOutputSize; + cm::optional<int> MaxFailedTestOutputSize; + cm::optional<int> MaxTestNameWidth; + }; + + struct IncludeOptions { - } + struct IndexOptions + { + cm::optional<int> Start; + cm::optional<int> End; + cm::optional<int> Stride; + std::vector<int> SpecificTests; + + std::string IndexFile; + }; + + std::string Name; + std::string Label; + cm::optional<IndexOptions> Index; + cm::optional<bool> UseUnion; + }; + + struct ExcludeOptions + { + struct FixturesOptions + { + std::string Any; + std::string Setup; + std::string Cleanup; + }; + + std::string Name; + std::string Label; + cm::optional<FixturesOptions> Fixtures; + }; + + struct FilterOptions + { + cm::optional<IncludeOptions> Include; + cm::optional<ExcludeOptions> Exclude; + }; + + struct ExecutionOptions + { + enum class ShowOnlyEnum + { + Human, + JsonV1 + }; + + struct RepeatOptions + { + enum class ModeEnum + { + UntilFail, + UntilPass, + AfterTimeout + }; + + ModeEnum Mode; + int Count; + }; + + enum class NoTestsActionEnum + { + Default, + Error, + Ignore + }; + + cm::optional<bool> StopOnFailure; + cm::optional<bool> EnableFailover; + cm::optional<int> Jobs; + std::string ResourceSpecFile; + cm::optional<int> TestLoad; + cm::optional<ShowOnlyEnum> ShowOnly; + cm::optional<bool> RerunFailed; + + cm::optional<RepeatOptions> Repeat; + cm::optional<bool> InteractiveDebugging; + cm::optional<bool> ScheduleRandom; + cm::optional<int> Timeout; + cm::optional<NoTestsActionEnum> NoTestsAction; + }; + + std::string ConfigurePreset; + cm::optional<bool> InheritConfigureEnvironment; + std::string Configuration; + std::vector<std::string> OverwriteConfigurationFile; + cm::optional<OutputOptions> Output; + cm::optional<FilterOptions> Filter; + cm::optional<ExecutionOptions> Execution; + + ReadFileResult VisitPresetInherit(const Preset& parent) override; + ReadFileResult VisitPresetAfterInherit() override; }; + template <class T> class PresetPair { public: - UnexpandedPreset Unexpanded; - cm::optional<ExpandedPreset> Expanded; + T Unexpanded; + cm::optional<T> Expanded; }; - std::string SourceDir; - std::map<std::string, PresetPair> Presets; - std::vector<std::string> PresetOrder; + std::map<std::string, PresetPair<ConfigurePreset>> ConfigurePresets; + std::map<std::string, PresetPair<BuildPreset>> BuildPresets; + std::map<std::string, PresetPair<TestPreset>> TestPresets; - enum class ReadFileResult - { - READ_OK, - FILE_NOT_FOUND, - JSON_PARSE_ERROR, - INVALID_ROOT, - NO_VERSION, - INVALID_VERSION, - UNRECOGNIZED_VERSION, - INVALID_CMAKE_VERSION, - UNRECOGNIZED_CMAKE_VERSION, - INVALID_PRESETS, - INVALID_PRESET, - INVALID_VARIABLE, - DUPLICATE_PRESETS, - CYCLIC_PRESET_INHERITANCE, - USER_PRESET_INHERITANCE, - INVALID_MACRO_EXPANSION, - }; + std::vector<std::string> ConfigurePresetOrder; + std::vector<std::string> BuildPresetOrder; + std::vector<std::string> TestPresetOrder; + + std::string SourceDir; static std::string GetFilename(const std::string& sourceDir); static std::string GetUserFilename(const std::string& sourceDir); @@ -140,9 +300,44 @@ public: bool allowNoFiles = false); static const char* ResultToString(ReadFileResult result); + std::string GetGeneratorForPreset(const std::string& presetName) const + { + auto configurePresetName = presetName; + + auto buildPresetIterator = this->BuildPresets.find(presetName); + if (buildPresetIterator != this->BuildPresets.end()) { + configurePresetName = + buildPresetIterator->second.Unexpanded.ConfigurePreset; + } else { + auto testPresetIterator = this->TestPresets.find(presetName); + if (testPresetIterator != this->TestPresets.end()) { + configurePresetName = + testPresetIterator->second.Unexpanded.ConfigurePreset; + } + } + + auto configurePresetIterator = + this->ConfigurePresets.find(configurePresetName); + if (configurePresetIterator != this->ConfigurePresets.end()) { + return configurePresetIterator->second.Unexpanded.Generator; + } + + // This should only happen if the preset is hidden + // or (for build or test presets) if ConfigurePreset is invalid. + return ""; + } + + static void PrintPresets( + const std::vector<const cmCMakePresetsFile::Preset*>& presets); + void PrintConfigurePresetList() const; + void PrintConfigurePresetList( + const std::function<bool(const ConfigurePreset&)>& filter) const; + void PrintBuildPresetList() const; + void PrintTestPresetList() const; + void PrintAllPresets() const; + private: - ReadFileResult ReadJSONFile(const std::string& filename, - std::vector<std::string>& presetOrder, - std::map<std::string, PresetPair>& presetMap, - bool user); + ReadFileResult ReadProjectPresetsInternal(bool allowNoFiles); + ReadFileResult ReadJSONFile(const std::string& filename, bool user); + void ClearPresets(); }; diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 6c1071d..4bea0d5 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -18,6 +18,7 @@ #include <vector> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/algorithm> #include <cmext/string_view> @@ -38,6 +39,7 @@ # include <unistd.h> // IWYU pragma: keep #endif +#include "cmCMakePresetsFile.h" #include "cmCTestBuildAndTestHandler.h" #include "cmCTestBuildHandler.h" #include "cmCTestConfigureHandler.h" @@ -2257,6 +2259,311 @@ bool cmCTest::AddVariableDefinition(const std::string& arg) return false; } +void cmCTest::SetPersistentOptionIfNotEmpty(const std::string& value, + const std::string& optionName) +{ + if (!value.empty()) { + this->GetTestHandler()->SetPersistentOption(optionName, value.c_str()); + this->GetMemCheckHandler()->SetPersistentOption(optionName, value.c_str()); + } +} + +bool cmCTest::SetArgsFromPreset(const std::string& presetName, + bool listPresets) +{ + const auto workingDirectory = cmSystemTools::GetCurrentWorkingDirectory(); + + cmCMakePresetsFile settingsFile; + auto result = settingsFile.ReadProjectPresets(workingDirectory); + if (result != cmCMakePresetsFile::ReadFileResult::READ_OK) { + cmSystemTools::Error(cmStrCat("Could not read presets from ", + workingDirectory, ": ", + cmCMakePresetsFile::ResultToString(result))); + return false; + } + + if (listPresets) { + settingsFile.PrintTestPresetList(); + return true; + } + + auto presetPair = settingsFile.TestPresets.find(presetName); + if (presetPair == settingsFile.TestPresets.end()) { + cmSystemTools::Error(cmStrCat("No such test preset in ", workingDirectory, + ": \"", presetName, '"')); + settingsFile.PrintTestPresetList(); + return false; + } + + if (presetPair->second.Unexpanded.Hidden) { + cmSystemTools::Error(cmStrCat("Cannot use hidden test preset in ", + workingDirectory, ": \"", presetName, '"')); + settingsFile.PrintTestPresetList(); + return false; + } + + auto const& expandedPreset = presetPair->second.Expanded; + if (!expandedPreset) { + cmSystemTools::Error(cmStrCat("Could not evaluate test preset \"", + presetName, "\": Invalid macro expansion")); + settingsFile.PrintTestPresetList(); + return false; + } + + auto configurePresetPair = + settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset); + if (configurePresetPair == settingsFile.ConfigurePresets.end()) { + cmSystemTools::Error(cmStrCat("No such configure preset in ", + workingDirectory, ": \"", + expandedPreset->ConfigurePreset, '"')); + settingsFile.PrintConfigurePresetList(); + return false; + } + + if (configurePresetPair->second.Unexpanded.Hidden) { + cmSystemTools::Error(cmStrCat("Cannot use hidden configure preset in ", + workingDirectory, ": \"", + expandedPreset->ConfigurePreset, '"')); + settingsFile.PrintConfigurePresetList(); + return false; + } + + auto const& expandedConfigurePreset = configurePresetPair->second.Expanded; + if (!expandedConfigurePreset) { + cmSystemTools::Error(cmStrCat("Could not evaluate configure preset \"", + expandedPreset->ConfigurePreset, + "\": Invalid macro expansion")); + return false; + } + + auto presetEnvironment = expandedPreset->Environment; + for (auto const& var : presetEnvironment) { + if (var.second) { + cmSystemTools::PutEnv(cmStrCat(var.first, '=', *var.second)); + } + } + + if (!expandedPreset->Configuration.empty()) { + this->SetConfigType(expandedPreset->Configuration); + } + + // Set build directory to value specified by the configure preset. + this->AddCTestConfigurationOverwrite( + cmStrCat("BuildDirectory=", expandedConfigurePreset->BinaryDir)); + for (const auto& kvp : expandedPreset->OverwriteConfigurationFile) { + this->AddCTestConfigurationOverwrite(kvp); + } + + if (expandedPreset->Output) { + this->Impl->TestProgressOutput = + expandedPreset->Output->ShortProgress.value_or(false); + + if (expandedPreset->Output->Verbosity) { + const auto& verbosity = *expandedPreset->Output->Verbosity; + switch (verbosity) { + case cmCMakePresetsFile::TestPreset::OutputOptions::VerbosityEnum:: + Extra: + this->Impl->ExtraVerbose = true; + // intentional fallthrough + case cmCMakePresetsFile::TestPreset::OutputOptions::VerbosityEnum:: + Verbose: + this->Impl->Verbose = true; + break; + case cmCMakePresetsFile::TestPreset::OutputOptions::VerbosityEnum:: + Default: + default: + // leave default settings + break; + } + } + + this->Impl->Debug = expandedPreset->Output->Debug.value_or(false); + this->Impl->ShowLineNumbers = + expandedPreset->Output->Debug.value_or(false); + this->Impl->OutputTestOutputOnTestFailure = + expandedPreset->Output->OutputOnFailure.value_or(false); + this->Impl->Quiet = expandedPreset->Output->Quiet.value_or(false); + + if (!expandedPreset->Output->OutputLogFile.empty()) { + this->SetOutputLogFileName(expandedPreset->Output->OutputLogFile); + } + + this->Impl->LabelSummary = + expandedPreset->Output->LabelSummary.value_or(true); + this->Impl->SubprojectSummary = + expandedPreset->Output->SubprojectSummary.value_or(true); + + if (expandedPreset->Output->MaxPassedTestOutputSize) { + this->Impl->TestHandler.SetTestOutputSizePassed( + *expandedPreset->Output->MaxPassedTestOutputSize); + } + + if (expandedPreset->Output->MaxFailedTestOutputSize) { + this->Impl->TestHandler.SetTestOutputSizeFailed( + *expandedPreset->Output->MaxFailedTestOutputSize); + } + + if (expandedPreset->Output->MaxTestNameWidth) { + this->Impl->MaxTestNameWidth = *expandedPreset->Output->MaxTestNameWidth; + } + } + + if (expandedPreset->Filter) { + if (expandedPreset->Filter->Include) { + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Include->Name, "IncludeRegularExpression"); + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Include->Label, "LabelRegularExpression"); + + if (expandedPreset->Filter->Include->Index) { + if (expandedPreset->Filter->Include->Index->IndexFile.empty()) { + const auto& start = expandedPreset->Filter->Include->Index->Start; + const auto& end = expandedPreset->Filter->Include->Index->End; + const auto& stride = expandedPreset->Filter->Include->Index->Stride; + std::string indexOptions; + indexOptions += (start ? std::to_string(*start) : "") + ","; + indexOptions += (end ? std::to_string(*end) : "") + ","; + indexOptions += (stride ? std::to_string(*stride) : "") + ","; + indexOptions += + cmJoin(expandedPreset->Filter->Include->Index->SpecificTests, ","); + + this->SetPersistentOptionIfNotEmpty(indexOptions, + "TestsToRunInformation"); + } else { + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Include->Index->IndexFile, + "TestsToRunInformation"); + } + } + + if (expandedPreset->Filter->Include->UseUnion.value_or(false)) { + this->GetTestHandler()->SetPersistentOption("UseUnion", "true"); + this->GetMemCheckHandler()->SetPersistentOption("UseUnion", "true"); + } + } + + if (expandedPreset->Filter->Exclude) { + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Exclude->Name, "ExcludeRegularExpression"); + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Exclude->Label, + "ExcludeLabelRegularExpression"); + + if (expandedPreset->Filter->Exclude->Fixtures) { + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Exclude->Fixtures->Any, + "ExcludeFixtureRegularExpression"); + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Exclude->Fixtures->Setup, + "ExcludeFixtureSetupRegularExpression"); + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Filter->Exclude->Fixtures->Cleanup, + "ExcludeFixtureCleanupRegularExpression"); + } + } + } + + if (expandedPreset->Execution) { + this->Impl->StopOnFailure = + expandedPreset->Execution->StopOnFailure.value_or(false); + this->Impl->Failover = + expandedPreset->Execution->EnableFailover.value_or(false); + + if (expandedPreset->Execution->Jobs) { + auto jobs = *expandedPreset->Execution->Jobs; + this->SetParallelLevel(jobs); + this->Impl->ParallelLevelSetInCli = true; + } + + this->SetPersistentOptionIfNotEmpty( + expandedPreset->Execution->ResourceSpecFile, "ResourceSpecFile"); + + if (expandedPreset->Execution->TestLoad) { + auto testLoad = *expandedPreset->Execution->TestLoad; + this->SetTestLoad(testLoad); + } + + if (expandedPreset->Execution->ShowOnly) { + this->Impl->ShowOnly = true; + + switch (*expandedPreset->Execution->ShowOnly) { + case cmCMakePresetsFile::TestPreset::ExecutionOptions::ShowOnlyEnum:: + JsonV1: + this->Impl->Quiet = true; + this->Impl->OutputAsJson = true; + this->Impl->OutputAsJsonVersion = 1; + break; + case cmCMakePresetsFile::TestPreset::ExecutionOptions::ShowOnlyEnum:: + Human: + // intentional fallthrough (human is the default) + default: + break; + } + } + + if (expandedPreset->Execution->RerunFailed.value_or(false)) { + this->GetTestHandler()->SetPersistentOption("RerunFailed", "true"); + this->GetMemCheckHandler()->SetPersistentOption("RerunFailed", "true"); + } + + if (expandedPreset->Execution->Repeat) { + this->Impl->RepeatCount = expandedPreset->Execution->Repeat->Count; + switch (expandedPreset->Execution->Repeat->Mode) { + case cmCMakePresetsFile::TestPreset::ExecutionOptions::RepeatOptions:: + ModeEnum::UntilFail: + this->Impl->RepeatMode = cmCTest::Repeat::UntilFail; + break; + case cmCMakePresetsFile::TestPreset::ExecutionOptions::RepeatOptions:: + ModeEnum::UntilPass: + this->Impl->RepeatMode = cmCTest::Repeat::UntilPass; + break; + case cmCMakePresetsFile::TestPreset::ExecutionOptions::RepeatOptions:: + ModeEnum::AfterTimeout: + this->Impl->RepeatMode = cmCTest::Repeat::AfterTimeout; + break; + default: + // should never default since mode is required + return false; + } + } + + if (expandedPreset->Execution->InteractiveDebugging) { + this->Impl->InteractiveDebugMode = + *expandedPreset->Execution->InteractiveDebugging; + } + + if (expandedPreset->Execution->ScheduleRandom.value_or(false)) { + this->Impl->ScheduleType = "Random"; + } + + if (expandedPreset->Execution->Timeout) { + this->Impl->GlobalTimeout = + cmDuration(*expandedPreset->Execution->Timeout); + } + + if (expandedPreset->Execution->NoTestsAction) { + switch (*expandedPreset->Execution->NoTestsAction) { + case cmCMakePresetsFile::TestPreset::ExecutionOptions:: + NoTestsActionEnum::Error: + this->Impl->NoTestsMode = cmCTest::NoTests::Error; + break; + case cmCMakePresetsFile::TestPreset::ExecutionOptions:: + NoTestsActionEnum::Ignore: + this->Impl->NoTestsMode = cmCTest::NoTests::Ignore; + break; + case cmCMakePresetsFile::TestPreset::ExecutionOptions:: + NoTestsActionEnum::Default: + break; + default: + // should never default + return false; + } + } + } + + return true; +} + // the main entry point of ctest, called from main int cmCTest::Run(std::vector<std::string>& args, std::string* output) { @@ -2268,6 +2575,37 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output) // copy the command line cm::append(this->Impl->InitialCommandLineArguments, args); + // check if a test preset was specified + + bool listPresets = + find(args.begin(), args.end(), "--list-presets") != args.end(); + auto it = find(args.begin(), args.end(), "--preset"); + if (listPresets || it != args.end()) { + std::string errormsg; + bool success; + + if (listPresets) { + // If listing presets we don't need a presetName + success = this->SetArgsFromPreset("", listPresets); + } else { + if (++it != args.end()) { + auto presetName = *it; + success = this->SetArgsFromPreset(presetName, listPresets); + } else { + cmSystemTools::Error("'--preset' requires an argument"); + success = false; + } + } + + if (listPresets) { + return success ? 0 : 1; + } + + if (!success) { + return 1; + } + } + // process the command line arguments for (size_t i = 1; i < args.size(); ++i) { // handle the simple commandline arguments @@ -2339,7 +2677,7 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output) this->Impl->ScheduleType = "Random"; } - // pass the argument to all the handlers as well, but i may no longer be + // pass the argument to all the handlers as well, but it may no longer be // set to what it was originally so I'm not sure this is working as // intended for (auto& handler : this->Impl->GetTestingHandlers()) { diff --git a/Source/cmCTest.h b/Source/cmCTest.h index e12f8b0..4669a1c 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -461,6 +461,9 @@ public: void SetRunCurrentScript(bool value); private: + void SetPersistentOptionIfNotEmpty(const std::string& value, + const std::string& optionName); + int GenerateNotesFile(const std::string& files); void BlockTestErrorDiagnostics(); @@ -484,6 +487,9 @@ private: /** add a variable definition from a command line -D value */ bool AddVariableDefinition(const std::string& arg); + /** set command line arguments read from a test preset */ + bool SetArgsFromPreset(const std::string& presetName, bool listPresets); + /** parse and process most common command line arguments */ bool HandleCommandLineArguments(size_t& i, std::vector<std::string>& args, std::string& errormsg); diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 48848a7..4b57395 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -726,6 +726,17 @@ void cmake::LoadEnvironmentPresets() readGeneratorVar("CMAKE_GENERATOR_TOOLSET", this->GeneratorToolset); } +namespace { +enum class ListPresets +{ + None, + Configure, + Build, + Test, + All, +}; +} + // Parse the args void cmake::SetArgs(const std::vector<std::string>& args) { @@ -738,7 +749,8 @@ void cmake::SetArgs(const std::vector<std::string>& args) std::string profilingFormat; std::string profilingOutput; std::string presetName; - bool listPresets = false; + + ListPresets listPresets = ListPresets::None; #endif auto SourceArgLambda = [](std::string const& value, cmake* state) -> bool { @@ -995,11 +1007,27 @@ void cmake::SetArgs(const std::vector<std::string>& args) presetName = value; return true; }); - arguments.emplace_back("--list-presets", CommandArgument::Values::Zero, - [&](std::string const&, cmake*) -> bool { - listPresets = true; - return true; - }); + arguments.emplace_back( + "--list-presets", CommandArgument::Values::ZeroOrOne, + [&](std::string const& value, cmake*) -> bool { + if (value.empty() || value == "configure") { + listPresets = ListPresets::Configure; + } else if (value == "build") { + listPresets = ListPresets::Build; + } else if (value == "test") { + listPresets = ListPresets::Test; + } else if (value == "all") { + listPresets = ListPresets::All; + } else { + cmSystemTools::Error( + "Invalid value specified for --list-presets.\n" + "Valid values are configure, build, test, or all. " + "When no value is passed the default is configure."); + return false; + } + + return true; + }); #endif @@ -1119,7 +1147,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) } #if !defined(CMAKE_BOOTSTRAP) - if (listPresets || !presetName.empty()) { + if (listPresets != ListPresets::None || !presetName.empty()) { cmCMakePresetsFile settingsFile; auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory()); if (result != cmCMakePresetsFile::ReadFileResult::READ_OK) { @@ -1128,12 +1156,24 @@ void cmake::SetArgs(const std::vector<std::string>& args) ": ", cmCMakePresetsFile::ResultToString(result))); return; } - if (listPresets) { - this->PrintPresetList(settingsFile); + + if (listPresets != ListPresets::None) { + if (listPresets == ListPresets::Configure) { + this->PrintPresetList(settingsFile); + } else if (listPresets == ListPresets::Build) { + settingsFile.PrintBuildPresetList(); + } else if (listPresets == ListPresets::Test) { + settingsFile.PrintTestPresetList(); + } else if (listPresets == ListPresets::All) { + settingsFile.PrintAllPresets(); + } + + this->SetWorkingMode(WorkingMode::HELP_MODE); return; } - auto preset = settingsFile.Presets.find(presetName); - if (preset == settingsFile.Presets.end()) { + + auto preset = settingsFile.ConfigurePresets.find(presetName); + if (preset == settingsFile.ConfigurePresets.end()) { cmSystemTools::Error(cmStrCat("No such preset in ", this->GetHomeDirectory(), ": \"", presetName, '"')); @@ -1562,44 +1602,16 @@ void cmake::PrintPresetList(const cmCMakePresetsFile& file) const { std::vector<GeneratorInfo> generators; this->GetRegisteredGenerators(generators, false); + auto filter = + [&generators](const cmCMakePresetsFile::ConfigurePreset& preset) -> bool { + auto condition = [&preset](const GeneratorInfo& info) -> bool { + return info.name == preset.Generator; + }; + auto it = std::find_if(generators.begin(), generators.end(), condition); + return it != generators.end(); + }; - std::vector<cmCMakePresetsFile::UnexpandedPreset> presets; - for (auto const& p : file.PresetOrder) { - auto const& preset = file.Presets.at(p); - if (!preset.Unexpanded.Hidden && preset.Expanded && - std::find_if(generators.begin(), generators.end(), - [&preset](const GeneratorInfo& info) { - return info.name == preset.Unexpanded.Generator; - }) != generators.end()) { - presets.push_back(preset.Unexpanded); - } - } - - if (presets.empty()) { - return; - } - - std::cout << "Available presets:\n\n"; - - auto longestPresetName = - std::max_element(presets.begin(), presets.end(), - [](const cmCMakePresetsFile::UnexpandedPreset& a, - const cmCMakePresetsFile::UnexpandedPreset& b) { - return a.Name.length() < b.Name.length(); - }); - auto longestLength = longestPresetName->Name.length(); - - for (auto const& preset : presets) { - std::cout << " \"" << preset.Name << '"'; - auto const& description = preset.DisplayName; - if (!description.empty()) { - for (std::size_t i = 0; i < longestLength - preset.Name.length(); ++i) { - std::cout << ' '; - } - std::cout << " - " << description; - } - std::cout << '\n'; - } + file.PrintConfigurePresetList(filter); } #endif @@ -3068,15 +3080,119 @@ std::vector<std::string> cmake::GetDebugConfigs() return configs; } -int cmake::Build(int jobs, const std::string& dir, - const std::vector<std::string>& targets, - const std::string& config, - const std::vector<std::string>& nativeOptions, bool clean, - bool verbose) +int cmake::Build(int jobs, std::string dir, std::vector<std::string> targets, + std::string config, std::vector<std::string> nativeOptions, + bool clean, bool verbose, const std::string& presetName, + bool listPresets) { - this->SetHomeDirectory(""); this->SetHomeOutputDirectory(""); + +#if !defined(CMAKE_BOOTSTRAP) + if (!presetName.empty() || listPresets) { + this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory()); + this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory()); + + cmCMakePresetsFile settingsFile; + auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory()); + if (result != cmCMakePresetsFile::ReadFileResult::READ_OK) { + cmSystemTools::Error( + cmStrCat("Could not read presets from ", this->GetHomeDirectory(), + ": ", cmCMakePresetsFile::ResultToString(result))); + return 1; + } + + if (listPresets) { + settingsFile.PrintBuildPresetList(); + return 0; + } + + auto presetPair = settingsFile.BuildPresets.find(presetName); + if (presetPair == settingsFile.BuildPresets.end()) { + cmSystemTools::Error(cmStrCat("No such build preset in ", + this->GetHomeDirectory(), ": \"", + presetName, '"')); + settingsFile.PrintBuildPresetList(); + return 1; + } + + if (presetPair->second.Unexpanded.Hidden) { + cmSystemTools::Error(cmStrCat("Cannot use hidden build preset in ", + this->GetHomeDirectory(), ": \"", + presetName, '"')); + settingsFile.PrintBuildPresetList(); + return 1; + } + + auto const& expandedPreset = presetPair->second.Expanded; + if (!expandedPreset) { + cmSystemTools::Error(cmStrCat("Could not evaluate build preset \"", + presetName, + "\": Invalid macro expansion")); + settingsFile.PrintBuildPresetList(); + return 1; + } + + auto configurePresetPair = + settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset); + if (configurePresetPair == settingsFile.ConfigurePresets.end()) { + cmSystemTools::Error(cmStrCat("No such configure preset in ", + this->GetHomeDirectory(), ": \"", + expandedPreset->ConfigurePreset, '"')); + this->PrintPresetList(settingsFile); + return 1; + } + + if (configurePresetPair->second.Unexpanded.Hidden) { + cmSystemTools::Error(cmStrCat("Cannot use hidden configure preset in ", + this->GetHomeDirectory(), ": \"", + expandedPreset->ConfigurePreset, '"')); + this->PrintPresetList(settingsFile); + return 1; + } + + auto const& expandedConfigurePreset = configurePresetPair->second.Expanded; + if (!expandedConfigurePreset) { + cmSystemTools::Error(cmStrCat("Could not evaluate configure preset \"", + expandedPreset->ConfigurePreset, + "\": Invalid macro expansion")); + return 1; + } + + dir = expandedConfigurePreset->BinaryDir; + + this->UnprocessedPresetEnvironment = expandedPreset->Environment; + this->ProcessPresetEnvironment(); + + if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL && expandedPreset->Jobs) { + jobs = *expandedPreset->Jobs; + } + + if (targets.empty()) { + targets.insert(targets.begin(), expandedPreset->Targets.begin(), + expandedPreset->Targets.end()); + } + + if (config.empty()) { + config = expandedPreset->Configuration; + } + + if (!clean && expandedPreset->CleanFirst) { + clean = *expandedPreset->CleanFirst; + } + + if (!verbose && expandedPreset->Verbose) { + verbose = *expandedPreset->Verbose; + } + + if (nativeOptions.empty()) { + nativeOptions.insert(nativeOptions.begin(), + expandedPreset->NativeToolOptions.begin(), + expandedPreset->NativeToolOptions.end()); + } + } +#endif + if (!cmSystemTools::FileIsDirectory(dir)) { std::cerr << "Error: " << dir << " is not a directory\n"; return 1; diff --git a/Source/cmake.h b/Source/cmake.h index d936f28..82e028c 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -238,7 +238,7 @@ public: bool CreateAndSetGlobalGenerator(const std::string& name, bool allowArch); #ifndef CMAKE_BOOTSTRAP - //! Print list of presets + //! Print list of configure presets void PrintPresetList(const cmCMakePresetsFile& file) const; #endif @@ -556,10 +556,10 @@ public: cmListFileBacktrace const& backtrace = cmListFileBacktrace()) const; //! run the --build option - int Build(int jobs, const std::string& dir, - const std::vector<std::string>& targets, const std::string& config, - const std::vector<std::string>& nativeOptions, bool clean, - bool verbose); + int Build(int jobs, std::string dir, std::vector<std::string> targets, + std::string config, std::vector<std::string> nativeOptions, + bool clean, bool verbose, const std::string& presetName, + bool listPresets); //! run the --open option bool Open(const std::string& dir, bool dryRun); diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index ba471b7..cd3c955 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -425,6 +425,8 @@ int do_build(int ac, char const* const* av) bool foundClean = false; bool foundNonClean = false; bool verbose = cmSystemTools::HasEnv("VERBOSE"); + std::string presetName; + bool listPresets = false; auto jLambda = [&](std::string const& value) -> bool { jobs = extract_job_number("-j", value); @@ -464,6 +466,16 @@ int do_build(int ac, char const* const* av) cmCommandLineArgument<bool(std::string const& value)>; std::vector<CommandArgument> arguments = { + CommandArgument{ "--preset", CommandArgument::Values::One, + [&](std::string const& value) -> bool { + presetName = value; + return true; + } }, + CommandArgument{ "--list-presets", CommandArgument::Values::Zero, + [&](std::string const&) -> bool { + listPresets = true; + return true; + } }, CommandArgument{ "-j", CommandArgument::Values::ZeroOrOne, jLambda }, CommandArgument{ "--parallel", CommandArgument::Values::ZeroOrOne, parallelLambda }, @@ -494,11 +506,26 @@ int do_build(int ac, char const* const* av) }; if (ac >= 3) { - dir = cmSystemTools::CollapseFullPath(av[2]); - std::vector<std::string> inputArgs; - inputArgs.reserve(ac - 3); - cm::append(inputArgs, av + 3, av + ac); + + bool hasPreset = false; + for (int i = 2; i < ac; ++i) { + if (strcmp(av[i], "--list-presets") == 0 || + strcmp(av[i], "--preset") == 0) { + hasPreset = true; + break; + } + } + + if (hasPreset) { + inputArgs.reserve(ac - 2); + cm::append(inputArgs, av + 2, av + ac); + } else { + dir = cmSystemTools::CollapseFullPath(av[2]); + + inputArgs.reserve(ac - 3); + cm::append(inputArgs, av + 3, av + ac); + } decltype(inputArgs.size()) i = 0; for (; i < inputArgs.size() && !nativeOptionsPassed; ++i) { @@ -551,12 +578,16 @@ int do_build(int ac, char const* const* av) } } - if (dir.empty()) { + if (dir.empty() && presetName.empty() && !listPresets) { /* clang-format off */ std::cerr << - "Usage: cmake --build <dir> [options] [-- [native-options]]\n" + "Usage: cmake --build [<dir> | --preset <preset>] [options] [-- [native-options]]\n" "Options:\n" " <dir> = Project binary directory to be built.\n" + " --preset <preset>\n" + " = Specify a build preset.\n" + " --list-presets\n" + " = List available build presets.\n" " --parallel [<jobs>], -j [<jobs>]\n" " = Build in parallel using the given number of jobs. \n" " If <jobs> is omitted the native build tool's \n" @@ -587,8 +618,10 @@ int do_build(int ac, char const* const* av) cm.SetProgressCallback([&cm](const std::string& msg, float prog) { cmakemainProgressCallback(msg, prog, &cm); }); - return cm.Build(jobs, dir, targets, config, nativeOptions, cleanFirst, - verbose); + + return cm.Build(jobs, std::move(dir), std::move(targets), std::move(config), + std::move(nativeOptions), cleanFirst, verbose, presetName, + listPresets); #endif } diff --git a/Source/ctest.cxx b/Source/ctest.cxx index 600df1d..1404b0c 100644 --- a/Source/ctest.cxx +++ b/Source/ctest.cxx @@ -26,6 +26,8 @@ static const char* cmDocumentationUsage[][2] = { { nullptr, { nullptr, nullptr } }; static const char* cmDocumentationOptions[][2] = { + { "--preset <preset>", "Read arguments from a test preset." }, + { "--list-presets", "List available test presets." }, { "-C <cfg>, --build-config <cfg>", "Choose configuration to test." }, { "--progress", "Enable short progress output from tests." }, { "-V,--verbose", "Enable verbose output from tests." }, diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 549d8eb..91fe6ca 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -805,6 +805,8 @@ add_RunCMake_test(PrecompileHeaders -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} add_RunCMake_test("UnityBuild") add_RunCMake_test(CMakePresets -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}) +add_RunCMake_test(CMakePresetsBuild -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}) +add_RunCMake_test(CMakePresetsTest -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}) if(${CMAKE_GENERATOR} MATCHES "Make|Ninja") add_RunCMake_test(TransformDepfile) diff --git a/Tests/RunCMake/CMakePresets/ListPresets-stdout.txt b/Tests/RunCMake/CMakePresets/ListPresets-stdout.txt index 1758f33..60d6adb 100644 --- a/Tests/RunCMake/CMakePresets/ListPresets-stdout.txt +++ b/Tests/RunCMake/CMakePresets/ListPresets-stdout.txt @@ -1,5 +1,5 @@ ^Not searching for unused variables given on the command line\. -Available presets: +Available configure presets: "zzzzzz" - Sleepy "aaaaaaaa" - Screaming diff --git a/Tests/RunCMake/CMakePresets/ListPresetsHidden-stdout.txt b/Tests/RunCMake/CMakePresets/ListPresetsHidden-stdout.txt index 1758f33..60d6adb 100644 --- a/Tests/RunCMake/CMakePresets/ListPresetsHidden-stdout.txt +++ b/Tests/RunCMake/CMakePresets/ListPresetsHidden-stdout.txt @@ -1,5 +1,5 @@ ^Not searching for unused variables given on the command line\. -Available presets: +Available configure presets: "zzzzzz" - Sleepy "aaaaaaaa" - Screaming diff --git a/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset-stdout.txt b/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset-stdout.txt index 1758f33..60d6adb 100644 --- a/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset-stdout.txt +++ b/Tests/RunCMake/CMakePresets/ListPresetsNoSuchPreset-stdout.txt @@ -1,5 +1,5 @@ ^Not searching for unused variables given on the command line\. -Available presets: +Available configure presets: "zzzzzz" - Sleepy "aaaaaaaa" - Screaming diff --git a/Tests/RunCMake/CMakePresets/ListPresetsWorkingDir-stdout.txt b/Tests/RunCMake/CMakePresets/ListPresetsWorkingDir-stdout.txt index 1758f33..60d6adb 100644 --- a/Tests/RunCMake/CMakePresets/ListPresetsWorkingDir-stdout.txt +++ b/Tests/RunCMake/CMakePresets/ListPresetsWorkingDir-stdout.txt @@ -1,5 +1,5 @@ ^Not searching for unused variables given on the command line\. -Available presets: +Available configure presets: "zzzzzz" - Sleepy "aaaaaaaa" - Screaming diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake index 1ffda3d..25a7f2f 100644 --- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake @@ -9,21 +9,7 @@ endif() set(RunCMake-check-file check.cmake) -function(validate_schema file expected_result) - execute_process( - COMMAND "${PYTHON_EXECUTABLE}" "${RunCMake_SOURCE_DIR}/validate_schema.py" "${file}" - RESULT_VARIABLE _result - OUTPUT_VARIABLE _output - ERROR_VARIABLE _error - ) - if(NOT _result STREQUAL expected_result) - string(REPLACE "\n" "\n" _output_p "${_output}") - string(REPLACE "\n" "\n" _error_p "${_error}") - string(APPEND RunCMake_TEST_FAILED "Expected result of validating ${file}: ${expected_result}\nActual result: ${_result}\nOutput:\n${_output_p}\nError:\n${_error_p}") - endif() - - set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) -endfunction() +include("${RunCMake_SOURCE_DIR}/validate_schema.cmake") function(run_cmake_presets name) set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/${name}") diff --git a/Tests/RunCMake/CMakePresets/validate_schema.cmake b/Tests/RunCMake/CMakePresets/validate_schema.cmake new file mode 100644 index 0000000..68b638f --- /dev/null +++ b/Tests/RunCMake/CMakePresets/validate_schema.cmake @@ -0,0 +1,19 @@ +function(validate_schema file expected_result) + if (NOT CMakePresets_VALIDATE_SCRIPT_PATH) + set(CMakePresets_VALIDATE_SCRIPT_PATH "${RunCMake_SOURCE_DIR}/validate_schema.py") + endif() + + execute_process( + COMMAND "${PYTHON_EXECUTABLE}" "${CMakePresets_VALIDATE_SCRIPT_PATH}" "${file}" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _output + ERROR_VARIABLE _error + ) + if(NOT _result STREQUAL expected_result) + string(REPLACE "\n" "\n " _output_p "${_output}") + string(REPLACE "\n" "\n " _error_p "${_error}") + string(APPEND RunCMake_TEST_FAILED "Expected result of validating ${file}: ${expected_result}\nActual result: ${_result}\nOutput:\n ${_output_p}\nError:\n ${_error_p}\n") + endif() + + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) +endfunction() diff --git a/Tests/RunCMake/CMakePresetsBuild/CMakeLists.txt.in b/Tests/RunCMake/CMakePresetsBuild/CMakeLists.txt.in new file mode 100644 index 0000000..129184a --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/CMakeLists.txt.in @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.19) +project("@CASE_NAME@" NONE) +include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake") diff --git a/Tests/RunCMake/CMakePresetsBuild/Good-build-build-other-check.cmake b/Tests/RunCMake/CMakePresetsBuild/Good-build-build-other-check.cmake new file mode 100644 index 0000000..b2699db --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Good-build-build-other-check.cmake @@ -0,0 +1,5 @@ +include("${RunCMake_SOURCE_DIR}/TestVariable.cmake") + +test_environment_variable("TEST_ENV_" "other") + +include("${RunCMake_SOURCE_DIR}/check.cmake") diff --git a/Tests/RunCMake/CMakePresetsBuild/Good-build-macros-check.cmake b/Tests/RunCMake/CMakePresetsBuild/Good-build-macros-check.cmake new file mode 100644 index 0000000..7801c3d --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Good-build-macros-check.cmake @@ -0,0 +1,14 @@ +include("${RunCMake_SOURCE_DIR}/TestVariable.cmake") + +if(RunCMake_GENERATOR STREQUAL "NMake Makefiles JOM") + # JOM removes the '$' and content following it. +else() + test_environment_variable("TEST_DOLLAR" "x\\$x") +endif() +test_environment_variable("TEST_GENERATOR" "${RunCMake_GENERATOR}") +test_environment_variable("TEST_PRESET_NAME" "xmacrosx") +test_environment_variable("TEST_SOURCE_DIR_" "x[^\n]*[/\\\\]Tests[/\\\\]RunCMake[/\\\\]CMakePresetsBuild[/\\\\]Goodx") +test_environment_variable("TEST_SOURCE_DIR_NAME" "xGoodx") +test_environment_variable("TEST_SOURCE_PARENT_DIR" "x[^\n]*[/\\\\]Tests[/\\\\]RunCMake[/\\\\]CMakePresetsBuildx") + +include("${RunCMake_SOURCE_DIR}/check.cmake") diff --git a/Tests/RunCMake/CMakePresetsBuild/Good-build-noEnvironment-check.cmake b/Tests/RunCMake/CMakePresetsBuild/Good-build-noEnvironment-check.cmake new file mode 100644 index 0000000..94a54ca --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Good-build-noEnvironment-check.cmake @@ -0,0 +1,6 @@ +string(FIND "${actual_stdout}" "TEST_ENV_" TEST_ENV_POS) +if (NOT TEST_ENV_POS EQUAL -1) + message(FATAL_ERROR "Found TEST_ENV_ in environment") +endif() + +include("${RunCMake_SOURCE_DIR}/check.cmake") diff --git a/Tests/RunCMake/CMakePresetsBuild/Good-build-withEnvironment-check.cmake b/Tests/RunCMake/CMakePresetsBuild/Good-build-withEnvironment-check.cmake new file mode 100644 index 0000000..9c6b5c2 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Good-build-withEnvironment-check.cmake @@ -0,0 +1,8 @@ +include("${RunCMake_SOURCE_DIR}/TestVariable.cmake") + +test_environment_variable("TEST_ENV_" "Environment variable") +test_environment_variable("TEST_ENV_OVERRIDE_" "Overridden") +test_environment_variable("TEST_ENV_OVERRIDE_REF" "xOverriddenx") +test_environment_variable("TEST_ENV_REF" "xEnvironment variablex") + +include("${RunCMake_SOURCE_DIR}/check.cmake") diff --git a/Tests/RunCMake/CMakePresetsBuild/Good.cmake b/Tests/RunCMake/CMakePresetsBuild/Good.cmake new file mode 100644 index 0000000..491d367 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Good.cmake @@ -0,0 +1,3 @@ +add_custom_target(good ALL) +add_custom_command(TARGET good PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E environment) diff --git a/Tests/RunCMake/CMakePresetsBuild/Good.json.in b/Tests/RunCMake/CMakePresetsBuild/Good.json.in new file mode 100644 index 0000000..fd43c4e --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Good.json.in @@ -0,0 +1,78 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build/${presetName}", + "environment": { + "TEST_ENV_": "Environment variable", + "TEST_ENV_OVERRIDE_": "Environment variable", + "TEST_ENV_OVERRIDE_REF": "x$env{TEST_ENV_OVERRIDE_}x" + } + }, + { + "name": "other", + "inherits": "default", + "environment": { + "TEST_ENV_": "other" + } + } + ], + "buildPresets": [ + { + "name": "build-default", + "hidden": true, + "inherits": [], + "configurePreset": "default", + "vendor": {}, + "displayName": "", + "description": "", + "inheritConfigureEnvironment": true, + "environment": {}, + "jobs": 0, + "targets": [], + "configuration": "", + "verbose": true, + "nativeToolOptions": [] + }, + { + "name": "build-other", + "configurePreset": "other" + }, + { + "name": "withEnvironment", + "inherits": "build-default", + "environment": { + "TEST_ENV_OVERRIDE_": "Overridden", + "TEST_ENV_REF": "x$env{TEST_ENV_}x", + "TEST_ENV_OVERRIDE_REF": "x$env{TEST_ENV_OVERRIDE_}x" + } + }, + { + "name": "noEnvironment", + "inherits": "build-default", + "inheritConfigureEnvironment": false + }, + { + "name": "macros", + "inherits": "build-default", + "inheritConfigureEnvironment": false, + "environment": { + "TEST_SOURCE_DIR_": "x${sourceDir}x", + "TEST_SOURCE_PARENT_DIR": "x${sourceParentDir}x", + "TEST_SOURCE_DIR_NAME": "x${sourceDirName}x", + "TEST_PRESET_NAME": "x${presetName}x", + "TEST_GENERATOR": "x${generator}x", + "TEST_DOLLAR": "x${dollar}x" + } + }, + { + "name": "vendorObject", + "configurePreset": "default", + "vendor": { + "example.com": "value" + } + } + ] +} diff --git a/Tests/RunCMake/CMakePresetsBuild/Invalid-build-badConfigurePreset-result.txt b/Tests/RunCMake/CMakePresetsBuild/Invalid-build-badConfigurePreset-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Invalid-build-badConfigurePreset-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresetsBuild/Invalid-build-badConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/Invalid-build-badConfigurePreset-stderr.txt new file mode 100644 index 0000000..7c3255d --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Invalid-build-badConfigurePreset-stderr.txt @@ -0,0 +1,2 @@ +CMake Error: No such configure preset in [^ +]*/Tests/RunCMake/CMakePresetsBuild/Invalid: "dne" diff --git a/Tests/RunCMake/CMakePresetsBuild/Invalid-build-hidden-result.txt b/Tests/RunCMake/CMakePresetsBuild/Invalid-build-hidden-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Invalid-build-hidden-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresetsBuild/Invalid-build-hidden-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/Invalid-build-hidden-stderr.txt new file mode 100644 index 0000000..f10c7b8 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Invalid-build-hidden-stderr.txt @@ -0,0 +1,2 @@ +CMake Error: Cannot use hidden build preset in [^ +]*/Tests/RunCMake/CMakePresetsBuild/Invalid: "hidden" diff --git a/Tests/RunCMake/CMakePresetsBuild/Invalid-build-vendorMacro-result.txt b/Tests/RunCMake/CMakePresetsBuild/Invalid-build-vendorMacro-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Invalid-build-vendorMacro-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresetsBuild/Invalid-build-vendorMacro-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/Invalid-build-vendorMacro-stderr.txt new file mode 100644 index 0000000..a3b951f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Invalid-build-vendorMacro-stderr.txt @@ -0,0 +1 @@ +CMake Error: Could not evaluate build preset "vendorMacro": Invalid macro expansion diff --git a/Tests/RunCMake/CMakePresetsBuild/Invalid.cmake b/Tests/RunCMake/CMakePresetsBuild/Invalid.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Invalid.cmake diff --git a/Tests/RunCMake/CMakePresetsBuild/Invalid.json.in b/Tests/RunCMake/CMakePresetsBuild/Invalid.json.in new file mode 100644 index 0000000..cf56cef --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/Invalid.json.in @@ -0,0 +1,27 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build/${presetName}" + } + ], + "buildPresets": [ + { + "name": "hidden", + "hidden": true + }, + { + "name": "vendorMacro", + "configurePreset": "default", + "environment": { + "TEST": "$vendor{bad.TEST}" + } + }, + { + "name": "badConfigurePreset", + "configurePreset": "dne" + } + ] +} diff --git a/Tests/RunCMake/CMakePresetsBuild/ListPresets-build-x-stdout.txt b/Tests/RunCMake/CMakePresetsBuild/ListPresets-build-x-stdout.txt new file mode 100644 index 0000000..4d30707 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/ListPresets-build-x-stdout.txt @@ -0,0 +1,5 @@ +Available build presets: + + "build-default" - build-default displayName + "empty" + "display" - display displayName diff --git a/Tests/RunCMake/CMakePresetsBuild/ListPresets.cmake b/Tests/RunCMake/CMakePresetsBuild/ListPresets.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/ListPresets.cmake diff --git a/Tests/RunCMake/CMakePresetsBuild/ListPresets.json.in b/Tests/RunCMake/CMakePresetsBuild/ListPresets.json.in new file mode 100644 index 0000000..3f5e02c --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/ListPresets.json.in @@ -0,0 +1,31 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build/${presetName}" + } + ], + "buildPresets": [ + { + "name": "build-default", + "configurePreset": "default", + "displayName": "build-default displayName", + "description": "build-default description" + }, + { + "name": "empty", + "inherits": "build-default" + }, + { + "name": "display", + "inherits": "build-default", + "displayName": "display displayName" + }, + { + "name": "hidden", + "hidden": true + } + ] +} diff --git a/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-result.txt b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt new file mode 100644 index 0000000..fcb37bc --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt @@ -0,0 +1,2 @@ +CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset: Invalid preset diff --git a/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset.cmake b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset.cmake diff --git a/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset.json.in b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset.json.in new file mode 100644 index 0000000..81b2cde --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset.json.in @@ -0,0 +1,15 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build/${presetName}" + } + ], + "buildPresets": [ + { + "name": "noConfigurePreset" + } + ] +} diff --git a/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported-build-x-result.txt b/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported-build-x-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported-build-x-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported-build-x-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported-build-x-stderr.txt new file mode 100644 index 0000000..d6ae62d --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported-build-x-stderr.txt @@ -0,0 +1,2 @@ +CMake Error: Could not read presets from [^ +]*Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported: File version must be 2 or higher for build and test preset support. diff --git a/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported.json.in b/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported.json.in new file mode 100644 index 0000000..0028239 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported.json.in @@ -0,0 +1,7 @@ +{ + "version": 1, + "configurePresets": [ + ], + "buildPresets": [ + ] +} diff --git a/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake new file mode 100644 index 0000000..9491524 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake @@ -0,0 +1,67 @@ +include(RunCMake) + +# Presets do not support legacy VS generator name architecture suffix. +if(RunCMake_GENERATOR MATCHES "^(Visual Studio [0-9]+ [0-9]+) ") + set(RunCMake_GENERATOR "${CMAKE_MATCH_1}") +endif() + +function(run_cmake_build_presets name CMakePresetsBuild_CONFIGURE_PRESETS CMakePresetsBuild_BUILD_PRESETS) + set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/${name}") + set(RunCMake_TEST_BINARY_DIR "${RunCMake_TEST_SOURCE_DIR}/build") + set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}") + + set(RunCMake_TEST_NO_CLEAN TRUE) + + file(REMOVE_RECURSE "${RunCMake_TEST_SOURCE_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + + set(CASE_NAME "${name}") + set(CASE_SOURCE_DIR "${RunCMake_SOURCE_DIR}") + configure_file("${RunCMake_SOURCE_DIR}/CMakeLists.txt.in" "${RunCMake_TEST_SOURCE_DIR}/CMakeLists.txt" @ONLY) + + if(NOT CMakePresetsBuild_FILE) + set(CMakePresetsBuild_FILE "${RunCMake_SOURCE_DIR}/${name}.json.in") + endif() + if(EXISTS "${CMakePresetsBuild_FILE}") + configure_file("${CMakePresetsBuild_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" @ONLY) + endif() + + if(NOT CMakeUserPresets_FILE) + set(CMakeUserPresets_FILE "${RunCMake_SOURCE_DIR}/${name}User.json.in") + endif() + if(EXISTS "${CMakeUserPresets_FILE}") + configure_file("${CMakeUserPresets_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakeUserPresets.json" @ONLY) + endif() + + if (NOT CMakePresetsBuild_BUILD_ONLY) + foreach(CONFIGURE_PRESET ${CMakePresetsBuild_CONFIGURE_PRESETS}) + run_cmake_command("${name}-configure-${CONFIGURE_PRESET}" + "${CMAKE_COMMAND}" "--preset" "${CONFIGURE_PRESET}") + endforeach() + endif() + + foreach(BUILD_PRESET ${CMakePresetsBuild_BUILD_PRESETS}) + if (EXISTS "${RunCMake_SOURCE_DIR}/${name}-build-${BUILD_PRESET}-check.cmake") + set(RunCMake-check-file "${name}-build-${BUILD_PRESET}-check.cmake") + else() + set(RunCMake-check-file "check.cmake") + endif() + + run_cmake_command(${name}-build-${BUILD_PRESET} + ${CMAKE_COMMAND} "--build" "--preset" "${BUILD_PRESET}" ${ARGN}) + endforeach() +endfunction() + +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) + +run_cmake_build_presets(Good "default;other" "build-other;withEnvironment;noEnvironment;macros;vendorObject") + +set(CMakePresetsBuild_BUILD_ONLY 1) +run_cmake_build_presets(ListPresets "x" "x" "--list-presets") +run_cmake_build_presets(NoConfigurePreset "x" "noConfigurePreset") +run_cmake_build_presets(Invalid "x" "hidden;vendorMacro;badConfigurePreset") + +set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) +run_cmake_build_presets(PresetsUnsupported "x" "x") +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) +set(CMakePresetsBuild_BUILD_ONLY 0) diff --git a/Tests/RunCMake/CMakePresetsBuild/TestVariable.cmake b/Tests/RunCMake/CMakePresetsBuild/TestVariable.cmake new file mode 100644 index 0000000..3113dcc --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/TestVariable.cmake @@ -0,0 +1,14 @@ +function(test_environment_variable name expected_value) + string(REGEX MATCH "${name}=([^\n]*)" REGEX_RESULT "${actual_stdout}") + if(NOT REGEX_RESULT) + string(APPEND RunCMake_TEST_FAILED "Environment variable '${name}' is not defined.\n") + else() + set(actual_value "${CMAKE_MATCH_1}") + if(NOT "${actual_value}" MATCHES "${expected_value}") + string(REPLACE "\n" "\n " _actual "${expected_value}") + string(REPLACE "\n" "\n " _expect "${actual_value}") + string(APPEND RunCMake_TEST_FAILED "Expected value of environment variable '${name}':\n ${_expect}\nActual value:\n ${_actual}\n") + endif() + endif() + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) +endfunction() diff --git a/Tests/RunCMake/CMakePresetsBuild/check.cmake b/Tests/RunCMake/CMakePresetsBuild/check.cmake new file mode 100644 index 0000000..e79c4f1 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsBuild/check.cmake @@ -0,0 +1,3 @@ +set(CMakePresets_VALIDATE_SCRIPT_PATH "${RunCMake_SOURCE_DIR}/../CMakePresets/validate_schema.py") +include("${RunCMake_SOURCE_DIR}/../CMakePresets/validate_schema.cmake") +include("${RunCMake_SOURCE_DIR}/../CMakePresets/check.cmake") diff --git a/Tests/RunCMake/CMakePresetsTest/CMakeLists.txt.in b/Tests/RunCMake/CMakePresetsTest/CMakeLists.txt.in new file mode 100644 index 0000000..129184a --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/CMakeLists.txt.in @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.19) +project("@CASE_NAME@" NONE) +include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake") diff --git a/Tests/RunCMake/CMakePresetsTest/Good-indexFile.txt b/Tests/RunCMake/CMakePresetsTest/Good-indexFile.txt new file mode 100644 index 0000000..4ac2314 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Good-indexFile.txt @@ -0,0 +1 @@ +2,,2 diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-config-debug-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-config-debug-stdout.txt new file mode 100644 index 0000000..c281c81 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-config-debug-stdout.txt @@ -0,0 +1,5 @@ +Test project [^ +]*/Tests/RunCMake/CMakePresetsTest/Good/build/default + Start 6: debug-only +.* +100% tests passed, 0 tests failed out of 1 diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-config-release-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-config-release-stdout.txt new file mode 100644 index 0000000..66bfd22 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-config-release-stdout.txt @@ -0,0 +1,5 @@ +Test project [^ +]*/Tests/RunCMake/CMakePresetsTest/Good/build/default + Start 6: release-only +.* +100% tests passed, 0 tests failed out of 1 diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-exclude-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-exclude-stdout.txt new file mode 100644 index 0000000..5e990de --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-exclude-stdout.txt @@ -0,0 +1,7 @@ +Test project [^ +]*/Tests/RunCMake/CMakePresetsTest/Good/build/default + Start 3: testc +.* + Start 4: testd +.* +100% tests passed, 0 tests failed out of 2 diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-index-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-index-stdout.txt new file mode 100644 index 0000000..2df6fcb --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-index-stdout.txt @@ -0,0 +1,7 @@ +Test project [^ +]*/Tests/RunCMake/CMakePresetsTest/Good/build/default + Start 1: testa +.* + Start 3: testc +.* +100% tests passed, 0 tests failed out of 2 diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-indexFile-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-indexFile-stdout.txt new file mode 100644 index 0000000..1366876 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-indexFile-stdout.txt @@ -0,0 +1,7 @@ +Test project [^ +]*/Tests/RunCMake/CMakePresetsTest/Good/build/default + Start 2: testb +.* + Start 4: testd +.* +100% tests passed, 0 tests failed out of 2 diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-noEnvironment-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-noEnvironment-stdout.txt new file mode 100644 index 0000000..8b4845e --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-noEnvironment-stdout.txt @@ -0,0 +1,8 @@ +Test project [^ +]*/Tests/RunCMake/CMakePresetsTest/Good/build/default +.* + Start 5: test-env +.* +5: TEST_ENV_REF=xx +.* +100% tests passed, 0 tests failed out of 1 diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-showOnly-stdout.txt b/Tests/RunCMake/CMakePresetsTest/Good-test-showOnly-stdout.txt new file mode 100644 index 0000000..67ddd4f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Good-test-showOnly-stdout.txt @@ -0,0 +1,8 @@ +Test project [^ +]*/Tests/RunCMake/CMakePresetsTest/Good/build/default + Test #1: testa + Test #2: testb + Test #3: testc + Test #4: testd + +Total Tests: 4 diff --git a/Tests/RunCMake/CMakePresetsTest/Good-withEnvironment-check.cmake b/Tests/RunCMake/CMakePresetsTest/Good-withEnvironment-check.cmake new file mode 100644 index 0000000..0c19556 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Good-withEnvironment-check.cmake @@ -0,0 +1,8 @@ +include("${RunCMake_SOURCE_DIR}/../CMakePresetsBuild/TestVariable.cmake") + +test_environment_variable("TEST_ENV" "Environment variable") +test_environment_variable("TEST_ENV_OVERRIDE" "Override") +test_environment_variable("TEST_ENV_OVERRIDE_REF" "xOverridex") +test_environment_variable("TEST_ENV_REF" "xEnvironment variablex") + +include("${RunCMake_SOURCE_DIR}/check.cmake") diff --git a/Tests/RunCMake/CMakePresetsTest/Good.cmake b/Tests/RunCMake/CMakePresetsTest/Good.cmake new file mode 100644 index 0000000..0a6cf57 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Good.cmake @@ -0,0 +1,27 @@ +enable_testing() +add_test(testa ${CMAKE_COMMAND} -E echo testa) +add_test(testb ${CMAKE_COMMAND} -E echo testb) +add_test(testc ${CMAKE_COMMAND} -E echo testc) +add_test(testd ${CMAKE_COMMAND} -E echo testd) + +set_tests_properties(testa testb testc testd + PROPERTIES LABELS "echo") +set_property(TEST testa testb + APPEND PROPERTY LABELS ab) +set_property(TEST testb testc + APPEND PROPERTY LABELS bc) +set_property(TEST testc testd + APPEND PROPERTY LABELS cd) + +add_test(test-env ${CMAKE_COMMAND} -E environment | sort) +set_tests_properties(test-env PROPERTIES LABELS "env") + +add_test(NAME debug-only + COMMAND ${CMAKE_COMMAND} -E echo debug-only + CONFIGURATIONS Debug) +add_test(NAME release-only + COMMAND ${CMAKE_COMMAND} -E echo release-only + CONFIGURATIONS Release) + +set_tests_properties(debug-only release-only + PROPERTIES LABELS "config") diff --git a/Tests/RunCMake/CMakePresetsTest/Good.json.in b/Tests/RunCMake/CMakePresetsTest/Good.json.in new file mode 100644 index 0000000..791d41f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Good.json.in @@ -0,0 +1,184 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build/${presetName}", + "environment": { + "TEST_ENV": "Environment variable", + "TEST_ENV_OVERRIDE": "Overridden environment variable" + } + } + ], + "buildPresets": [ + { + "name": "build-default-debug", + "configurePreset": "default", + "configuration": "Debug" + }, + { + "name": "build-default-release", + "inherits": "build-default-debug", + "configuration": "Release" + } + ], + "testPresets": [ + { + "name": "minimal", + "configurePreset": "default" + }, + { + "name": "defaults", + "hidden": false, + "inherits": [], + "vendor": {}, + "displayName": "", + "description": "", + "environment": {}, + "configurePreset": "default", + "inheritConfigureEnvironment": true, + "configuration": "", + "overwriteConfigurationFile": [], + "output": { + "shortProgress": false, + "verbosity": "default", + "debug": false, + "outputOnFailure": false, + "quiet": false, + "outputLogFile": "", + "labelSummary": true, + "subprojectSummary": true + // "maxPassedTestOutputSize": 0 + // "maxTestNameWidth": 0 + }, + "filter": { + "include": { + "name": "", + "label": "", + "useUnion": false, + "index": "" + // "index": { + // "start": 0, + // "end": 0, + // "stride": 0 + // } + }, + "exclude": { + "name": "", + "label": "", + "fixtures": { + "any": "", + "setup": "", + "cleanup": "" + } + } + }, + "execution": { + "stopOnFailure": false, + "enableFailover": false, + "jobs": 0, + "resourceSpecFile": "", + // "testLoad": 0, + "showOnly": "human", + "repeat": { + "mode": "until-pass", + "count": 1 + }, + "interactiveDebugging": false, + "scheduleRandom": false, + // "timeout": 0, + "noTestsAction": "default" + } + }, + { + "name": "noEnvironment", + "configurePreset": "default", + "inheritConfigureEnvironment": false, + "environment": { + "TEST_ENV_REF": "x$env{TEST_ENV}x" + }, + "filter": { + "include": { + "name": "test-env" + } + }, + "output": { + "verbosity": "verbose" + } + }, + { + "name": "withEnvironment", + "inherits": "noEnvironment", + "inheritConfigureEnvironment": true, + "environment": { + "TEST_ENV_OVERRIDE": "Override", + "TEST_ENV_OVERRIDE_REF": "x$env{TEST_ENV_OVERRIDE}x", + "TEST_ENV_REF": "x$env{TEST_ENV}x" + } + }, + { + "name": "config-debug", + "inherits": "minimal", + "configuration": "Debug", + "filter": { + "include": { + "label": "config" + } + } + }, + { + "name": "config-release", + "inherits": "minimal", + "configuration": "Release", + "filter": { + "include": { + "label": "config" + } + } + }, + { + "name": "exclude", + "inherits": "minimal", + "filter": { + "exclude": { + "name": "test-env", + "label": "(ab|config)" + } + } + }, + { + "name": "index", + "inherits": "minimal", + "filter": { + "include": { + "index": { + "end": 4, + "stride": 2 + } + } + } + }, + { + "name": "indexFile", + "inherits": "minimal", + "filter": { + "include": { + "index": "${sourceDir}/Good-indexFile.txt" + } + } + }, + { + "name": "showOnly", + "inherits": "minimal", + "filter": { + "include": { + "label": "echo" + } + }, + "execution": { + "showOnly": "human" + } + } + ] +} diff --git a/Tests/RunCMake/CMakePresetsTest/Invalid-test-badConfigurePreset-result.txt b/Tests/RunCMake/CMakePresetsTest/Invalid-test-badConfigurePreset-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Invalid-test-badConfigurePreset-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresetsTest/Invalid-test-badConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresetsTest/Invalid-test-badConfigurePreset-stderr.txt new file mode 100644 index 0000000..9cf9987 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Invalid-test-badConfigurePreset-stderr.txt @@ -0,0 +1,2 @@ +CMake Error: No such configure preset in [^ +]*/Tests/RunCMake/CMakePresetsTest/Invalid: "dne" diff --git a/Tests/RunCMake/CMakePresetsTest/Invalid-test-hidden-result.txt b/Tests/RunCMake/CMakePresetsTest/Invalid-test-hidden-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Invalid-test-hidden-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresetsTest/Invalid-test-hidden-stderr.txt b/Tests/RunCMake/CMakePresetsTest/Invalid-test-hidden-stderr.txt new file mode 100644 index 0000000..41b1b4e --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Invalid-test-hidden-stderr.txt @@ -0,0 +1,2 @@ +CMake Error: Cannot use hidden test preset in [^ +]*/Tests/RunCMake/CMakePresetsTest/Invalid: "hidden" diff --git a/Tests/RunCMake/CMakePresetsTest/Invalid-test-vendorMacro-result.txt b/Tests/RunCMake/CMakePresetsTest/Invalid-test-vendorMacro-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Invalid-test-vendorMacro-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresetsTest/Invalid-test-vendorMacro-stderr.txt b/Tests/RunCMake/CMakePresetsTest/Invalid-test-vendorMacro-stderr.txt new file mode 100644 index 0000000..ed5bb98 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Invalid-test-vendorMacro-stderr.txt @@ -0,0 +1 @@ +CMake Error: Could not evaluate test preset "vendorMacro": Invalid macro expansion diff --git a/Tests/RunCMake/CMakePresetsTest/Invalid.cmake b/Tests/RunCMake/CMakePresetsTest/Invalid.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Invalid.cmake diff --git a/Tests/RunCMake/CMakePresetsTest/Invalid.json.in b/Tests/RunCMake/CMakePresetsTest/Invalid.json.in new file mode 100644 index 0000000..c7096f2 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/Invalid.json.in @@ -0,0 +1,27 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build/${presetName}" + } + ], + "testPresets": [ + { + "name": "hidden", + "hidden": true + }, + { + "name": "vendorMacro", + "configurePreset": "default", + "environment": { + "TEST": "$vendor{bad.TEST}" + } + }, + { + "name": "badConfigurePreset", + "configurePreset": "dne" + } + ] +} diff --git a/Tests/RunCMake/CMakePresetsTest/ListPresets-test-x-stdout.txt b/Tests/RunCMake/CMakePresetsTest/ListPresets-test-x-stdout.txt new file mode 100644 index 0000000..46ffbcf --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/ListPresets-test-x-stdout.txt @@ -0,0 +1,12 @@ +Available test presets: + + "minimal" + "defaults" + "noEnvironment" + "withEnvironment" + "config-debug" + "config-release" + "exclude" + "index" + "indexFile" + "showOnly" diff --git a/Tests/RunCMake/CMakePresetsTest/ListPresets.cmake b/Tests/RunCMake/CMakePresetsTest/ListPresets.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/ListPresets.cmake diff --git a/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-result.txt b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt new file mode 100644 index 0000000..b167f68 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt @@ -0,0 +1,2 @@ +CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset: Invalid preset diff --git a/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset.cmake b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset.cmake diff --git a/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset.json.in b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset.json.in new file mode 100644 index 0000000..4591cc8 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset.json.in @@ -0,0 +1,15 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build/${presetName}" + } + ], + "testPresets": [ + { + "name": "noConfigurePreset" + } + ] +} diff --git a/Tests/RunCMake/CMakePresetsTest/NoTestsAction-test-noTestsAction-result.txt b/Tests/RunCMake/CMakePresetsTest/NoTestsAction-test-noTestsAction-result.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/NoTestsAction-test-noTestsAction-result.txt @@ -0,0 +1 @@ +8 diff --git a/Tests/RunCMake/CMakePresetsTest/NoTestsAction-test-noTestsAction-stderr.txt b/Tests/RunCMake/CMakePresetsTest/NoTestsAction-test-noTestsAction-stderr.txt new file mode 100644 index 0000000..eafba1c --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/NoTestsAction-test-noTestsAction-stderr.txt @@ -0,0 +1 @@ +No tests were found!!! diff --git a/Tests/RunCMake/CMakePresetsTest/NoTestsAction.cmake b/Tests/RunCMake/CMakePresetsTest/NoTestsAction.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/NoTestsAction.cmake diff --git a/Tests/RunCMake/CMakePresetsTest/NoTestsAction.json.in b/Tests/RunCMake/CMakePresetsTest/NoTestsAction.json.in new file mode 100644 index 0000000..20a50d6 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/NoTestsAction.json.in @@ -0,0 +1,19 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "default", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build/${presetName}" + } + ], + "testPresets": [ + { + "name": "noTestsAction", + "configurePreset": "default", + "execution": { + "noTestsAction": "error" + } + } + ] +} diff --git a/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported-test-x-result.txt b/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported-test-x-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported-test-x-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported-test-x-stderr.txt b/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported-test-x-stderr.txt new file mode 100644 index 0000000..eb0ec1a --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported-test-x-stderr.txt @@ -0,0 +1,2 @@ +CMake Error: Could not read presets from [^ +]*Tests/RunCMake/CMakePresetsTest/PresetsUnsupported: File version must be 2 or higher for build and test preset support. diff --git a/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported.json.in b/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported.json.in new file mode 100644 index 0000000..ff1b000 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported.json.in @@ -0,0 +1,7 @@ +{ + "version": 1, + "configurePresets": [ + ], + "testPresets": [ + ] +} diff --git a/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake new file mode 100644 index 0000000..6360fc2 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake @@ -0,0 +1,94 @@ +include(RunCMake) + +# Presets do not support legacy VS generator name architecture suffix. +if(RunCMake_GENERATOR MATCHES "^(Visual Studio [0-9]+ [0-9]+) ") + set(RunCMake_GENERATOR "${CMAKE_MATCH_1}") +endif() + +function(run_cmake_test_presets name CMakePresetsTest_CONFIGURE_PRESETS CMakePresetsTest_BUILD_PRESETS CMakePresetsTest_TEST_PRESETS) + set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/${name}") + set(RunCMake_TEST_BINARY_DIR "${RunCMake_TEST_SOURCE_DIR}/build") + set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}") + + set(RunCMake_TEST_NO_CLEAN TRUE) + + file(REMOVE_RECURSE "${RunCMake_TEST_SOURCE_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + + set(CASE_NAME "${name}") + set(CASE_SOURCE_DIR "${RunCMake_SOURCE_DIR}") + configure_file("${RunCMake_SOURCE_DIR}/CMakeLists.txt.in" "${RunCMake_TEST_SOURCE_DIR}/CMakeLists.txt" @ONLY) + + if(NOT CMakePresetsTest_FILE) + set(CMakePresetsTest_FILE "${RunCMake_SOURCE_DIR}/${name}.json.in") + endif() + if(EXISTS "${CMakePresetsTest_FILE}") + configure_file("${CMakePresetsTest_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" @ONLY) + endif() + + if(NOT CMakeUserPresets_FILE) + set(CMakeUserPresets_FILE "${RunCMake_SOURCE_DIR}/${name}User.json.in") + endif() + if(EXISTS "${CMakeUserPresets_FILE}") + configure_file("${CMakeUserPresets_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakeUserPresets.json" @ONLY) + endif() + + foreach(ASSET ${CMakePresetsTest_ASSETS}) + configure_file("${RunCMake_SOURCE_DIR}/${ASSET}" "${RunCMake_TEST_SOURCE_DIR}" COPYONLY) + endforeach() + + if (NOT CMakePresetsTest_NO_CONFIGURE) + foreach(CONFIGURE_PRESET ${CMakePresetsTest_CONFIGURE_PRESETS}) + run_cmake_command("${name}-configure-${CONFIGURE_PRESET}" + "${CMAKE_COMMAND}" "--preset" "${CONFIGURE_PRESET}") + endforeach() + endif() + + if (NOT CMakePresetsTest_NO_BUILD) + foreach(BUILD_PRESET ${CMakePresetsTest_BUILD_PRESETS}) + run_cmake_command("${name}-build-${BUILD_PRESET}" + "${CMAKE_COMMAND}" "--build" "--preset" "${BUILD_PRESET}") + endforeach() + endif() + + foreach(TEST_PRESET ${CMakePresetsTest_TEST_PRESETS}) + if (EXISTS "${RunCMake_SOURCE_DIR}/${name}-test-${TEST_PRESET}-check.cmake") + set(RunCMake-check-file "${name}-test-${TEST_PRESET}-check.cmake") + else() + set(RunCMake-check-file "check.cmake") + endif() + + run_cmake_command(${name}-test-${TEST_PRESET} + ${CMAKE_CTEST_COMMAND} "--preset" "${TEST_PRESET}" ${ARGN}) + endforeach() +endfunction() + +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) +set(CMakePresetsTest_NO_BUILD 1) + +set(CMakePresetsTest_ASSETS "Good-indexFile.txt") +set(GoodTestPresets + "minimal;defaults;noEnvironment;withEnvironment" + "config-debug;config-release" + "exclude;index;indexFile;showOnly") +run_cmake_test_presets(Good + "default" + "" + "${GoodTestPresets}") +unset(CMakePresetsTest_ASSETS) + +set(CMakePresetsTest_NO_CONFIGURE 1) +set(CMakePresetsTest_FILE "${RunCMake_SOURCE_DIR}/Good.json.in") +run_cmake_test_presets(ListPresets "" "" "x" "--list-presets") +unset(CMakePresetsTest_FILE) + +run_cmake_test_presets(NoConfigurePreset "" "" "noConfigurePreset") +run_cmake_test_presets(NoTestsAction "default" "" "noTestsAction") +run_cmake_test_presets(Invalid "" "" "hidden;vendorMacro;badConfigurePreset") + +set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) +run_cmake_test_presets(PresetsUnsupported "" "" "x") +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) +set(CMakePresetsTest_NO_CONFIGURE 0) + +set(CMakePresetsTest_NO_BUILD 0) diff --git a/Tests/RunCMake/CMakePresetsTest/check.cmake b/Tests/RunCMake/CMakePresetsTest/check.cmake new file mode 100644 index 0000000..e79c4f1 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsTest/check.cmake @@ -0,0 +1,3 @@ +set(CMakePresets_VALIDATE_SCRIPT_PATH "${RunCMake_SOURCE_DIR}/../CMakePresets/validate_schema.py") +include("${RunCMake_SOURCE_DIR}/../CMakePresets/validate_schema.cmake") +include("${RunCMake_SOURCE_DIR}/../CMakePresets/check.cmake") diff --git a/Tests/RunCMake/CommandLine/BuildDir--build--parallel-bad-number-stderr.txt b/Tests/RunCMake/CommandLine/BuildDir--build--parallel-bad-number-stderr.txt index e73d760..16f8be8 100644 --- a/Tests/RunCMake/CommandLine/BuildDir--build--parallel-bad-number-stderr.txt +++ b/Tests/RunCMake/CommandLine/BuildDir--build--parallel-bad-number-stderr.txt @@ -1,3 +1,3 @@ ^'--parallel' invalid number '12ab' given\. + -Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\] +Usage: cmake --build \[<dir> \| --preset <preset>\] \[options\] \[-- \[native-options\]\] diff --git a/Tests/RunCMake/CommandLine/BuildDir--build--parallel-large-stderr.txt b/Tests/RunCMake/CommandLine/BuildDir--build--parallel-large-stderr.txt index 94fc157..e7b9aaa 100644 --- a/Tests/RunCMake/CommandLine/BuildDir--build--parallel-large-stderr.txt +++ b/Tests/RunCMake/CommandLine/BuildDir--build--parallel-large-stderr.txt @@ -1,3 +1,3 @@ ^The <jobs> value is too large\. + -Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\] +Usage: cmake --build \[<dir> \| --preset <preset>\] \[options\] \[-- \[native-options\]\] diff --git a/Tests/RunCMake/CommandLine/BuildDir--build--parallel-no-space-bad-number-stderr.txt b/Tests/RunCMake/CommandLine/BuildDir--build--parallel-no-space-bad-number-stderr.txt index e73d760..16f8be8 100644 --- a/Tests/RunCMake/CommandLine/BuildDir--build--parallel-no-space-bad-number-stderr.txt +++ b/Tests/RunCMake/CommandLine/BuildDir--build--parallel-no-space-bad-number-stderr.txt @@ -1,3 +1,3 @@ ^'--parallel' invalid number '12ab' given\. + -Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\] +Usage: cmake --build \[<dir> \| --preset <preset>\] \[options\] \[-- \[native-options\]\] diff --git a/Tests/RunCMake/CommandLine/BuildDir--build--parallel-zero-stderr.txt b/Tests/RunCMake/CommandLine/BuildDir--build--parallel-zero-stderr.txt index 8ed4fee..d1241f4 100644 --- a/Tests/RunCMake/CommandLine/BuildDir--build--parallel-zero-stderr.txt +++ b/Tests/RunCMake/CommandLine/BuildDir--build--parallel-zero-stderr.txt @@ -1,3 +1,3 @@ ^The <jobs> value requires a positive integer argument\. + -Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\] +Usage: cmake --build \[<dir> \| --preset <preset>\] \[options\] \[-- \[native-options\]\] diff --git a/Tests/RunCMake/CommandLine/BuildDir--build-jobs-bad-number-stderr.txt b/Tests/RunCMake/CommandLine/BuildDir--build-jobs-bad-number-stderr.txt index c810087..d52b165 100644 --- a/Tests/RunCMake/CommandLine/BuildDir--build-jobs-bad-number-stderr.txt +++ b/Tests/RunCMake/CommandLine/BuildDir--build-jobs-bad-number-stderr.txt @@ -1,3 +1,3 @@ ^'-j' invalid number '12ab' given\. + -Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\] +Usage: cmake --build \[<dir> \| --preset <preset>\] \[options\] \[-- \[native-options\]\] diff --git a/Tests/RunCMake/CommandLine/BuildDir--build-jobs-large-stderr.txt b/Tests/RunCMake/CommandLine/BuildDir--build-jobs-large-stderr.txt index 94fc157..e7b9aaa 100644 --- a/Tests/RunCMake/CommandLine/BuildDir--build-jobs-large-stderr.txt +++ b/Tests/RunCMake/CommandLine/BuildDir--build-jobs-large-stderr.txt @@ -1,3 +1,3 @@ ^The <jobs> value is too large\. + -Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\] +Usage: cmake --build \[<dir> \| --preset <preset>\] \[options\] \[-- \[native-options\]\] diff --git a/Tests/RunCMake/CommandLine/BuildDir--build-jobs-no-space-bad-number-stderr.txt b/Tests/RunCMake/CommandLine/BuildDir--build-jobs-no-space-bad-number-stderr.txt index c810087..d52b165 100644 --- a/Tests/RunCMake/CommandLine/BuildDir--build-jobs-no-space-bad-number-stderr.txt +++ b/Tests/RunCMake/CommandLine/BuildDir--build-jobs-no-space-bad-number-stderr.txt @@ -1,3 +1,3 @@ ^'-j' invalid number '12ab' given\. + -Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\] +Usage: cmake --build \[<dir> \| --preset <preset>\] \[options\] \[-- \[native-options\]\] diff --git a/Tests/RunCMake/CommandLine/BuildDir--build-jobs-zero-stderr.txt b/Tests/RunCMake/CommandLine/BuildDir--build-jobs-zero-stderr.txt index 8ed4fee..d1241f4 100644 --- a/Tests/RunCMake/CommandLine/BuildDir--build-jobs-zero-stderr.txt +++ b/Tests/RunCMake/CommandLine/BuildDir--build-jobs-zero-stderr.txt @@ -1,3 +1,3 @@ ^The <jobs> value requires a positive integer argument\. + -Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\] +Usage: cmake --build \[<dir> \| --preset <preset>\] \[options\] \[-- \[native-options\]\] diff --git a/Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-first-stderr.txt b/Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-first-stderr.txt index 40d9bec..2dd9bc4 100644 --- a/Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-first-stderr.txt +++ b/Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-first-stderr.txt @@ -1,2 +1,2 @@ ^Error: Building 'clean' and other targets together is not supported\. -Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\] +Usage: cmake --build \[<dir> \| --preset <preset>\] \[options\] \[-- \[native-options\]\] diff --git a/Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-second-stderr.txt b/Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-second-stderr.txt index 40d9bec..2dd9bc4 100644 --- a/Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-second-stderr.txt +++ b/Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-second-stderr.txt @@ -1,2 +1,2 @@ ^Error: Building 'clean' and other targets together is not supported\. -Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\] +Usage: cmake --build \[<dir> \| --preset <preset>\] \[options\] \[-- \[native-options\]\] diff --git a/Tests/RunCMake/CommandLine/build-no-dir-stderr.txt b/Tests/RunCMake/CommandLine/build-no-dir-stderr.txt index 8d518f6..9b4d26b 100644 --- a/Tests/RunCMake/CommandLine/build-no-dir-stderr.txt +++ b/Tests/RunCMake/CommandLine/build-no-dir-stderr.txt @@ -1 +1 @@ -^Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\] +^Usage: cmake --build \[<dir> \| --preset <preset>\] \[options\] \[-- \[native-options\]\] |