diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index 5ccd4fa542..92a11e3a6c 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst @@ -723,3 +723,4 @@ See Also ^^^^^^^^ * :command:`add_custom_target` +* :command:`add_custom_rule` diff --git a/Help/command/add_custom_rule.rst b/Help/command/add_custom_rule.rst new file mode 100644 index 0000000000..0d44130569 --- /dev/null +++ b/Help/command/add_custom_rule.rst @@ -0,0 +1,402 @@ +add_custom_rule +--------------- + +.. versionadded:: 4.5 + +Add a custom template rule to the generated build system. + +Synopsis +^^^^^^^^ + +.. parsed-literal:: + `Generating Files`_ + add_custom_rule( `OUTPUT`_ [ ...] + COMMAND [...] + [...]) + + `Derived Rule`_ + add_custom_rule( `FROM_RULE`_ + [...]) + +Generating Files +^^^^^^^^^^^^^^^^ + +.. signature:: + add_custom_rule( OUTPUT [ ...] + COMMAND [...] + [...]) + :target: + OUTPUT + + Add a custom template rule to produce an output: + + .. code-block:: cmake + + add_custom_rule( OUTPUT [ ...] + COMMAND [...] + [COMMAND [...]] ... + [DEPENDS ...] + [BYPRODUCTS ...] + [DEPFILE ] + [CONFIGURATOR [FOR_FILE_SET ] + [FOR_SOURCE ]] + [GLOBAL]) + + This defines a template rule ```` to generate specified ``OUTPUT`` + file(s). Rule names defined in all uppercase are reserved for CMake's own + built-in rules. + + The association of source files with the template rule is done by creating + :ref:`file sets ` of type ````. For each file of the file + set, a :command:`custom command ` will be created in the + same directory as the target owning the file set and the output files of this + custom command will be declared as part of a file set attached to + the same target. The type of this file set, as well as its name, are + controlled by the :prop_rule:`OUTPUT_FILE_SET` rule property. This output + file set will have the same scope (``PRIVATE``, ``PUBLIC``, or ``INTERFACE``) + as the input file set. + + To parameterize the template, some patterns are defined which can be used as + part of the ``add_custom_rule`` arguments as well as the :ref:`rule's + properties `. These patterns will be + instantiated for each source file. The supported patterns are: + + .. note:: + + The instantiation of the patterns are done in the context of the directory + where the file set was created. + + These patterns cannot be changed by the functions specified by the + ``CONFIGURATOR`` option. + + ```` + Name of the rule used as template. + + ```` + Name of the target to which the file set of sources is attached. + + ```` + Name of the file set used for the rule instantiation. + + ```` + The value of the :variable:`CMAKE_SOURCE_DIR` variable. + + ```` + The value of the :variable:`CMAKE_BINARY_DIR` variable. + + ```` + The path to the source directory of the file set creation. + + ```` + The path to the binary directory of the file set creation. + + ```` + The full path of the current source file being processed. + + ```` + The directory of the current source file being processed. + + ```` + The file name of the current source file being processed. + + ```` + The stem name (i.e. without directory and extension) of the source file + being processed. + + ```` + Content, in this order, of the :prop_fs:`INCLUDE_DIRECTORIES` file set + property, :prop_sf:`INCLUDE_DIRECTORIES` source property, and + :prop_rule:`INCLUDE_DIRECTORIES` rule property. + + Because CMake is not aware of the tool involved by the rule, there is + no specific processing regarding this pattern. This is the user's + responsibility to format, using :manual:`generator expressions + `, the content of pattern to be compatible + with the tool. + + For example, if the tool requires the flag ``-inc:`` to identify an include + directory, the following can be specified as part of the ``COMMAND`` + option: + + .. code-block:: cmake + + $,PREPEND,-inc:> + + ```` + Content, in this order, of the :prop_rule:`COMPILE_DEFINITIONS` rule + property, :prop_sf:`COMPILE_DEFINITIONS` source property, and + :prop_fs:`COMPILE_DEFINITIONS` file set property. + + Because CMake is not aware of the tool involved by the rule, there is + no specific processing regarding this pattern. This is the user's + responsibility to format, using :manual:`generator expressions + `, the content of pattern to be compatible + with the tool. + + For example, if the tool requires the flag ``-def:`` to identify a compile + definition, the following can be specified as part of the ``COMMAND`` + option: + + .. code-block:: cmake + + $,PREPEND,-def:> + + ```` + Content, in this order, of the :prop_rule:`COMPILE_OPTIONS` rule property, + :prop_sf:`COMPILE_OPTIONS` source property, and:prop_fs:`COMPILE_OPTIONS` + file set property. + + The options, which have the same semantics as those of the + :command:`add_custom_command` command, are: + + ``OUTPUT`` + Specify the output files the command is expected to produce. + Each output file will be marked with the :prop_sf:`GENERATED` + source file property automatically. At least one ``OUTPUT`` must be given. + + ``COMMAND`` + Specify the command-line(s) to execute at build time. + At least one ``COMMAND`` must be given. + + ``DEPENDS`` + Specify files on which the command depends. + + ``BYPRODUCTS`` + Specify the files the command is expected to produce but whose + modification time may or may not be newer than the dependencies. + + ``DEPFILE`` + Specify a depfile which holds dependencies for the custom command. It is + usually emitted by the custom command itself. + + ``CONFIGURATOR`` + Specify one or two CMake functions which will be called at the generation + step, in the context of the file set directory, before the effective + instantiation and custom commands definition. + + .. note:: + + The rule properties are all read-only during the execution of the + configurators. Moreover, it is strongly discouraged to change the + target properties. + + ``FOR_FILE_SET`` + The specified function will be called once per file set. + The expected signature is the following: + + .. signature:: + configurator(rule target fileset outputFileset patterns) + + The arguments provide the names of the effective artifacts involved in + the current rule instantiation. + + The ``patterns`` argument holds the name of the variable which can be + used to enrich the list of patterns. The expected value is a + :ref:`semicolon-separated list ` of items having + the syntax ``PATTERN=VALUE`` or ``PATTERN=``. More precisely, each item + must match the regular expression ``(^[A-Z][A-Z0-9_]+)=(.*)$``. Items + which does not this regular expression will be ignored. + + ``FOR_SOURCE`` + The specified function will be called for each file of the file set. + The expected signature is the following: + + .. signature:: + configurator(rule target fileset outputFileset source patterns) + + The arguments provide the names of the effective artifacts involved in + the current rule instantiation. + + The ``patterns`` argument holds the name of the variable which can be + used to enrich the list of patterns. The expected value is a + :ref:`semicolon-separated list ` of items having + the syntax ``PATTERN=VALUE`` or ``PATTERN=``. More precisely, each item + must match the regular expression ``(^[A-Z][A-Z0-9_]+)=(.*)$``. Items + which does not this regular expression will be ignored. + + .. note:: + + The source configurator is evaluated after the file set one. So, the + changes done by it will overwrite any changes done by the file set + configurator. + + .. note:: + + Any patterns specified through The :prop_fs:`RULE_PATTERNS` file set + and :prop_sf:`_PATTERNS` source file properties will take + precedence over, respectively, the file set and the source configurators. + + ``GLOBAL`` + Make the rule name globally visible. Without this keyword, the rule will + only be visible in the directory where it was created as well as the + sub-directories. + +Example +======= + +Define a rule to compile swig files: + +.. code-block:: cmake + + function(fileset_configurator rule target fileset patterns) + # define pattern + set(${patterns} "OUTFILE_DIR=" PARENT_SCOPE) + endfunction() + + function(source_configurator rule target fileset source patterns) + # define flag to handle C++ + get_property(cxx SOURCE "${source}" TARGET_DIRECTORY "${target}" PROPERTY CPLUSPLUS) + if (cxx) + set_property(SOURCE "${source}" TARGET_DIRECTORY "${target}" + APPEND PROPERTY COMPILE_OPTIONS -c++) + endif() + endfunction() + + set(OUTFILE_EXT "$,TARGET_DIRECTORY:,CPLUSPLUS>>,.cxx,.c>") + set(SWIG_LANGUAGE "-$,TARGET:,LANGUAGE>>") + + add_custom_rule(swig + OUTPUT "/${OUTFILE_EXT}" + COMMAND ${SWIG_EXECUTABLE} "" + "/${OUTFILE_EXT}" + ${SWIG_LANGUAGE} + + CONFIGURATOR FOR_FILE_SET fileset_configurator FOR_SOURCE source_configurator) + +And, by defining a file set of type ``swig``, we can compile swig sources: + +.. code-block:: cmake + + add_library(swig_example) + + target_sources(swig_example PRIVATE FILE_SET swig_srcs TYPE swig + FILES file1.i file2.i) + # define the target language + set_property(FILE_SET swig_srcs TARGET swig_example PROPERTY LANGUAGE python) + # define swig c++ mode + set_property(SOURCE file1.i file2.i PROPERTY CPLUSPLUS ON) + +Derived Rule +^^^^^^^^^^^^ + +.. signature:: + add_custom_rule( FROM_RULE + [...]) + :target: + FROM_RULE + + Create a new template rule ```` inheriting a snapshot of all the + characteristics of the ````, including the properties except the + ``GLOBAL`` one. Rule names defined in all uppercase are reserved for CMake's + own built-in rules. + + .. code-block:: cmake + + add_custom_rule( FROM_RULE + [CONFIGURATOR [FOR_FILE_SET [CHAIN|OVERRIDE]] + [FOR_SOURCE [CHAIN|OVERRIDE]]] + [GLOBAL]) + + Properties attached to this new rule can be freely customized, independently + of the rule we inherited from. + + The options are: + + ``FROM_RULE`` + Specify the rule from which this new rule will inherit. + + ``CONFIGURATOR`` + Specify one or two CMake functions which will be called at the generation + step before the effective instantiation and custom commands definition. + + .. note:: + + The rule properties are all read-only during the execution of the + configurators. Moreover, it is strongly discouraged to change the + target properties. + + ``FOR_FILE_SET`` + The specified function will be called once per file set. + The expected signature is the following: + + .. signature:: + configurator(rule target fileset outputFileset patterns) + + The arguments provide the names of the effective artifacts involved in + the current rule instantiation. + + The ``patterns`` argument holds the name of the variable which can be + used to enrich the list of patterns. + + ``FOR_SOURCE`` + The specified function will be called for each file of the file set. + The expected signature is the following: + + .. signature:: + configurator(rule target fileset outputFileset source patterns) + + The arguments provide the names of the effective artifacts involved in + the current rule instantiation. + + The ``patterns`` argument holds the name of the variable which can be + used to enrich the list of patterns. + + For these two sub-options, there are two possible configurations: + + ``CHAIN`` + This ```` will be added to the already specified + configurators of inherited rules. Configurators will be called in order + of their rules' definition. + + ``OVERRIDE`` + The specified ```` will override any other already defined + configurators. This is the default. + + ``GLOBAL`` + Make the rule name globally visible. Without this keyword, the rule will + only be visible in the directory where it was created as well as the + sub-directories. + +Example +======= + +By reusing the previous defined rule ``swig``, we can provide a more simple way +to compile swig sources by creating a more specialized rule: + + +.. code-block:: cmake + + function(python_configurator rule target fileset outputFileset patterns) + # define target language + set_property(FILE_SET ${fileset} TARGET ${target} PROPERTY LANGUAGE python) + endfunction() + + function(cxx_configurator rule target fileset outputFileset source patterns) + # define swig c++ mode + set_property(SOURCE "${source}" TARGET_DIRECTORY ${target} PROPERTY CPLUSPLUS ON) + set_property(SOURCE "${source}" TARGET_DIRECTORY ${target} + APPEND PROPERTY COMPILE_OPTIONS -c++) + endfunction() + + add_custom_rule(swig_python FROM_RULE swig + CONFIGURATOR FOR_FILE_SET python_configurator CHAIN + FOR_SOURCE cxx_configurator OVERRIDE) + +Now, we can define a file set which does not need any specific settings. And +because the ``CHAIN`` option was specified for the file set configurator, the +pattern ```` will be defined as well. + +.. code-block:: cmake + + add_library(swig_example) + + target_sources(swig_example PRIVATE FILE_SET swig_srcs TYPE swig_python + FILES file1.i file2.i) + +See Also +^^^^^^^^ + +* :command:`set_property(RULE)` +* :command:`get_property(RULE)` +* :command:`target_sources` +* :command:`add_custom_command` diff --git a/Help/command/get_property.rst b/Help/command/get_property.rst index 5ce9e83c2f..cecab01aaf 100644 --- a/Help/command/get_property.rst +++ b/Help/command/get_property.rst @@ -8,6 +8,7 @@ Get a property. get_property( ] | + RULE | TARGET | FILE_SET TARGET | SOURCE @@ -39,6 +40,12 @@ It must be one of the following: .. versionadded:: 3.19 ```` may reference a binary directory. +``RULE`` + .. versionadded:: 4.5 + + Scope must name one existing rule in the current directory, created + by the :command:`add_custom_rule` command. + ``TARGET`` Scope must name one existing target. See also the :command:`get_target_property` command. diff --git a/Help/command/set_property.rst b/Help/command/set_property.rst index 5fe7825460..87c4b53e94 100644 --- a/Help/command/set_property.rst +++ b/Help/command/set_property.rst @@ -7,6 +7,7 @@ Set a named property in a given scope. set_property({GLOBAL | DIRECTORY [] | + RULE ... | TARGET ... | FILE_SET ... TARGET | SOURCE ... @@ -36,6 +37,12 @@ It must be one of the following: .. versionadded:: 3.19 ```` may reference a binary directory. +``RULE`` + .. versionadded:: 4.5 + + Scope may name zero or more existing rules in the current directory, created + by the :command:`add_custom_rule` command. + ``TARGET`` Scope may name zero or more existing targets. See also the :command:`set_target_properties` command. diff --git a/Help/manual/cmake-buildsystem.7.rst b/Help/manual/cmake-buildsystem.7.rst index 7b349d0a2a..761ce60efa 100644 --- a/Help/manual/cmake-buildsystem.7.rst +++ b/Help/manual/cmake-buildsystem.7.rst @@ -426,8 +426,18 @@ Acceptable file set types are: using the ``export`` keyword). This file set type may not have an ``INTERFACE`` scope except on ``IMPORTED`` targets. -The optional default file sets are named after their type. The target may not -be a custom target or, for ``HEADERS`` and ``CXX_MODULES`` types, a +```` + .. versionadded:: 4.5 + + Specifies sources which will be processed by the custom rule defined by the + :command:`add_custom_rule` command. + +For ``HEADERS``, ``SOURCES``, and ``CXX_MODULES`` types, the optional default +file sets are named after their type. + +For ```` types, the target may be a custom target. + +For ``HEADERS`` and ``CXX_MODULES`` types, the target may not be a :prop_tgt:`FRAMEWORK` target. Files in a ``PRIVATE`` or ``PUBLIC`` file set are marked as source files for diff --git a/Help/manual/cmake-commands.7.rst b/Help/manual/cmake-commands.7.rst index 8a25b62f9c..e9c971726b 100644 --- a/Help/manual/cmake-commands.7.rst +++ b/Help/manual/cmake-commands.7.rst @@ -80,6 +80,7 @@ These commands are available only in CMake projects. /command/add_compile_definitions /command/add_compile_options /command/add_custom_command + /command/add_custom_rule /command/add_custom_target /command/add_definitions /command/add_dependencies diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index a396ddbb88..1f575744d4 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -1449,6 +1449,26 @@ Configuration Expressions in ``...`` are evaluated using the custom command's "command config". With other generators, the content of ``...`` is evaluated normally. +Rule-Dependent Expressions +-------------------------- + +Rule Properties +^^^^^^^^^^^^^^^ + +These expressions look up the values of rule properties. + +.. genex:: $ + + .. versionadded:: 4.5 + + Value of the property ``prop`` on the rule ``rule``, or empty if + the property is not set. An error will be raised if the rule is not + known by CMake. + + This generator expression can only be used in the definition of a custom rule + (see :command:`add_custom_rule`). Moreover, ``rule`` parameter must be the + pattern ````. Any other value will raise an error. + Toolchain And Language Expressions ---------------------------------- diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 97d0073b0e..b9417560f7 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -88,6 +88,7 @@ Properties on Directories /prop_dir/RULE_LAUNCH_COMPILE /prop_dir/RULE_LAUNCH_CUSTOM /prop_dir/RULE_LAUNCH_LINK + /prop_dir/RULES /prop_dir/SOURCE_DIR /prop_dir/SUBDIRECTORIES /prop_dir/SYSTEM @@ -99,6 +100,39 @@ Properties on Directories /prop_dir/VS_SOLUTION_ITEMS /prop_dir/VS_STARTUP_PROJECT +.. _`Rule Properties`: + +Properties on Rules +=================== + +.. toctree:: + :maxdepth: 1 + + /prop_rule/BYPRODUCTS + /prop_rule/COMMAND + /prop_rule/COMMAND_INDEX + /prop_rule/COMMAND_COUNT + /prop_rule/COMMAND_EXPAND_LISTS + /prop_rule/COMMENT + /prop_rule/COMPILE_DEFINITIONS + /prop_rule/COMPILE_OPTIONS + /prop_rule/DEPENDS_EXPLICIT_ONLY + /prop_rule/DEPENDS + /prop_rule/DEPFILE + /prop_rule/FILE_SET_CONFIGURATORS + /prop_rule/GLOBAL + /prop_rule/INCLUDE_DIRECTORIES + /prop_rule/JOB_POOL_COMPILE + /prop_rule/JOB_SERVER_AWARE + /prop_rule/NAME + /prop_rule/OUTPUT + /prop_rule/OUTPUT_FILE_SET + /prop_rule/PARENT_RULE + /prop_rule/SOURCE_CONFIGURATORS + /prop_rule/USES_TERMINAL + /prop_rule/VERBATIM + /prop_rule/WORKING_DIRECTORY + .. _`Target Properties`: Properties on Targets @@ -555,6 +589,7 @@ Properties on File Sets /prop_fs/INTERFACE_COMPILE_OPTIONS /prop_fs/INTERFACE_INCLUDE_DIRECTORIES /prop_fs/INTERFACE_SOURCES + /prop_fs/RULE_PATTERNS /prop_fs/SCOPE /prop_fs/SKIP_LINTING /prop_fs/SKIP_PRECOMPILE_HEADERS @@ -632,6 +667,7 @@ Properties on Source Files /prop_sf/OBJECT_DEPENDS /prop_sf/OBJECT_NAME /prop_sf/OBJECT_OUTPUTS + /prop_sf/RULE_PATTERNS /prop_sf/Rust_EMIT /prop_sf/SKIP_AUTOGEN /prop_sf/SKIP_AUTOMOC diff --git a/Help/prop_dir/RULES.rst b/Help/prop_dir/RULES.rst new file mode 100644 index 0000000000..a2c57fb9c3 --- /dev/null +++ b/Help/prop_dir/RULES.rst @@ -0,0 +1,12 @@ +RULES +----- + +.. versionadded:: 4.5 + +This read-only directory property contains a +:ref:`semicolon-separated list ` of +rules added in the directory by calls to the :command:`add_custom_rule` +command. +Each entry in the list is the logical name of a rule, suitable +to pass to the :command:`get_property` command ``RULE`` option +when called in the same directory. diff --git a/Help/prop_fs/COMPILE_DEFINITIONS.rst b/Help/prop_fs/COMPILE_DEFINITIONS.rst index bc400bf66d..23fbf0143e 100644 --- a/Help/prop_fs/COMPILE_DEFINITIONS.rst +++ b/Help/prop_fs/COMPILE_DEFINITIONS.rst @@ -5,12 +5,11 @@ COMPILE_DEFINITIONS Preprocessor definitions for compiling a source file. -The ``COMPILE_DEFINITIONS`` property may be set to a semicolon-separated -list of preprocessor definitions using the syntax ``VAR`` or ``VAR=value``. -Function-style definitions are not supported. CMake will -automatically escape the value correctly for the native build system -(note that CMake language syntax may require escapes to specify some -values). +The ``COMPILE_DEFINITIONS`` property may be set to a :ref:`semicolon-separated +list ` of preprocessor definitions using the syntax +``VAR`` or ``VAR=value``. Function-style definitions are not supported. CMake +will automatically escape the value correctly for the native build system (note +that CMake language syntax may require escapes to specify some values). CMake will automatically drop some definitions that are not supported by the native build tool. :generator:`Xcode` does not support diff --git a/Help/prop_fs/INCLUDE_DIRECTORIES.rst b/Help/prop_fs/INCLUDE_DIRECTORIES.rst index fd940ec4bd..4f983db122 100644 --- a/Help/prop_fs/INCLUDE_DIRECTORIES.rst +++ b/Help/prop_fs/INCLUDE_DIRECTORIES.rst @@ -5,11 +5,11 @@ INCLUDE_DIRECTORIES List of preprocessor include file search directories. -This property holds a :ref:`semicolon-separated list ` of paths -and will be added to the list of include directories when the sources of this -file set are built. These directories will take precedence over directories -defined at target level and source level except for :generator:`Xcode` -generator due to technical limitations. +This property holds a :ref:`semicolon-separated list ` of +paths and will be added to the list of include directories when the sources of +this file set are built. These directories will take precedence over +directories defined at target level and source level except for +:generator:`Xcode` generator due to technical limitations. Relative paths should not be added to this property directly. diff --git a/Help/prop_fs/JOB_POOL_COMPILE.rst b/Help/prop_fs/JOB_POOL_COMPILE.rst index b460affd33..e90b778ab4 100644 --- a/Help/prop_fs/JOB_POOL_COMPILE.rst +++ b/Help/prop_fs/JOB_POOL_COMPILE.rst @@ -24,5 +24,6 @@ This property is undefined by default. See Also ^^^^^^^^ +* :prop_rule:`JOB_POOL_COMPILE` rule property * :prop_sf:`JOB_POOL_COMPILE` source file property * :prop_tgt:`JOB_POOL_COMPILE` target property diff --git a/Help/prop_fs/RULE_PATTERNS.rst b/Help/prop_fs/RULE_PATTERNS.rst new file mode 100644 index 0000000000..815c22495e --- /dev/null +++ b/Help/prop_fs/RULE_PATTERNS.rst @@ -0,0 +1,27 @@ +RULE_PATTERNS +------------- + +.. versionadded:: 4.5 + +Patterns specification for instantiating a rule. + +The ``RULE_PATTERNS`` property may be set to a +:ref:`semicolon-separated list ` of patterns using the +syntax ``PATTERN=VALUE`` or ``PATTERN=``. More precisely, each item must match +the regular expression ``(^[A-Z][A-Z0-9_]+)=(.*)$``. + +CMake will automatically drop any patterns which do not match against this +regular expression. + +The list is ordered, so a pattern can use in its definition a previously +defined pattern. In the following example, +``OUTPUT_DIR=/some/path;OUTPUT_FILE=/my_file``, when the pattern +```` is expanded, the pattern ```` is already known. + +Related properties: + +* :prop_sf:`_PATTERNS` to specify patterns for a specific file. + +Related commands: + +* :command:`add_custom_rule` for custom rule specification. diff --git a/Help/prop_rule/BYPRODUCTS.rst b/Help/prop_rule/BYPRODUCTS.rst new file mode 100644 index 0000000000..45cfa28798 --- /dev/null +++ b/Help/prop_rule/BYPRODUCTS.rst @@ -0,0 +1,9 @@ +BYPRODUCTS +---------- + +.. versionadded:: 4.5 + +Read-only property giving a +:ref:`semicolon-separated list ` of the produced +artifacts, if any, by the rule as specified by the ``BYPRODUCTS`` option of the +:command:`add_custom_rule` command. diff --git a/Help/prop_rule/COMMAND.rst b/Help/prop_rule/COMMAND.rst new file mode 100644 index 0000000000..aaffe5da14 --- /dev/null +++ b/Help/prop_rule/COMMAND.rst @@ -0,0 +1,12 @@ +COMMAND +------- + +.. versionadded:: 4.5 + +Read-only property giving a +:ref:`semicolon-separated list ` of the arguments of the +rule as specified by the first ``COMMAND`` option of the +:command:`add_custom_rule` command. + +This is equivalent to the :prop_rule:`COMMAND_` rule property with the +index ``0``. diff --git a/Help/prop_rule/COMMAND_COUNT.rst b/Help/prop_rule/COMMAND_COUNT.rst new file mode 100644 index 0000000000..99d97c9afe --- /dev/null +++ b/Help/prop_rule/COMMAND_COUNT.rst @@ -0,0 +1,10 @@ +COMMAND_COUNT +------------- + +.. versionadded:: 4.5 + +Read-only property giving the count of ``COMMAND`` options of the +:command:`add_custom_rule` command. + +To retrieve the ````th ``COMMAND``, use the :prop_rule:`COMMAND_` +rule property. diff --git a/Help/prop_rule/COMMAND_EXPAND_LISTS.rst b/Help/prop_rule/COMMAND_EXPAND_LISTS.rst new file mode 100644 index 0000000000..4b024468b0 --- /dev/null +++ b/Help/prop_rule/COMMAND_EXPAND_LISTS.rst @@ -0,0 +1,11 @@ +COMMAND_EXPAND_LISTS +-------------------- + +.. versionadded:: 4.5 + +``COMMAND_EXPAND_LISTS`` is a boolean specifying that the lists in the +``COMMAND`` arguments of the :command:`add_custom_rule` command will be +expanded, including those created with +:manual:`generator expressions `. + +By default, ``COMMAND_EXPAND_LISTS`` is true. diff --git a/Help/prop_rule/COMMAND_INDEX.rst b/Help/prop_rule/COMMAND_INDEX.rst new file mode 100644 index 0000000000..3016e963bd --- /dev/null +++ b/Help/prop_rule/COMMAND_INDEX.rst @@ -0,0 +1,16 @@ +COMMAND_ +--------------- + +.. versionadded:: 4.5 + +Read-only property giving a +:ref:`semicolon-separated list ` of the arguments of the +rule as specified by the ````th ``COMMAND`` option of the +:command:`add_custom_rule` command. The index range is starting at ``0``. If +the index specified is out of the range of commands, ``NOTFOUND`` is returned. + +The :prop_rule:`COMMAND` rule property can be used as a shorthand to the +``COMMAND_0`` rule property. + +The number of commands can be retrieve using the :prop_rule:`COMMAND_COUNT` +rule property. diff --git a/Help/prop_rule/COMMENT.rst b/Help/prop_rule/COMMENT.rst new file mode 100644 index 0000000000..497f3ad12e --- /dev/null +++ b/Help/prop_rule/COMMENT.rst @@ -0,0 +1,8 @@ +COMMENT +------- + +.. versionadded:: 4.5 + +Display the given message before the commands are executed at build time. +Arguments to ``COMMENT`` may use +:manual:`generator expressions `. diff --git a/Help/prop_rule/COMPILE_DEFINITIONS.rst b/Help/prop_rule/COMPILE_DEFINITIONS.rst new file mode 100644 index 0000000000..1268cfc4a3 --- /dev/null +++ b/Help/prop_rule/COMPILE_DEFINITIONS.rst @@ -0,0 +1,24 @@ +COMPILE_DEFINITIONS +------------------- + +.. versionadded:: 4.5 + +Preprocessor definitions for compiling the file set's sources associated with +this rule. + +The ``COMPILE_DEFINITIONS`` property may be set to a +:ref:`semicolon-separated list ` of preprocessor +definitions using the syntax ``VAR`` or ``VAR=value``. Function-style +definitions are not supported. CMake will automatically escape the value +correctly for the native build system (note that CMake language syntax may +require escapes to specify some values). + +CMake will automatically drop definitions that are not supported +by the native build tool. + +.. include:: /include/COMPILE_DEFINITIONS_DISCLAIMER.rst + +Contents of ``COMPILE_DEFINITIONS`` may use :manual:`generator expressions +` with the syntax ``$<...>``. See the +:manual:`cmake-buildsystem(7)` manual for more on defining buildsystem +properties. diff --git a/Help/prop_rule/COMPILE_OPTIONS.rst b/Help/prop_rule/COMPILE_OPTIONS.rst new file mode 100644 index 0000000000..2729101b47 --- /dev/null +++ b/Help/prop_rule/COMPILE_OPTIONS.rst @@ -0,0 +1,15 @@ +COMPILE_OPTIONS +--------------- + +List of options to pass to the compiler. + +This property holds a :ref:`semicolon-separated list ` +of options specified so far for its rule. Use the +:command:`set_property(RULE)` command to append more options. + +Contents of ``COMPILE_OPTIONS`` may use :manual:`generator expressions +` with the syntax ``$<...>``. See the +:manual:`cmake-buildsystem(7)` manual for more on defining buildsystem +properties. + +.. include:: ../command/include/OPTIONS_SHELL.rst diff --git a/Help/prop_rule/DEPENDS.rst b/Help/prop_rule/DEPENDS.rst new file mode 100644 index 0000000000..67907f4e7d --- /dev/null +++ b/Help/prop_rule/DEPENDS.rst @@ -0,0 +1,9 @@ +DEPENDS +------- + +.. versionadded:: 4.5 + +Read-only property giving a +:ref:`semicolon-separated list ` of the dependencies, if +any, of the rule as specified by the ``DEPENDS`` option of the +:command:`add_custom_rule` command. diff --git a/Help/prop_rule/DEPENDS_EXPLICIT_ONLY.rst b/Help/prop_rule/DEPENDS_EXPLICIT_ONLY.rst new file mode 100644 index 0000000000..2f076bda2c --- /dev/null +++ b/Help/prop_rule/DEPENDS_EXPLICIT_ONLY.rst @@ -0,0 +1,11 @@ +DEPENDS_EXPLICIT_ONLY +--------------------- + +.. versionadded:: 4.5 + +``DEPENDS_EXPLICIT_ONLY`` is a boolean indicating that the rule's ``DEPENDS`` +argument represents all files required by the command and implicit dependencies +are not required. + +If not defined, the :variable:`CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY` +will be used during the instantiations of the rule. diff --git a/Help/prop_rule/DEPFILE.rst b/Help/prop_rule/DEPFILE.rst new file mode 100644 index 0000000000..4dd14e9419 --- /dev/null +++ b/Help/prop_rule/DEPFILE.rst @@ -0,0 +1,7 @@ +DEPFILE +------- + +.. versionadded:: 4.5 + +Read-only property giving the dependency file, if any, of the rule as specified +by the ``DEPFILE`` option of the :command:`add_custom_rule` command. diff --git a/Help/prop_rule/FILE_SET_CONFIGURATORS.rst b/Help/prop_rule/FILE_SET_CONFIGURATORS.rst new file mode 100644 index 0000000000..b2ccfd4783 --- /dev/null +++ b/Help/prop_rule/FILE_SET_CONFIGURATORS.rst @@ -0,0 +1,9 @@ +FILE_SET_CONFIGURATORS +---------------------- + +.. versionadded:: 4.5 + +Read-only property giving a +:ref:`semicolon-separated list ` of the file set +configurators, as specified by ``CONFIGURATOR FOR_FILE_SET`` option, given in +the order of their evaluation. diff --git a/Help/prop_rule/GLOBAL.rst b/Help/prop_rule/GLOBAL.rst new file mode 100644 index 0000000000..55852dc6aa --- /dev/null +++ b/Help/prop_rule/GLOBAL.rst @@ -0,0 +1,20 @@ +GLOBAL +------ + +.. versionadded:: 4.5 + +Indication of whether a rule is globally visible. + +The boolean value of this property is true for rules created with the +``GLOBAL`` options to :command:`add_custom_rule()`. + +For rules created without the additional option ``GLOBAL`` this is false. +However, setting this property to true promotes that rule to global scope. This +promotion can only be done in the same directory where the rule was created. + +.. note:: + + Once an rule has been made global, it cannot be changed back to + non-global. Therefore, if a project sets this property, it may only + provide a value of true. CMake will issue an error if the project tries to + set the property to a non-true value, even if the value was already false. diff --git a/Help/prop_rule/INCLUDE_DIRECTORIES.rst b/Help/prop_rule/INCLUDE_DIRECTORIES.rst new file mode 100644 index 0000000000..791693fc0e --- /dev/null +++ b/Help/prop_rule/INCLUDE_DIRECTORIES.rst @@ -0,0 +1,20 @@ +INCLUDE_DIRECTORIES +------------------- + +.. versionadded:: 4.5 + +List of preprocessor include file search directories. + +The ``INCLUDE_DIRECTORIES`` property may be set to a +:ref:`semicolon-separated list ` of directories given so +far to the :command:`set_property(RULE)` command. + +The value of this property is used by the rule definitions to set the include +paths for the compiler. + +Relative paths should not be added to this property. + +Contents of ``INCLUDE_DIRECTORIES`` may use :manual:`generator expressions +` with the syntax ``$<...>``. See the +:manual:`cmake-buildsystem(7)` manual for more on defining buildsystem +properties. diff --git a/Help/prop_rule/JOB_POOL_COMPILE.rst b/Help/prop_rule/JOB_POOL_COMPILE.rst new file mode 100644 index 0000000000..adeac883f1 --- /dev/null +++ b/Help/prop_rule/JOB_POOL_COMPILE.rst @@ -0,0 +1,17 @@ +JOB_POOL_COMPILE +---------------- + +.. versionadded:: 4.5 + +:ref:`Ninja only `: Pool used for compiling. + +The number of parallel compile processes for a rule may be limited by defining +pools with the global :prop_gbl:`JOB_POOLS` property and then specifying the +pool to use. + +See Also +^^^^^^^^ + +* :prop_tgt:`JOB_POOL_COMPILE` target property +* :prop_fs:`JOB_POOL_COMPILE` file set property +* :prop_sf:`JOB_POOL_COMPILE` source file property diff --git a/Help/prop_rule/JOB_SERVER_AWARE.rst b/Help/prop_rule/JOB_SERVER_AWARE.rst new file mode 100644 index 0000000000..f84bb58c03 --- /dev/null +++ b/Help/prop_rule/JOB_SERVER_AWARE.rst @@ -0,0 +1,15 @@ +JOB_SERVER_AWARE +---------------- + +.. versionadded:: 4.5 + +``JOB_SERVER_AWARE`` is a boolean specifying that the commands produced by the +instantiation of the rule are GNU Make job server aware. + +For the :generator:`Unix Makefiles`, :generator:`MSYS Makefiles`, and +:generator:`MinGW Makefiles` generators this will add the ``+`` prefix to the +recipe line. See the `GNU Make Documentation`_ for more information. + +This option is ignored by other generators. + +.. _`GNU Make Documentation`: https://www.gnu.org/software/make/manual/html_node/MAKE-Variable.html diff --git a/Help/prop_rule/NAME.rst b/Help/prop_rule/NAME.rst new file mode 100644 index 0000000000..2bb3904aba --- /dev/null +++ b/Help/prop_rule/NAME.rst @@ -0,0 +1,6 @@ +NAME +---- + +.. versionadded:: 4.5 + +Read-only property giving the name of the rule. diff --git a/Help/prop_rule/OUTPUT.rst b/Help/prop_rule/OUTPUT.rst new file mode 100644 index 0000000000..f68da85bf1 --- /dev/null +++ b/Help/prop_rule/OUTPUT.rst @@ -0,0 +1,9 @@ +OUTPUT +------ + +.. versionadded:: 4.5 + +Read-only property giving a +:ref:`semicolon-separated list ` of outputs of the rule +as specified by the ``OUTPUT`` option of the :command:`add_custom_rule` +command. diff --git a/Help/prop_rule/OUTPUT_FILE_SET.rst b/Help/prop_rule/OUTPUT_FILE_SET.rst new file mode 100644 index 0000000000..a3c077aeb1 --- /dev/null +++ b/Help/prop_rule/OUTPUT_FILE_SET.rst @@ -0,0 +1,23 @@ +OUTPUT_FILE_SET +--------------- + +.. versionadded:: 4.5 + +Specify the name and the type of the file set storing the files produced by the +instantiation of a rule. + +The ``OUTPUT_FILE_SET`` property must hold a +:ref:`semicolon-separated list ` of two elements +specifying the name of the file set and the type. + +The name can use the following patterns to enable the production of unique +names for the output file set: + +* ````: name of the rule +* ````: name of the target +* ````: name of the file set + +The type must be one of the :ref:`predefined types `. + +This property gets the following default value: +``__cmake_rule____outputs;SOURCES``. diff --git a/Help/prop_rule/PARENT_RULE.rst b/Help/prop_rule/PARENT_RULE.rst new file mode 100644 index 0000000000..f05ae7bfa8 --- /dev/null +++ b/Help/prop_rule/PARENT_RULE.rst @@ -0,0 +1,8 @@ +PARENT_RULE +----------- + +.. versionadded:: 4.5 + +Read-only property giving the name of the parent rule for rules created by +:command:`add_custom_rule(FROM_RULE)` command. This is an empty string for +other rules. diff --git a/Help/prop_rule/SOURCE_CONFIGURATORS.rst b/Help/prop_rule/SOURCE_CONFIGURATORS.rst new file mode 100644 index 0000000000..86cc91c99c --- /dev/null +++ b/Help/prop_rule/SOURCE_CONFIGURATORS.rst @@ -0,0 +1,9 @@ +SOURCE_CONFIGURATORS +-------------------- + +.. versionadded:: 4.5 + +Read-only property giving a +:ref:`semicolon-separated list ` of the source +configurators, as specified by ``CONFIGURATOR FOR_SOURCE`` option, given in +the order of their evaluation. diff --git a/Help/prop_rule/USES_TERMINAL.rst b/Help/prop_rule/USES_TERMINAL.rst new file mode 100644 index 0000000000..f64a4d2555 --- /dev/null +++ b/Help/prop_rule/USES_TERMINAL.rst @@ -0,0 +1,9 @@ +USES_TERMINAL +------------- + +.. versionadded:: 4.5 + +``USES_TERMINAL`` is a boolean which request that the commands produced by the +instantiation of the rule will be given direct access to the terminal if +possible. With the :ref:`Ninja Generators`, this places the command in the +``console`` :prop_gbl:`pool `. diff --git a/Help/prop_rule/VERBATIM.rst b/Help/prop_rule/VERBATIM.rst new file mode 100644 index 0000000000..b011a8439c --- /dev/null +++ b/Help/prop_rule/VERBATIM.rst @@ -0,0 +1,15 @@ +VERBATIM +-------- + +.. versionadded:: 4.5 + +``VERBATIM`` is a boolean indicating that all arguments to the commands +produced by the instantiation of the rule will be escaped properly for the +build tool so that the invoked command receives each argument unchanged. Note +that one level of escapes is still used by the CMake language processor before +:command:`add_custom_command` command even sees the arguments. + +By default, ``VERBATIM`` has a true value because it is recommended as it +enables correct behavior. When ``VERBATIM`` is not given the behavior is +platform specific because there is no protection of tool-specific special +characters. diff --git a/Help/prop_rule/WORKING_DIRECTORY.rst b/Help/prop_rule/WORKING_DIRECTORY.rst new file mode 100644 index 0000000000..0414b4be7e --- /dev/null +++ b/Help/prop_rule/WORKING_DIRECTORY.rst @@ -0,0 +1,13 @@ +WORKING_DIRECTORY +----------------- + +.. versionadded:: 4.5 + +Execute the commands produced by the instantiation of the rule with the given +current working directory. If it is a relative path, it will be interpreted +relative to the build tree directory corresponding to the current source +directory of the target. If not specified, the default value is the build +directory corresponding to the current source directory of the target. + +Arguments to ``WORKING_DIRECTORY`` may use +:manual:`generator expressions `. diff --git a/Help/prop_sf/JOB_POOL_COMPILE.rst b/Help/prop_sf/JOB_POOL_COMPILE.rst index 2c9a8d4683..3ad9c17267 100644 --- a/Help/prop_sf/JOB_POOL_COMPILE.rst +++ b/Help/prop_sf/JOB_POOL_COMPILE.rst @@ -23,5 +23,6 @@ This property is undefined by default. See Also ^^^^^^^^ +* :prop_rule:`JOB_POOL_COMPILE` rule property * :prop_fs:`JOB_POOL_COMPILE` file set property * :prop_tgt:`JOB_POOL_COMPILE` target property diff --git a/Help/prop_sf/RULE_PATTERNS.rst b/Help/prop_sf/RULE_PATTERNS.rst new file mode 100644 index 0000000000..b5d9e6a49c --- /dev/null +++ b/Help/prop_sf/RULE_PATTERNS.rst @@ -0,0 +1,27 @@ +_PATTERNS +--------------- + +.. versionadded:: 4.5 + +Patterns specification for instantiating the rule ````. + +The ``_PATTERNS`` property may be set to a +:ref:`semicolon-separated list ` of patterns using the +syntax ``PATTERN=VALUE`` or ``PATTERN=``. More precisely, each item must match +the regular expression ``(^[A-Z][A-Z0-9_]+)=(.*)$``. + +CMake will automatically drop any patterns which do not match against this +regular expression. + +The list is ordered, so a pattern can use in its definition a previously +defined pattern. In the following example, +``OUTPUT_DIR=/some/path;OUTPUT_FILE=/my_file``, when the pattern +```` is expanded, the pattern ```` is already known. + +Related properties: + +* :prop_fs:`RULE_PATTERNS` to specify patterns for a file set. + +Related commands: + +* :command:`add_custom_rule` for custom rule specification. diff --git a/Help/prop_tgt/JOB_POOL_COMPILE.rst b/Help/prop_tgt/JOB_POOL_COMPILE.rst index d70b010a2a..f1cb1ecf08 100644 --- a/Help/prop_tgt/JOB_POOL_COMPILE.rst +++ b/Help/prop_tgt/JOB_POOL_COMPILE.rst @@ -19,5 +19,6 @@ This property is initialized by the value of See Also ^^^^^^^^ +* :prop_rule:`JOB_POOL_COMPILE` rule property * :prop_fs:`JOB_POOL_COMPILE` file set property * :prop_sf:`JOB_POOL_COMPILE` source file property diff --git a/Help/release/dev/add_custom_rule.rst b/Help/release/dev/add_custom_rule.rst new file mode 100644 index 0000000000..cd757ea579 --- /dev/null +++ b/Help/release/dev/add_custom_rule.rst @@ -0,0 +1,5 @@ +add_custom_rule +--------------- + +* The :command:`add_custom_rule` command was added to allow the definition of + pattern rules. diff --git a/Help/release/dev/genex-rule_property.rst b/Help/release/dev/genex-rule_property.rst new file mode 100644 index 0000000000..fddb58c1ab --- /dev/null +++ b/Help/release/dev/genex-rule_property.rst @@ -0,0 +1,5 @@ +genex-rule_property +------------------- + +* CMake gains the :genex:`RULE_PROPERTY` generator expression to query + properties of rules created by :command:`add_custom_rule` command. diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 9ab4d3931f..ce6b1c1383 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -163,6 +163,10 @@ add_library( cmCryptoHash.h cmCurl.cxx cmCurl.h + cmRule.cxx + cmRule.h + cmCustomRule.cxx + cmSpecializedRule.cxx cmCustomCommand.cxx cmCustomCommand.h cmCustomCommandGenerator.cxx @@ -320,6 +324,8 @@ add_library( cmGeneratorFileSet.h cmGeneratorFileSets.cxx cmGeneratorFileSets.h + cmGeneratorRule.cxx + cmGeneratorRule.h cmGeneratorTarget.cxx cmGeneratorTarget.h cmGeneratorTarget_CompatibleInterface.cxx @@ -585,6 +591,8 @@ add_library( cmAddCustomCommandCommand.h cmAddCustomTargetCommand.cxx cmAddCustomTargetCommand.h + cmAddCustomRuleCommand.cxx + cmAddCustomRuleCommand.h cmAddDefinitionsCommand.cxx cmAddDefinitionsCommand.h cmAddDependenciesCommand.cxx diff --git a/Source/cmAddCustomRuleCommand.cxx b/Source/cmAddCustomRuleCommand.cxx new file mode 100644 index 0000000000..d6bb62b45d --- /dev/null +++ b/Source/cmAddCustomRuleCommand.cxx @@ -0,0 +1,445 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmAddCustomRuleCommand.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "cmsys/RegularExpression.hxx" + +#include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" +#include "cmExecutionStatus.h" +#include "cmGeneratorExpression.h" +#include "cmMakefile.h" +#include "cmRange.h" +#include "cmRule.h" +#include "cmState.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +namespace { +bool IsReservedName(std::string const& name) +{ + static cmsys::RegularExpression reservedNameValidator("^[A-Z_.:+-]+$"); + + return reservedNameValidator.find(name); +} + +template +class FromRuleArgumentParser : public cmArgumentParser +{ +public: + FromRuleArgumentParser() + : cmArgumentParser() + { + this->Bind("CHAIN"_s, &Result::Chain) + .Bind("OVERRIDE"_s, &Result::Override) + .BindParsedKeywords(&Result::ParsedKeywords); + } +}; +} + +bool cmAddCustomRuleCommand(std::vector const& args, + cmExecutionStatus& status) +{ + if (args.size() < 2) { + status.SetError("called with incorrect number of arguments"); + return false; + } + + // keywords + static cm::static_string_view const COMMAND{ "COMMAND"_s }; + static cm::static_string_view const OUTPUT{ "OUTPUT"_s }; + static cm::static_string_view const DEPENDS{ "DEPENDS"_s }; + static cm::static_string_view const DEPFILE{ "DEPFILE"_s }; + static cm::static_string_view const BYPRODUCTS{ "BYPRODUCTS"_s }; + static cm::static_string_view const GLOBAL{ "GLOBAL"_s }; + static cm::static_string_view const CONFIGURATOR{ "CONFIGURATOR"_s }; + static cm::static_string_view const FOR_FILE_SET{ "FOR_FILE_SET"_s }; + static cm::static_string_view const FOR_SOURCE{ "FOR_SOURCE"_s }; + static cm::static_string_view const FROM_RULE{ "FROM_RULE"_s }; + + static cm::string_view const Keywords[]{ COMMAND, OUTPUT, DEPENDS, + DEPFILE, BYPRODUCTS, GLOBAL, + CONFIGURATOR, FROM_RULE }; + cmMakefile& mf = status.GetMakefile(); + std::string const& ruleName = args[0]; + + // Check the rule name. + if (cm::contains(Keywords, ruleName)) { + status.SetError("rule name is missing."); + return false; + } + // check name validity + if (IsReservedName(ruleName)) { + status.SetError("names in all uppercase are reserved for CMake."); + return false; + } + if (!cmGeneratorExpression::IsValidTargetName(ruleName)) { + status.SetError(cmStrCat("invalid name for RULE: ", ruleName, '.')); + return false; + } + + // Make sure the rule does not already exist. + if (mf.FindRuleToUse(ruleName)) { + status.SetError( + cmStrCat("cannot create RULE \"", ruleName, + "\" because another RULE with the same name already exists.")); + return false; + } + + struct BaseArguments : public ArgumentParser::ParseResult + { + cm::optional>> + Configurators; + bool Global = false; + std::vector ParsedKeywords; + + cm::RuleScope GetScope() + { + return this->Global ? cm::RuleScope::Global : cm::RuleScope::Local; + } + }; + + if (cm::contains(args, FROM_RULE)) { + struct Arguments : public BaseArguments + { + std::string FromRule; + }; + + std::vector unexpectedArgs; + auto parser = cmArgumentParser{} + .Bind(FROM_RULE, &Arguments::FromRule) + .Bind(CONFIGURATOR, &Arguments::Configurators) + .Bind(GLOBAL, &Arguments::Global) + .BindParsedKeywords(&Arguments::ParsedKeywords); + auto parsedArgs = + parser.Parse(cmMakeRange(args).advance(1), &unexpectedArgs); + + // do various checks for arguments consistency + if (!parsedArgs.Check("", &unexpectedArgs, status)) { + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + if ((std::count(parsedArgs.ParsedKeywords.cbegin(), + parsedArgs.ParsedKeywords.cend(), FROM_RULE) > 1) || + (std::count(parsedArgs.ParsedKeywords.cbegin(), + parsedArgs.ParsedKeywords.cend(), CONFIGURATOR) > 1)) { + status.SetError( + "only one occurrence of \"FROM_RULE\" or \"CONFIGURATOR\" " + "options is allowed."); + return false; + } + + // Configurator syntax: (CHAIN|OVERRIDE) + struct ConfiguratorArguments : public ArgumentParser::ParseResult + { + std::string Configurator; + bool Chain = false; + bool Override = false; + std::vector ParsedKeywords; + }; + + // configurators syntax: FOR_FILE_SET + // FOR_SOURCE + struct ConfiguratorsArguments : public ArgumentParser::ParseResult + { + cm::optional ForFileSet; + cm::optional ForSource; + } parsedConfigurators; + + if (parsedArgs.Configurators) { + auto fileSetConfiguratorParser = + FromRuleArgumentParser{}.Bind( + FOR_FILE_SET, &ConfiguratorArguments::Configurator); + + auto sourceConfiguratorParser = + FromRuleArgumentParser{}.Bind( + FOR_SOURCE, &ConfiguratorArguments::Configurator); + + auto configuratorsParser = + cmArgumentParser{} + .BindSubParser(FOR_FILE_SET, fileSetConfiguratorParser, + &ConfiguratorsArguments::ForFileSet) + .BindSubParser(FOR_SOURCE, sourceConfiguratorParser, + &ConfiguratorsArguments::ForSource); + + unexpectedArgs.clear(); + configuratorsParser.Parse(parsedConfigurators, *parsedArgs.Configurators, + &unexpectedArgs); + + // do various checks for arguments consistency + if (!parsedConfigurators.Check("", &unexpectedArgs, status)) { + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + if (!parsedConfigurators.ForFileSet && !parsedConfigurators.ForSource) { + status.SetError( + cmStrCat("cannot create RULE \"", ruleName, + "\" because the options \"FOR_FILE_SET\" or \"FOR_SOURCE\" " + "are expected for the \"CONFIGURATOR\" option.")); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + if ((parsedConfigurators.ForFileSet && + std::count(parsedConfigurators.ForFileSet->ParsedKeywords.cbegin(), + parsedConfigurators.ForFileSet->ParsedKeywords.cend(), + FOR_FILE_SET) > 1) || + (parsedConfigurators.ForSource && + std::count(parsedConfigurators.ForSource->ParsedKeywords.cbegin(), + parsedConfigurators.ForSource->ParsedKeywords.cend(), + FOR_SOURCE) > 1)) { + status.SetError( + "only one occurrence of \"FOR_FILE_SET\" or \"FOR_SOURCE\" " + "sub-options of \"CONFIGURATOR\" option is allowed."); + return false; + } + + auto checkConfigurator = + [&status, &mf, + &ruleName](cm::optional& configurator, + cm::string_view type) -> bool { + if (!configurator) { + return true; + } + ConfiguratorArguments& ca = configurator.value(); + + cm::optional commandType = + mf.GetState()->GetCommandType(ca.Configurator); + if (!commandType) { + status.SetError(cmStrCat("command specified for \"", type, + "\" does not exist: ", ca.Configurator, + '.')); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + if (*commandType != cmStateEnums::CommandType::Function) { + status.SetError(cmStrCat("command specified for \"", type, + "\" is not a function: ", ca.Configurator, + '.')); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + if (ca.Chain && ca.Override) { + status.SetError(cmStrCat("cannot create RULE \"", ruleName, + "\" because the \"CHAIN\" and \"OVERRIDE\" " + "options of CONFIGURATOR \"", + ca.Configurator, + "\" are mutually exclusive.")); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + if (!ca.Chain && !ca.Override) { + ca.Override = true; + } + + return true; + }; + + if (!checkConfigurator(parsedConfigurators.ForFileSet, FOR_FILE_SET) || + !checkConfigurator(parsedConfigurators.ForSource, FOR_SOURCE)) { + return false; + } + } + + cmRule const* rule = mf.FindRuleToUse(parsedArgs.FromRule); + if (!rule) { + status.SetError(cmStrCat("cannot create RULE \"", ruleName, + "\" because the RULE \"", parsedArgs.FromRule, + "\" does not exist or is not accessible.")); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + auto newRule = cm::make_unique(mf, ruleName, *rule, + parsedArgs.GetScope()); + + if (parsedArgs.Configurators) { + if (parsedConfigurators.ForFileSet) { + newRule->SetConfigurator( + cmRule::ConfiguratorType::FileSet, + std::move(parsedConfigurators.ForFileSet->Configurator), + parsedConfigurators.ForFileSet->Chain + ? cmRule::ChainConfigurators::Yes + : cmRule::ChainConfigurators::No); + } + if (parsedConfigurators.ForSource) { + newRule->SetConfigurator( + cmRule::ConfiguratorType::Source, + std::move(parsedConfigurators.ForSource->Configurator), + parsedConfigurators.ForSource->Chain + ? cmRule::ChainConfigurators::Yes + : cmRule::ChainConfigurators::No); + } + } + + mf.AddRule(std::move(newRule)); + + return true; + } + + struct Arguments : public BaseArguments + { + ArgumentParser::NonEmpty> Output; + cm::optional>> + Byproducts; + ArgumentParser::NonEmpty>> Commands; + cm::optional>> Depends; + cm::optional Depfile; + }; + + std::vector unexpectedArgs; + auto parser = cmArgumentParser{} + .Bind(OUTPUT, &Arguments::Output) + .Bind(BYPRODUCTS, &Arguments::Byproducts) + .Bind(COMMAND, &Arguments::Commands) + .Bind(DEPENDS, &Arguments::Depends) + .Bind(DEPFILE, &Arguments::Depfile) + .Bind(CONFIGURATOR, &Arguments::Configurators) + .Bind(GLOBAL, &Arguments::Global) + .BindParsedKeywords(&Arguments::ParsedKeywords); + + auto parsedArgs = + parser.Parse(cmMakeRange(args).advance(1), &unexpectedArgs); + + // do various checks for arguments consistency + if (!parsedArgs.Check("", &unexpectedArgs, status)) { + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + if (parsedArgs.Commands.empty() || parsedArgs.Output.empty()) { + status.SetError(cmStrCat( + "cannot create RULE \"", ruleName, + "\" because the mandatory options \"COMMAND\" or \"OUTPUT\" are " + "missing.")); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + if ((std::count(parsedArgs.ParsedKeywords.cbegin(), + parsedArgs.ParsedKeywords.cend(), DEPFILE) > 1) || + (std::count(parsedArgs.ParsedKeywords.cbegin(), + parsedArgs.ParsedKeywords.cend(), CONFIGURATOR) > 1)) { + status.SetError("only one occurrence of \"DEPFILE\" or \"CONFIGURATOR\" " + "options is allowed."); + return false; + } + + struct ConfiguratorsArguments : public ArgumentParser::ParseResult + { + cm::optional ForFileSet; + cm::optional ForSource; + std::vector ParsedKeywords; + } parsedConfigurators; + + if (parsedArgs.Configurators) { + // parse the arguments of CONFIGURATOR option + // CONFIGURATOR syntax: FOR_FILE_SET + // FOR_SOURCE + auto configuratorsParser = + cmArgumentParser{} + .Bind(FOR_FILE_SET, &ConfiguratorsArguments::ForFileSet) + .Bind(FOR_SOURCE, &ConfiguratorsArguments::ForSource) + .BindParsedKeywords(&ConfiguratorsArguments::ParsedKeywords); + + unexpectedArgs.clear(); + configuratorsParser.Parse(parsedConfigurators, *parsedArgs.Configurators, + &unexpectedArgs); + + // do various checks for arguments consistency + if (!parsedConfigurators.Check("", &unexpectedArgs, status)) { + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + if ((std::count(parsedConfigurators.ParsedKeywords.cbegin(), + parsedConfigurators.ParsedKeywords.cend(), + FOR_FILE_SET) > 1) || + (std::count(parsedConfigurators.ParsedKeywords.cbegin(), + parsedConfigurators.ParsedKeywords.cend(), + FOR_SOURCE) > 1)) { + status.SetError( + "only one occurrence of \"FOR_FILE_SET\" or \"FOR_SOURCE\" " + "sub-options of \"CONFIGURATOR\" option is allowed."); + return false; + } + + if (!parsedConfigurators.ForFileSet && !parsedConfigurators.ForSource) { + status.SetError(cmStrCat( + "cannot create RULE \"", ruleName, + "\" because the options \"FOR_FILE_SET\" or \"FOR_SOURCE\" are " + "expected for the \"CONFIGURATOR\" option.")); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + auto checkConfigurator = + [&status, &mf](cm::optional const& configurator, + cm::string_view type) -> bool { + if (!configurator) { + return true; + } + + auto commandType = mf.GetState()->GetCommandType(*configurator); + if (!commandType) { + status.SetError(cmStrCat("command specified for \"", type, + "\" does not exist: ", *configurator, '.')); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + if (*commandType != cmStateEnums::CommandType::Function) { + status.SetError(cmStrCat("command specified for \"", type, + "\" is not a function: ", *configurator, + '.')); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + return true; + }; + + if (!checkConfigurator(parsedConfigurators.ForFileSet, FOR_FILE_SET) || + !checkConfigurator(parsedConfigurators.ForSource, FOR_SOURCE)) { + return false; + } + } + + auto rule = + cm::make_unique(mf, ruleName, parsedArgs.Commands, + parsedArgs.Output, parsedArgs.GetScope()); + if (parsedArgs.Byproducts) { + rule->SetByproducts(std::move(*parsedArgs.Byproducts)); + } + if (parsedArgs.Depends) { + rule->SetDepends(std::move(*parsedArgs.Depends)); + } + if (parsedArgs.Depfile) { + rule->SetDepfile(std::move(*parsedArgs.Depfile)); + } + if (parsedArgs.Configurators) { + if (parsedConfigurators.ForFileSet) { + rule->SetConfigurator(cmRule::ConfiguratorType::FileSet, + std::move(*parsedConfigurators.ForFileSet)); + } + if (parsedConfigurators.ForSource) { + rule->SetConfigurator(cmRule::ConfiguratorType::Source, + std::move(*parsedConfigurators.ForSource)); + } + } + + mf.AddRule(std::move(rule)); + + return true; +} diff --git a/Source/cmAddCustomRuleCommand.h b/Source/cmAddCustomRuleCommand.h new file mode 100644 index 0000000000..3d77d0970e --- /dev/null +++ b/Source/cmAddCustomRuleCommand.h @@ -0,0 +1,13 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include +#include + +class cmExecutionStatus; + +bool cmAddCustomRuleCommand(std::vector const& args, + cmExecutionStatus& status); diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index 355d4b49ba..50bce68f9c 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -7,6 +7,7 @@ #include "cmAddCompileDefinitionsCommand.h" #include "cmAddCustomCommandCommand.h" +#include "cmAddCustomRuleCommand.h" #include "cmAddCustomTargetCommand.h" #include "cmAddDefinitionsCommand.h" #include "cmAddDependenciesCommand.h" @@ -231,6 +232,7 @@ void GetProjectCommands(cmState* state) cmAddCompileDefinitionsCommand); state->AddBuiltinCommand("add_custom_command", cmAddCustomCommandCommand); state->AddBuiltinCommand("add_custom_target", cmAddCustomTargetCommand); + state->AddBuiltinCommand("add_custom_rule", cmAddCustomRuleCommand); state->AddBuiltinCommand("add_definitions", cmAddDefinitionsCommand); state->AddBuiltinCommand("add_dependencies", cmAddDependenciesCommand); state->AddBuiltinCommand("add_executable", cmAddExecutableCommand); diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h index 0a4a767bee..9f21c24d59 100644 --- a/Source/cmCustomCommand.h +++ b/Source/cmCustomCommand.h @@ -134,7 +134,7 @@ public: std::string const& GetTarget() const; void SetTarget(std::string const& target); - /** Set/Get the custom command rolee */ + /** Set/Get the custom command role */ std::string const& GetRole() const; void SetRole(std::string const& role); diff --git a/Source/cmCustomRule.cxx b/Source/cmCustomRule.cxx new file mode 100644 index 0000000000..87c99768f8 --- /dev/null +++ b/Source/cmCustomRule.cxx @@ -0,0 +1,55 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include +#include +#include + +#include "cmRule.h" + +class cmMakefile; + +cmCustomRule::cmCustomRule(cmMakefile& makefile, std::string name, + CustomCommands commands, + std::vector outputs, + cm::RuleScope scope) + : cmRule(makefile, name, scope) + , Commands(std::move(commands)) + , Outputs(std::move(outputs)) +{ +} + +cmCustomRule::CustomCommands const& cmCustomRule::GetCommands() const +{ + return this->Commands; +} +std::vector const& cmCustomRule::GetOutputs() const +{ + return this->Outputs; +} + +void cmCustomRule::SetByproducts(std::vector byproducts) +{ + this->Byproducts = std::move(byproducts); +} +std::vector const& cmCustomRule::GetByproducts() const +{ + return this->Byproducts; +} + +void cmCustomRule::SetDepends(std::vector depends) +{ + this->Depends = std::move(depends); +} +std::vector const& cmCustomRule::GetDepends() const +{ + return this->Depends; +} + +void cmCustomRule::SetDepfile(std::string depfile) +{ + this->Depfile = std::move(depfile); +} +std::string const& cmCustomRule::GetDepfile() const +{ + return this->Depfile; +} diff --git a/Source/cmFileSetMetadata.h b/Source/cmFileSetMetadata.h index 7d789a85dc..6fe98ef994 100644 --- a/Source/cmFileSetMetadata.h +++ b/Source/cmFileSetMetadata.h @@ -15,6 +15,17 @@ class cmMakefile; namespace cm { namespace FileSetMetadata { +enum class FileSetDomain : std::uint16_t +{ + // NATIVE: File set type is defined by CMake + NATIVE, + // RULE: file set type is matching a defined custom rule + RULE +}; +using FileSetDomainSet = cm::enum_set; +static FileSetDomainSet const AllFileSetDomains{ FileSetDomain::NATIVE, + FileSetDomain::RULE }; + enum class Visibility { Private, @@ -108,3 +119,4 @@ bool IsValidName(cm::string_view type); } CM_ENUM_SET_TRAITS(cm::FileSetMetadata::AttributeSet) +CM_ENUM_SET_TRAITS(cm::FileSetMetadata::FileSetDomainSet) diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 7f360709f6..70002d67bb 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -35,6 +35,7 @@ #include "cmGeneratorExpressionDAGChecker.h" #include "cmGeneratorExpressionEvaluator.h" #include "cmGeneratorFileSet.h" +#include "cmGeneratorRule.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmLinkItem.h" @@ -4442,6 +4443,89 @@ static const struct SourcePropertyNode : public cmGeneratorExpressionNode } } sourcePropertyNode; +static const struct RulePropertyNode : public cmGeneratorExpressionNode +{ + RulePropertyNode() {} // NOLINT(modernize-use-equals-default) + + // This node handles errors on parameter count itself. + int NumExpectedParameters() const override { return 2; } + + std::string Evaluate( + std::vector const& parameters, cm::GenEx::Evaluation* eval, + GeneratorExpressionContent const* content, + cmGeneratorExpressionDAGChecker* dagCheckerParent) const override + { + static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$"); + + std::string ruleName = parameters.front(); + std::string const& propertyName = parameters.back(); + + if (ruleName.empty() && propertyName.empty()) { + reportError(eval, content->GetOriginalExpression(), + "$ expression requires a " + "non-empty rule name and property name."); + return std::string{}; + } + if (ruleName.empty()) { + reportError(eval, content->GetOriginalExpression(), + "$ expression requires a " + "non-empty rule name."); + return std::string{}; + } + if (propertyName.empty()) { + reportError(eval, content->GetOriginalExpression(), + "$ expression requires a " + "non-empty property name."); + return std::string{}; + } + if (!propertyNameValidator.find(propertyName)) { + reportError(eval, content->GetOriginalExpression(), + "Property name not supported."); + return std::string{}; + } + + cmValue propertyValue; + cmGeneratorRule* genRule = nullptr; + + genRule = eval->Context.LG->FindGeneratorRuleToUse(ruleName); + + if (!genRule) { + reportError(eval, content->GetOriginalExpression(), + cmStrCat("Rule \"", ruleName, "\" is not known to CMake.")); + return std::string{}; + } + + propertyValue = genRule->GetProperty(propertyName); + + if (propertyName == "INCLUDE_DIRECTORIES"_s || + propertyName == "COMPILE_OPTIONS"_s || + propertyName == "COMPILE_DEFINITIONS"_s) { + cmGeneratorExpressionDAGChecker dagChecker{ + eval->HeadTarget, propertyName, content, + dagCheckerParent, eval->Context, eval->Backtrace, + }; + switch (dagChecker.Check()) { + case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: + dagChecker.ReportError(eval, content->GetOriginalExpression()); + return std::string{}; + case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: + // No error. We just skip cyclic references. + return std::string{}; + case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: + CM_FALLTHROUGH; + case cmGeneratorExpressionDAGChecker::DAG: + break; + } + + return cmGeneratorExpression::StripEmptyListElements( + this->EvaluateDependentExpression(propertyValue, eval, + eval->HeadTarget, &dagChecker, + eval->CurrentTarget)); + } + return propertyValue; + } +} rulePropertyNode; + static std::string getLinkedTargetsContent( cmGeneratorTarget const* target, std::string const& prop, cm::GenEx::Evaluation* eval, cmGeneratorExpressionDAGChecker* dagChecker, @@ -6453,6 +6537,7 @@ cmGeneratorExpressionNode const* cmGeneratorExpressionNode::GetNode( { "COMMA", &commaNode }, { "SEMICOLON", &semicolonNode }, { "QUOTE", "eNode }, + { "RULE_PROPERTY", &rulePropertyNode }, { "SOURCE_EXISTS", &sourceExistsNode }, { "SOURCE_PROPERTY", &sourcePropertyNode }, { "FILE_SET_EXISTS", &fileSetExistsNode }, diff --git a/Source/cmGeneratorFileSet.cxx b/Source/cmGeneratorFileSet.cxx index adc25e3ed7..62df8ba8d8 100644 --- a/Source/cmGeneratorFileSet.cxx +++ b/Source/cmGeneratorFileSet.cxx @@ -174,10 +174,12 @@ std::vector> ProcessIncludes( // // Class cmGeneratorFileSet // -cmGeneratorFileSet::cmGeneratorFileSet(cmGeneratorTarget const* target, - cmFileSet const* fileSet) +cmGeneratorFileSet::cmGeneratorFileSet( + cmGeneratorTarget const* target, cmFileSet const* fileSet, + cm::FileSetMetadata::FileSetDomain domain) : Target(target) , FileSet(fileSet) + , Domain(domain) { auto& cmake = *target->GetLocalGenerator()->GetCMakeInstance(); diff --git a/Source/cmGeneratorFileSet.h b/Source/cmGeneratorFileSet.h index e43a033a0a..0a0d3f2aec 100644 --- a/Source/cmGeneratorFileSet.h +++ b/Source/cmGeneratorFileSet.h @@ -33,7 +33,8 @@ class cmGeneratorFileSet public: using TargetPropertyEntry = cm::TargetPropertyEntry; - cmGeneratorFileSet(cmGeneratorTarget const*, cmFileSet const*); + cmGeneratorFileSet(cmGeneratorTarget const*, cmFileSet const*, + cm::FileSetMetadata::FileSetDomain); ~cmGeneratorFileSet() = default; cmGeneratorFileSet(cmGeneratorFileSet&&) = default; @@ -56,6 +57,8 @@ public: cmFileSet const* GetFileSet() const { return this->FileSet; } + cm::FileSetMetadata::FileSetDomain GetDomain() const { return this->Domain; } + cmValue GetProperty(std::string const& prop) const; std::vector> GetIncludeDirectories( @@ -130,6 +133,7 @@ public: private: cmGeneratorTarget const* Target; cmFileSet const* FileSet; + cm::FileSetMetadata::FileSetDomain Domain; mutable std::vector> CompiledDirectoryEntries; mutable std::vector> diff --git a/Source/cmGeneratorFileSets.cxx b/Source/cmGeneratorFileSets.cxx index b4808800b4..e3584a3cb8 100644 --- a/Source/cmGeneratorFileSets.cxx +++ b/Source/cmGeneratorFileSets.cxx @@ -12,9 +12,9 @@ #include #include #include +#include #include "cmFileSet.h" -#include "cmFileSetMetadata.h" #include "cmGenExContext.h" #include "cmGenExEvaluation.h" #include "cmGeneratorExpression.h" @@ -48,23 +48,8 @@ cmGeneratorFileSets::cmGeneratorFileSets(cmGeneratorTarget* target, target->GetName(), R"(".)")); }; - for (auto const& name : target->Target->GetAllPrivateFileSets()) { - cmFileSet const* fileSet = target->Target->GetFileSet(name); - if (isFramework && - !cm::FileSetMetadata::IsFrameworkSupported(fileSet->GetType())) { - issueMessage(fileSet); - continue; - } - auto entry = this->FileSets.emplace( - name, cm::make_unique(target, fileSet)); - auto const* genFileSet = entry.first->second.get(); - this->AllFileSets.push_back(genFileSet); - this->SelfFileSets[genFileSet->GetType()].push_back(genFileSet); - } - for (auto const& name : target->Target->GetAllInterfaceFileSets()) { - auto it = this->FileSets.find(name); - cmGeneratorFileSet const* genFileSet = nullptr; - if (it == this->FileSets.end()) { + for (auto domain : cm::FileSetMetadata::AllFileSetDomains) { + for (auto const& name : target->Target->GetAllPrivateFileSets(domain)) { cmFileSet const* fileSet = target->Target->GetFileSet(name); if (isFramework && !cm::FileSetMetadata::IsFrameworkSupported(fileSet->GetType())) { @@ -72,13 +57,30 @@ cmGeneratorFileSets::cmGeneratorFileSets(cmGeneratorTarget* target, continue; } auto entry = this->FileSets.emplace( - name, cm::make_unique(target, fileSet)); - genFileSet = entry.first->second.get(); + name, cm::make_unique(target, fileSet, domain)); + auto const* genFileSet = entry.first->second.get(); this->AllFileSets.push_back(genFileSet); - } else { - genFileSet = it->second.get(); + this->SelfFileSets[genFileSet->GetType()].push_back(genFileSet); + } + for (auto const& name : target->Target->GetAllInterfaceFileSets(domain)) { + auto it = this->FileSets.find(name); + cmGeneratorFileSet const* genFileSet = nullptr; + if (it == this->FileSets.end()) { + cmFileSet const* fileSet = target->Target->GetFileSet(name); + if (isFramework && + !cm::FileSetMetadata::IsFrameworkSupported(fileSet->GetType())) { + issueMessage(fileSet); + continue; + } + auto entry = this->FileSets.emplace( + name, cm::make_unique(target, fileSet, domain)); + genFileSet = entry.first->second.get(); + this->AllFileSets.push_back(genFileSet); + } else { + genFileSet = it->second.get(); + } + this->InterfaceFileSets[genFileSet->GetType()].push_back(genFileSet); } - this->InterfaceFileSets[genFileSet->GetType()].push_back(genFileSet); } } cmGeneratorFileSets::~cmGeneratorFileSets() = default; @@ -199,11 +201,12 @@ cmGeneratorFileSets::GetSources( std::vector> cmGeneratorFileSets::GetSources( cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cm::FileSetMetadata::FileSetDomainSet domains, cmGeneratorExpressionDAGChecker* dagChecker) const { return this->GetSources( - [](cmGeneratorFileSet const* fileSet) -> bool { - return fileSet->IsForSelf(); + [&domains](cmGeneratorFileSet const* fileSet) -> bool { + return fileSet->IsForSelf() && domains.contains(fileSet->GetDomain()); }, context, target, dagChecker); } @@ -223,11 +226,13 @@ cmGeneratorFileSets::GetSources( std::vector> cmGeneratorFileSets::GetInterfaceSources( cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cm::FileSetMetadata::FileSetDomainSet domains, cmGeneratorExpressionDAGChecker* dagChecker) const { return this->GetSources( - [](cmGeneratorFileSet const* fileSet) -> bool { - return fileSet->IsForInterface(); + [&domains](cmGeneratorFileSet const* fileSet) -> bool { + return fileSet->IsForInterface() && + domains.contains(fileSet->GetDomain()); }, context, target, dagChecker); } diff --git a/Source/cmGeneratorFileSets.h b/Source/cmGeneratorFileSets.h index 3d47a0d0f0..b63a402a62 100644 --- a/Source/cmGeneratorFileSets.h +++ b/Source/cmGeneratorFileSets.h @@ -14,6 +14,7 @@ #include +#include "cmFileSetMetadata.h" #include "cmTargetPropertyEntry.h" namespace cm { @@ -67,6 +68,8 @@ public: std::vector> GetSources( cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cm::FileSetMetadata::FileSetDomainSet + domains = { cm::FileSetMetadata::FileSetDomain::NATIVE }, cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; std::vector> GetSources( std::string type, cm::GenEx::Context const& context, @@ -75,6 +78,8 @@ public: std::vector> GetInterfaceSources( cm::GenEx::Context const& context, cmGeneratorTarget const* target, + cm::FileSetMetadata::FileSetDomainSet + domains = { cm::FileSetMetadata::FileSetDomain::NATIVE }, cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const; std::vector> GetInterfaceSources( std::string type, cm::GenEx::Context const& context, diff --git a/Source/cmGeneratorRule.cxx b/Source/cmGeneratorRule.cxx new file mode 100644 index 0000000000..239c8ad661 --- /dev/null +++ b/Source/cmGeneratorRule.cxx @@ -0,0 +1,293 @@ + +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmGeneratorRule.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "cmCMakePath.h" +#include "cmCustomCommand.h" +#include "cmCustomCommandLines.h" +#include "cmDiagnostics.h" +#include "cmFileSet.h" +#include "cmGeneratorExpression.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmMakefile.h" +#include "cmSourceFile.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" + +namespace { +std::vector ReservedPatterns{ "RULE"_s, + "TARGET"_s, + "FILE_SET"_s, + "SOURCE_DIR"_s, + "BINARY_DIR"_s, + "CURRENT_SOURCE_DIR"_s, + "CURRENT_BINARY_DIR"_s, + "SOURCE"_s, + "INPUT_DIR"_s, + "FILE_NAME"_s, + "BASE_NAME"_s, + "INCLUDE_DIRECTORIES"_s, + "COMPILE_OPTIONS"_s, + "COMPILE_DEFINITIONS"_s }; +} + +std::string cmGeneratorRule::RulePlaceholderExpander::ExpandVariable( + std::string const& variable) +{ + if (cm::contains(this->Values, variable)) { + return this->Values[variable]; + } + // If there is no variable defined, mark unresolved variable by '{' and '}' + return cmStrCat('{', variable, '}'); +} + +cmGeneratorRule::RulePlaceholderExpander::VariableMap const + cmGeneratorRule::DefaultProperties{ + { "INCLUDE_DIRECTORIES", + "$,TARGET:,INCLUDE_" + "DIRECTORIES>;$,TARGET_DIRECTORY:," + "INCLUDE_DIRECTORIES>;$,INCLUDE_DIRECTORIES>," + "EXCLUDE,^$>" }, + { "COMPILE_OPTIONS", + "$,COMPILE_OPTIONS>;$," + "TARGET_DIRECTORY:,COMPILE_OPTIONS>;$,TARGET:,COMPILE_OPTIONS>,EXCLUDE,^$>" }, + { "COMPILE_DEFINITIONS", + "$,COMPILE_DEFINITIONS>;$," + "TARGET_DIRECTORY:,COMPILE_DEFINITIONS>;$,TARGET:,COMPILE_DEFINITIONS>,EXCLUDE,^$>" } + }; + +cmGeneratorRule::cmGeneratorRule(cmRule const* rule, cmTarget const* target, + cmFileSet const* fileSet, + cmFileSet const* outputFileSet, + cmSourceFile const* source, + cmRule::PatternSet const& patterns) + : Rule(rule) + , Target(target) + , FileSet(fileSet) + , OutputFileSet(outputFileSet) + , Source(source) + , Makefile(*fileSet->GetMakefile()) +{ + this->Name = cmStrCat(rule->GetName(), '_', + std::hash{}(cmStrCat( + rule->GetName(), '-', target->GetName(), '-', + fileSet->GetName(), '-', source->GetFullPath()))); + + auto& values = this->RuleExpander.Values; + + cmCMakePath file = cmCMakePath{ this->Source->GetFullPath() }.Normal(); + cmCMakePath sourceDir = file.IsAbsolute() + ? file.GetParentPath() + : this->GetMakefile().GetCurrentSourceDirectory(); + + /* clang-format off */ + values.insert({ "RULE", this->GetName() }); + values.insert({ "TARGET", this->Target->GetName() }); + values.insert({ "FILE_SET", this->FileSet->GetName() }); + values.insert({ "SOURCE_DIR", this->GetMakefile().GetHomeDirectory() }); + values.insert({ "BINARY_DIR", this->GetMakefile().GetHomeOutputDirectory() }); + values.insert({ "CURRENT_SOURCE_DIR", this->GetMakefile().GetCurrentSourceDirectory() }); + values.insert({ "CURRENT_BINARY_DIR", this->GetMakefile().GetCurrentBinaryDirectory() }); + values.insert({ "SOURCE", file.GenericString() }); + values.insert({ "INPUT_DIR", file.GetParentPath().GenericString() }); + values.insert({ "FILE_NAME", file.GetFileName().GenericString() }); + values.insert({ "BASE_NAME", file.GetFileName().RemoveWideExtension().GenericString() }); + /* clang-format on */ + + // instantiate default properties pattern + for (auto const& item : DefaultProperties) { + values.insert( + { item.first, this->RuleExpander.ExpandVariables(item.second) }); + } + + this->UpdateRuleExpander(patterns); +} + +void cmGeneratorRule::UpdateRuleExpander(cmRule::PatternSet const& patterns) +{ + if (patterns.empty()) { + return; + } + + auto& values = this->RuleExpander.Values; + + for (cmRule::Pattern const& pattern : patterns) { + if (cm::contains(ReservedPatterns, pattern.Name)) { + // overwriting a reserved pattern is not allowed + continue; + } + + std::string data{ pattern.Value }; + values[pattern.Name] = this->RuleExpander.ExpandVariables(data); + } +} + +cmValue cmGeneratorRule::GetProperty(std::string const& property) const +{ + cmValue value = this->Properties.GetPropertyValue(property); + if (value) { + return value; + } + + if (property == "OUTPUT_FILE_SET"_s) { + this->Properties.SetProperty( + property, + cmList{ this->OutputFileSet->GetName(), this->OutputFileSet->GetType() } + .to_string()); + return this->Properties.GetPropertyValue(property); + } + + // property not yet instantiated, retrieve it from cmRule + value = this->Rule->GetProperty(property); + if (value) { + std::string expandedValue{ *value }; + this->Properties.SetProperty( + property, this->RuleExpander.ExpandVariables(expandedValue)); + return this->Properties.GetPropertyValue(property); + } + + return value; +} + +std::unique_ptr cmGeneratorRule::CreateCustomCommand() const +{ + auto expandVariables = [this](std::string const& item) -> std::string { + return this->RuleExpander.ExpandVariables(item); + }; + + auto expandVector = + [&expandVariables]( + std::vector const& data) -> std::vector { + std::vector result; + result.reserve(data.size()); + std::transform(data.begin(), data.end(), std::back_inserter(result), + expandVariables); + return result; + }; + + auto expandPaths = + [&expandVariables]( + std::string const& binaryDirectory, + std::vector const& data) -> std::vector { + std::vector result; + result.reserve(data.size()); + std::transform( + data.begin(), data.end(), std::back_inserter(result), + [&expandVariables, + &binaryDirectory](std::string const& item) -> std::string { + std::string path = expandVariables(item); + if (!cmSystemTools::FileIsFullPath(path) && + !cmGeneratorExpression::StartsWithGeneratorExpression(path)) { + path = cmStrCat(binaryDirectory, '/', path); + } + cmSystemTools::ConvertToUnixSlashes(path); + if (cmSystemTools::FileIsFullPath(path)) { + path = cmSystemTools::CollapseFullPath(path); + } + return path; + }); + return result; + }; + + auto cc = cm::make_unique(); + + cmCustomCommandLines commandLines; + for (cmRule::CustomCommand const& line : this->Rule->GetCommands()) { + cmCustomCommandLine command; + command.reserve(line.size()); + std::transform(line.begin(), line.end(), std::back_inserter(command), + expandVariables); + commandLines.push_back(command); + } + cc->SetCommandLines(std::move(commandLines)); + + cc->SetOutputs(expandPaths(this->GetMakefile().GetCurrentBinaryDirectory(), + this->Rule->GetOutputs())); + + if (!this->Rule->GetByproducts().empty()) { + cc->SetByproducts(expandVector(this->Rule->GetByproducts())); + } + + if (!this->Rule->GetDepfile().empty()) { + cc->SetDepfile(expandVariables(this->Rule->GetDepfile())); + } + std::vector depends{ 1, this->Source->GetFullPath() }; + if (!this->Rule->GetDepends().empty()) { + depends.insert(depends.end(), this->Rule->GetDepends().begin(), + this->Rule->GetDepends().end()); + } + cc->SetDepends(expandVector(depends)); + if (cmValue deps_explicit = + this->Rule->GetProperty("DEPENDS_EXPLICIT_ONLY")) { + cc->SetDependsExplicitOnly(deps_explicit.IsOn()); + } else { + cc->SetDependsExplicitOnly(this->GetMakefile().IsOn( + "CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY")); + } + + if (cmValue wd = this->Rule->GetProperty("WORKING_DIRECTORY")) { + cc->SetWorkingDirectory(expandVariables(*wd)); + } + + if (this->Rule->GetProperty("USES_TERMINAL").IsOn() && + !this->Rule->GetProperty("JOB_POOL_COMPILE")->empty()) { + this->GetMakefile().IssueDiagnostic( + cmDiagnostics::CMD_AUTHOR, + cmStrCat("RULE \"", this->GetName(), "\": JOB_POOL \"", + expandVariables(this->Rule->GetProperty("JOB_POOL_COMPILE")), + "\" is shadowed by USES_TERMINAL.")); + } + if (!this->Rule->GetProperty("JOB_POOL_COMPILE")->empty()) { + cc->SetJobPool( + expandVariables(*this->Rule->GetProperty("JOB_POOL_COMPILE"))); + } else if (this->Rule->GetProperty("USES_TERMINAL").IsOn()) { + cc->SetUsesTerminal(true); + } + + cc->SetJobserverAware(this->Rule->GetProperty("JOB_SERVER_AWARE").IsOn()); + + cc->SetEscapeOldStyle(!this->Rule->GetProperty("VERBATIM").IsOn()); + cc->SetCommandExpandLists( + this->Rule->GetProperty("COMMAND_EXPAND_LISTS").IsOn()); + + if (cmValue comment = this->Rule->GetProperty("COMMENT")) { + cc->SetComment(expandVariables(*comment)); + } + + return cc; +} + +std::unique_ptr cmGeneratorRule::Generate( + cmFileSet* outFileSet) const +{ + auto cc = this->CreateCustomCommand(); + + // populate file set with outputs from custom command + for (auto const& output : cc->GetOutputs()) { + outFileSet->AddFileEntry(BT{ output }); + // get the directory of the generated file + outFileSet->AddDirectoryEntry( + BT{ cmStrCat("$') }); + } + + return cc; +} diff --git a/Source/cmGeneratorRule.h b/Source/cmGeneratorRule.h new file mode 100644 index 0000000000..949614423b --- /dev/null +++ b/Source/cmGeneratorRule.h @@ -0,0 +1,78 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include +#include +#include + +#include "cmPlaceholderExpander.h" +#include "cmPropertyMap.h" +#include "cmRule.h" +#include "cmValue.h" + +class cmMakefile; +class cmTarget; +class cmFileSet; +class cmSourceFile; +class cmCustomCommand; + +class cmGeneratorRule +{ +public: + cmGeneratorRule(cmRule const* rule, cmTarget const* target, + cmFileSet const* fileSet, cmFileSet const* outputFileSet, + cmSourceFile const* source, + cmRule::PatternSet const& patterns); + + cmGeneratorRule(cmGeneratorRule const&) = delete; + cmGeneratorRule& operator=(cmGeneratorRule const&) = delete; + + cmMakefile& GetMakefile() const { return this->Makefile; } + + /** Get the name of the rule */ + std::string const& GetName() const { return this->Name; } + + bool IsGloballyVisible() const { return this->Rule->IsGloballyVisible(); } + + cmValue GetProperty(std::string const& property) const; + + std::unique_ptr Generate(cmFileSet* outFileSet) const; + +private: + void UpdateRuleExpander(cmRule::PatternSet const& patterns); + + std::unique_ptr CreateCustomCommand() const; + + std::string Name; + cmRule const* Rule; + cmTarget const* Target; + cmFileSet const* FileSet; + cmFileSet const* OutputFileSet; + cmSourceFile const* Source; + cmMakefile& Makefile; + mutable cmPropertyMap Properties; + + class RulePlaceholderExpander : public cmPlaceholderExpander + { + public: + using VariableMap = std::unordered_map; + VariableMap Values; + + std::string ExpandVariables(std::string const& string) + { + std::string value{ string }; + return cmPlaceholderExpander::ExpandVariables( + value, cmPlaceholderExpander::HandleGenex::Yes); + } + + private: + std::string ExpandVariable(std::string const& variable) override; + }; + + static RulePlaceholderExpander::VariableMap const DefaultProperties; + + mutable RulePlaceholderExpander RuleExpander; +}; diff --git a/Source/cmGeneratorTarget_Sources.cxx b/Source/cmGeneratorTarget_Sources.cxx index 136e5b0349..58f6b158d2 100644 --- a/Source/cmGeneratorTarget_Sources.cxx +++ b/Source/cmGeneratorTarget_Sources.cxx @@ -92,7 +92,11 @@ void AddFileSetEntries(cmGeneratorTarget const* headTarget, cmGeneratorExpressionDAGChecker* dagChecker, cm::EvaluatedTargetPropertyEntries& entries) { - auto sources = fileSets->GetSources(context, headTarget, dagChecker); + auto sources = + fileSets->GetSources(context, headTarget, + cm::FileSetMetadata::FileSetDomainSet{ + cm::FileSetMetadata::FileSetDomain::NATIVE }, + dagChecker); entries = EvaluateTargetPropertyEntries(headTarget, context, dagChecker, sources); } diff --git a/Source/cmGetPropertyCommand.cxx b/Source/cmGetPropertyCommand.cxx index 6252893b5f..1b64f3b4e0 100644 --- a/Source/cmGetPropertyCommand.cxx +++ b/Source/cmGetPropertyCommand.cxx @@ -9,6 +9,7 @@ #include "cmMakefile.h" #include "cmProperty.h" #include "cmPropertyDefinition.h" +#include "cmRule.h" #include "cmSetPropertyCommand.h" #include "cmSourceFilePropertyHelper.h" #include "cmState.h" @@ -36,6 +37,9 @@ bool HandleGlobalMode(cmExecutionStatus& status, std::string const& name, bool HandleDirectoryMode(cmExecutionStatus& status, std::string const& name, OutType infoType, std::string const& variable, std::string const& propertyName); +bool HandleRuleMode(cmExecutionStatus& status, std::string const& name, + OutType infoType, std::string const& variable, + std::string const& propertyName); bool HandleTargetMode(cmExecutionStatus& status, std::string const& name, OutType infoType, std::string const& variable, std::string const& propertyName); @@ -97,6 +101,8 @@ bool cmGetPropertyCommand(std::vector const& args, scope = cmProperty::GLOBAL; } else if (args[1] == "DIRECTORY") { scope = cmProperty::DIRECTORY; + } else if (args[1] == "RULE") { + scope = cmProperty::RULE; } else if (args[1] == "TARGET") { scope = cmProperty::TARGET; } else if (args[1] == "FILE_SET") { @@ -112,11 +118,10 @@ bool cmGetPropertyCommand(std::vector const& args, } else if (args[1] == "INSTALL") { scope = cmProperty::INSTALL; } else { - status.SetError(cmStrCat("given invalid scope ", args[1], - ". " - "Valid scopes are " - "GLOBAL, DIRECTORY, TARGET, FILE_SET, SOURCE, " - "TEST, VARIABLE, CACHE, INSTALL.")); + status.SetError( + cmStrCat("given invalid scope ", args[1], + ". Valid scopes are GLOBAL, DIRECTORY, RULE, TARGET, " + "FILE_SET, SOURCE, TEST, VARIABLE, CACHE, INSTALL.")); return false; } @@ -236,6 +241,8 @@ bool cmGetPropertyCommand(std::vector const& args, case cmProperty::DIRECTORY: return HandleDirectoryMode(status, name, infoType, variable, propertyName); + case cmProperty::RULE: + return HandleRuleMode(status, name, infoType, variable, propertyName); case cmProperty::TARGET: return HandleTargetMode(status, name, infoType, variable, propertyName); @@ -322,6 +329,24 @@ bool HandleDirectoryMode(cmExecutionStatus& status, std::string const& name, return StoreResult(infoType, status.GetMakefile(), variable, prop); } +bool HandleRuleMode(cmExecutionStatus& status, std::string const& name, + OutType infoType, std::string const& variable, + std::string const& propertyName) +{ + if (name.empty()) { + status.SetError("not given name for RULE scope."); + return false; + } + + if (cmRule* rule = status.GetMakefile().FindRuleToUse(name)) { + cmValue prop = rule->GetProperty(propertyName); + return StoreResult(infoType, status.GetMakefile(), variable, prop); + } + status.SetError(cmStrCat("could not find RULE ", name, + ". Perhaps it has not yet been created.")); + return false; +} + bool HandleTargetMode(cmExecutionStatus& status, std::string const& name, OutType infoType, std::string const& variable, std::string const& propertyName) diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index e83d10c093..3d2baad520 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -40,8 +40,11 @@ #include "cmExperimental.h" #include "cmExportBuildFileGenerator.h" #include "cmExternalMakefileProjectGenerator.h" +#include "cmFileSet.h" +#include "cmFileSetMetadata.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorRule.h" #include "cmGeneratorTarget.h" #include "cmInstallDirs.h" #include "cmInstallExportGenerator.h" @@ -58,6 +61,7 @@ #include "cmOutputConverter.h" #include "cmPolicies.h" #include "cmRange.h" +#include "cmRule.h" #include "cmSbomArguments.h" #include "cmSourceFile.h" #include "cmState.h" @@ -65,6 +69,7 @@ #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmTarget.h" #include "cmTargetExport.h" #include "cmUnreachable.h" #include "cmValue.h" @@ -1524,6 +1529,9 @@ void cmGlobalGenerator::Configure() void cmGlobalGenerator::CreateGenerationObjects(TargetTypes targetTypes) { this->CreateLocalGenerators(); + + this->CreateCustomCommandsFromRules(); + // Commit side effects only if we are actually generating if (targetTypes == TargetTypes::AllTargets) { this->CheckTargetProperties(); @@ -2272,6 +2280,58 @@ cmGlobalGenerator::CreateMSVC60LinkLineComputer( cm::make_unique(outputConverter, stateDir)); } +void cmGlobalGenerator::CreateCustomCommandsFromRules() +{ + for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) { + cmMakefile* mf = this->Makefiles[i].get(); + cmLocalGenerator* lg = this->LocalGenerators[i].get(); + for (auto& item : mf->GetTargets()) { + cmTarget& target = item.second; + for (auto const& fsName : target.GetAllFileSetNames( + cm::FileSetMetadata::FileSetDomain::RULE)) { + cmFileSet const* fileSet = target.GetFileSet(fsName); + cmRule const* rule = + fileSet->GetMakefile()->FindRuleToUse(fileSet->GetType()); + if (!rule) { + continue; + } + + // generated files by the custom command are stored in a file set + cmFileSet* outFileSet = rule->GetOutputFileSet(&target, fileSet); + if (!outFileSet) { + continue; + } + + cmRule::PatternSet fileSetPatterns; + if (!rule->Instantiate(&target, fileSet, outFileSet, + fileSetPatterns)) { + continue; + } + + for (auto const& files : fileSet->GetFileEntries()) { + for (auto const& file : + cmList{ cm::remove_BT(files), cmList::EmptyElements::No }) { + cmSourceFile* source = mf->GetOrCreateSource(file); + source->ResolveFullPath(); + + cmRule::PatternSet sourcePatterns{ fileSetPatterns }; + if (!rule->Instantiate(&target, fileSet, outFileSet, source, + sourcePatterns)) { + continue; + } + + auto genRule = cm::make_unique( + rule, &target, fileSet, outFileSet, source, sourcePatterns); + auto cc = genRule->Generate(outFileSet); + lg->AddGeneratorRule(std::move(genRule)); + mf->AddCustomCommandToOutput(std::move(cc)); + } + } + } + } + } +} + void cmGlobalGenerator::FinalizeTargetConfiguration() { std::vector const langs = @@ -2392,6 +2452,7 @@ void cmGlobalGenerator::ClearGeneratorMembers() this->TargetDependencies.clear(); this->TargetSearchIndex.clear(); this->GeneratorTargetSearchIndex.clear(); + this->RuleSearchIndex.clear(); this->MakefileSearchIndex.clear(); this->LocalGeneratorSearchIndex.clear(); this->TargetOrderIndex.clear(); @@ -3027,6 +3088,20 @@ std::string cmGlobalGenerator::IndexGeneratorTargetUniquely( return id; } +void cmGlobalGenerator::IndexRule(cmRule* rule) +{ + if (rule->IsGloballyVisible()) { + this->RuleSearchIndex[rule->GetName()] = rule; + } +} + +void cmGlobalGenerator::IndexGeneratorRule(cmGeneratorRule* gr) +{ + if (gr->IsGloballyVisible()) { + this->GeneratorRuleSearchIndex[gr->GetName()] = gr; + } +} + void cmGlobalGenerator::IndexMakefile(cmMakefile* mf) { // We index by both source and binary directory. add_subdirectory @@ -3091,6 +3166,25 @@ cmGeneratorTarget* cmGlobalGenerator::FindGeneratorTarget( return this->FindGeneratorTargetImpl(name); } +cmRule* cmGlobalGenerator::FindRule(std::string const& name) const +{ + auto const it = this->RuleSearchIndex.find(name); + if (it != this->RuleSearchIndex.end()) { + return it->second; + } + return nullptr; +} + +cmGeneratorRule* cmGlobalGenerator::FindGeneratorRule( + std::string const& name) const +{ + auto const it = this->GeneratorRuleSearchIndex.find(name); + if (it != this->GeneratorRuleSearchIndex.end()) { + return it->second; + } + return nullptr; +} + bool cmGlobalGenerator::NameResolvesToFramework( std::string const& libname) const { diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 3184a28e60..21f688c8dc 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -57,6 +57,8 @@ class cmExternalMakefileProjectGenerator; class cmBuildSbomGenerator; class cmInstallSbomGenerator; class cmGeneratorTarget; +class cmRule; +class cmGeneratorRule; class cmInstallRuntimeDependencySet; class cmLinkLineComputer; class cmMakefile; @@ -178,6 +180,9 @@ public: virtual bool InspectConfigTypeVariables() { return true; } + // Produce custom commands for file sets attached to a custom rule + void CreateCustomCommandsFromRules(); + enum class CxxModuleSupportQuery { // Support is expected at the call site. @@ -412,6 +417,10 @@ public: void AddAlias(std::string const& name, std::string const& tgtName); bool IsAlias(std::string const& name) const; + //! Find a rule by name. + cmRule* FindRule(std::string const& name) const; + cmGeneratorRule* FindGeneratorRule(std::string const& name) const; + /** Determine if a name resolves to a framework on disk or a built target that is a framework. */ bool NameResolvesToFramework(std::string const& libname) const; @@ -532,6 +541,9 @@ public: virtual char const* GetRebuildCacheTargetName() const { return nullptr; } virtual char const* GetCleanTargetName() const { return nullptr; } + void IndexRule(cmRule* rule); + void IndexGeneratorRule(cmGeneratorRule* gt); + // Lookup edit_cache target command preferred by this generator. virtual std::string GetEditCacheCommand() const { return ""; } @@ -896,6 +908,8 @@ private: using TargetMap = std::unordered_map; using GeneratorTargetMap = std::unordered_map; + using RuleMap = std::unordered_map; + using GeneratorRuleMap = std::unordered_map; using MakefileMap = std::unordered_map; using LocalGeneratorMap = std::unordered_map; using TargetDirectoryRegistrationMap = @@ -913,6 +927,12 @@ private: // Map from target directories to targets using it. mutable TargetDirectoryMap TargetDirectories; + // Map efficiently from rule name to cmRule instance. + // Do not use this structure for looping over all rules. + // It may not contain all of them. + RuleMap RuleSearchIndex; + GeneratorRuleMap GeneratorRuleSearchIndex; + // Map efficiently from source directory path to cmMakefile instance. // Do not use this structure for looping over all directories. // It may not contain all of them (see note in IndexMakefile method). diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 3c06484fa1..06ad7a2ba8 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -36,6 +36,7 @@ #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionEvaluationFile.h" #include "cmGeneratorFileSet.h" +#include "cmGeneratorRule.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmInstallGenerator.h" @@ -897,6 +898,26 @@ cmGeneratorTarget* cmLocalGenerator::FindLocalNonAliasGeneratorTarget( return nullptr; } +void cmLocalGenerator::AddGeneratorRule(std::unique_ptr gr) +{ + cmGeneratorRule* gr_ptr = gr.get(); + + this->GeneratorRules.push_back(std::move(gr)); + this->GeneratorRuleSearchIndex.emplace(gr_ptr->GetName(), gr_ptr); + this->GlobalGenerator->IndexGeneratorRule(gr_ptr); +} + +cmGeneratorRule* cmLocalGenerator::FindGeneratorRuleToUse( + std::string const& name) const +{ + auto ri = this->GeneratorRuleSearchIndex.find(name); + if (ri != this->GeneratorRuleSearchIndex.end()) { + return ri->second; + } + + return this->GetGlobalGenerator()->FindGeneratorRule(name); +} + void cmLocalGenerator::ComputeTargetManifest() { // Collect the set of configuration types. diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 36eb57dc8d..0dfc86144b 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -32,6 +32,7 @@ class cmCompiledGeneratorExpression; class cmComputeLinkInformation; +class cmGeneratorRule; class cmCustomCommand; class cmCustomCommandGenerator; class cmCustomCommandLines; @@ -256,6 +257,9 @@ public: std::string const& name) const; cmGeneratorTarget* FindGeneratorTargetToUse(std::string const& name) const; + void AddGeneratorRule(std::unique_ptr gr); + cmGeneratorRule* FindGeneratorRuleToUse(std::string const& name) const; + /** * Process a list of include directories */ @@ -684,6 +688,11 @@ protected: GeneratorTargetVector OwnedImportedGeneratorTargets; std::map AliasTargets; + using GeneratorRuleMap = std::unordered_map; + GeneratorRuleMap GeneratorRuleSearchIndex; + using GeneratorRuleVector = std::vector>; + GeneratorRuleVector GeneratorRules; + std::map Compilers; std::map VariableMappings; std::string CompilerSysroot; diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 168856af3c..db21efce2e 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -52,6 +52,7 @@ #include "cmLocalGenerator.h" #include "cmMessageType.h" #include "cmRange.h" +#include "cmRule.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmSourceGroup.h" @@ -1310,6 +1311,29 @@ void cmMakefile::AppendCustomCommandToOutput( } } +cmRule* cmMakefile::AddRule(std::unique_ptr rule) +{ + // Add to the set of available rules. + this->Rules[rule->GetName()] = rule.get(); + this->GetGlobalGenerator()->IndexRule(rule.get()); + this->GetStateSnapshot().GetDirectory().AddRuleName(rule->GetName()); + + // Transfer ownership to this cmMakefile object. + this->RulesOwned.push_back(std::move(rule)); + return this->RulesOwned.back().get(); +} + +cmRule* cmMakefile::FindRuleToUse(std::string const& name) const +{ + auto i = this->Rules.find(name); + if (i != this->Rules.end()) { + return i->second; + } + + // Look for a target built in this project. + return this->GetGlobalGenerator()->FindRule(name); +} + cmTarget* cmMakefile::AddUtilityCommand(std::string const& utilityName, bool excludeFromAll, std::unique_ptr cc) @@ -1486,6 +1510,9 @@ void cmMakefile::InitializeFromParent(cmMakefile* parent) // Copy include regular expressions. this->ComplainFileRegularExpression = parent->ComplainFileRegularExpression; + // Non-global rules. + this->Rules = parent->Rules; + // Imported targets. this->ImportedTargets = parent->ImportedTargets; diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index e7836957c3..a7b0953690 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -52,6 +52,7 @@ enum class cmObjectLibraryCommands; class cmCompiledGeneratorExpression; class cmCustomCommandLines; +class cmRule; class cmExecutionStatus; class cmExpandedCommandArgument; class cmBuildSbomGenerator; @@ -260,6 +261,25 @@ public: cmImplicitDependsList const& implicit_depends, cmCustomCommandLines const& commandLines); + /** + * Add a custom rule to the build. + */ + cmRule* AddRule(std::unique_ptr rule); + + // -- List of custom rules + std::vector> const& GetOwnedRules() const + { + return this->RulesOwned; + } + using cmRuleMap = std::unordered_map; + /** Get the rules map */ + cmRuleMap const& GetRules() const { return this->Rules; } + + /** + * Lookup for a rule + */ + cmRule* FindRuleToUse(std::string const& name) const; + /** * Add a define flag to the build. */ @@ -1370,4 +1390,7 @@ private: bool IsSourceFileTryCompile; cm::ImportedTargetScope CurrentImportedTargetScope = cm::ImportedTargetScope::Local; + // -- List of custom rules + std::vector> RulesOwned; + cmRuleMap Rules; }; diff --git a/Source/cmPlaceholderExpander.cxx b/Source/cmPlaceholderExpander.cxx index 54b0686d97..785c6867d0 100644 --- a/Source/cmPlaceholderExpander.cxx +++ b/Source/cmPlaceholderExpander.cxx @@ -28,10 +28,13 @@ std::string& cmPlaceholderExpander::ExpandVariables(std::string& s, s = expandedInput; return s; } - char c = s[start + 1]; - // if the next char after the < is not A-Za-z then + + // if the previous character is a '$', this is a generator expression + // or if the next char after the < is not A-Za-z then // skip it and try to find the next < in the string - if (!cmsysString_isalpha(c)) { + if ((handleGenex == HandleGenex::Yes && start != 0 && + s[start - 1] == '$') || + !cmsysString_isalpha(s[start + 1])) { start = s.find('<', start + 1); } else { // extract the var diff --git a/Source/cmProperty.h b/Source/cmProperty.h index 43e5fabcc6..eb8334ba7b 100644 --- a/Source/cmProperty.h +++ b/Source/cmProperty.h @@ -18,6 +18,7 @@ public: VARIABLE, CACHED_VARIABLE, INSTALL, - FILE_SET + FILE_SET, + RULE }; }; diff --git a/Source/cmRule.cxx b/Source/cmRule.cxx new file mode 100644 index 0000000000..af755045d8 --- /dev/null +++ b/Source/cmRule.cxx @@ -0,0 +1,604 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#include "cmRule.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cmsys/RegularExpression.hxx" + +#include "cmExecutionStatus.h" +#include "cmFileSet.h" +#include "cmFileSetMetadata.h" +#include "cmGlobalGenerator.h" +#include "cmList.h" +#include "cmListFileCache.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPlaceholderExpander.h" +#include "cmRange.h" +#include "cmSourceFile.h" +#include "cmStringAlgorithms.h" +#include "cmTarget.h" +#include "cmValue.h" + +namespace { +cm::string_view const NAME = "NAME"_s; +cm::string_view const OUTPUT = "OUTPUT"_s; +cm::string_view const COMMAND = "COMMAND"_s; +cm::string_view const COMMAND_COUNT = "COMMAND_COUNT"_s; +cm::string_view const COMMAND_EXPAND_LISTS = "COMMAND_EXPAND_LISTS"_s; +cm::string_view const COMMENT = "COMMENT"_s; +cm::string_view const COMPILE_DEFINITIONS = "COMPILE_DEFINITIONS"_s; +cm::string_view const COMPILE_OPTIONS = "COMPILE_OPTIONS"_s; +cm::string_view const DEPENDS = "DEPENDS"_s; +cm::string_view const DEPENDS_EXPLICIT_ONLY = "DEPENDS_EXPLICIT_ONLY"_s; +cm::string_view const BYPRODUCTS = "BYPRODUCTS"_s; +cm::string_view const DEPFILE = "DEPFILE"_s; +cm::string_view const GLOBAL = "GLOBAL"_s; +cm::string_view const INCLUDE_DIRECTORIES = "INCLUDE_DIRECTORIES"_s; +cm::string_view const JOB_POOL_COMPILE = "JOB_POOL_COMPILE"_s; +cm::string_view const JOB_SERVER_AWARE = "JOB_SERVER_AWARE"_s; +cm::string_view const OUTPUT_FILE_SET = "OUTPUT_FILE_SET"_s; +cm::string_view const PARENT_RULE = "PARENT_RULE"_s; +cm::string_view const FILE_SET_CONFIGURATORS = "FILE_SET_CONFIGURATORS"_s; +cm::string_view const SOURCE_CONFIGURATORS = "SOURCE_CONFIGURATORS"_s; +cm::string_view const USES_TERMINAL = "USES_TERMINAL"_s; +cm::string_view const VERBATIM = "VERBATIM"_s; +cm::string_view const WORKING_DIRECTORY = "WORKING_DIRECTORY"_s; + +cmsys::RegularExpression commandIndex("^COMMAND_[0-9]+$"); + +enum class ReadOnlyCondition +{ + All, + Configuration, + Generation, +}; + +struct ReadOnlyProperty +{ + ReadOnlyProperty(ReadOnlyCondition cond) + : Condition{ cond } + { + } + + ReadOnlyCondition Condition; + + std::string Message(cm::string_view prop, cmRule const* rule) const + { + std::string msg; + switch (this->Condition) { + case ReadOnlyCondition::All: + msg = " property is read-only for rules (\""; + break; + case ReadOnlyCondition::Configuration: + msg = " property can't be set during configuration for rules (\""; + break; + case ReadOnlyCondition::Generation: + msg = " property can't be set during generation for rules (\""; + break; + } + return cmStrCat('"', prop, "\" ", msg, rule->GetName(), "\")\n"); + } + + bool IsReadOnly(cm::string_view prop, cmRule const* rule) const + { + if ((rule->InGeneration() && + this->Condition == ReadOnlyCondition::Configuration) || + (!rule->InGeneration() && + this->Condition == ReadOnlyCondition::Generation)) { + return false; + } + + rule->GetMakefile().IssueMessage(MessageType::FATAL_ERROR, + this->Message(prop, rule)); + + return true; + } +}; + +bool IsSettableProperty(cm::string_view prop, cmRule const* rule) +{ + using ROC = ReadOnlyCondition; + static std::unordered_map const + readOnlyProps{ { NAME, { ROC::All } }, + { OUTPUT, { ROC::All } }, + { COMMAND, { ROC::All } }, + { COMMAND_COUNT, { ROC::All } }, + { COMMAND_EXPAND_LISTS, { ROC::Generation } }, + { COMMENT, { ROC::Generation } }, + { COMPILE_DEFINITIONS, { ROC::Generation } }, + { COMPILE_OPTIONS, { ROC::Generation } }, + { DEPENDS, { ROC::All } }, + { DEPENDS_EXPLICIT_ONLY, { ROC::Generation } }, + { BYPRODUCTS, { ROC::All } }, + { DEPFILE, { ROC::All } }, + { GLOBAL, { ROC::Generation } }, + { INCLUDE_DIRECTORIES, { ROC::Generation } }, + { JOB_POOL_COMPILE, { ROC::Generation } }, + { JOB_SERVER_AWARE, { ROC::Generation } }, + { OUTPUT_FILE_SET, { ROC::Generation } }, + { PARENT_RULE, { ROC::All } }, + { FILE_SET_CONFIGURATORS, { ROC::All } }, + { SOURCE_CONFIGURATORS, { ROC::All } }, + { USES_TERMINAL, { ROC::Generation } }, + { VERBATIM, { ROC::Generation } }, + { WORKING_DIRECTORY, { ROC::Generation } } }; + + auto it = + readOnlyProps.find(commandIndex.find(prop.data()) ? COMMAND : prop); + + if (it != readOnlyProps.end()) { + return !(it->second.IsReadOnly(prop, rule)); + } + + if (rule->InGeneration()) { + rule->GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("rule properties cannot be changed during generation (\"", + rule->GetName(), "\")\n")); + return false; + } + + return true; +} + +class FileSetNamePlaceholderExpander : public cmPlaceholderExpander +{ +public: + using VariableMap = std::unordered_map; + VariableMap Values; + + bool InError = false; + +private: + std::string ExpandVariable(std::string const& variable) override + { + if (cm::contains(this->Values, variable)) { + return this->Values[variable]; + } + this->InError = true; + // If there is no variable defined, mark unresolved variable by '<' and '>' + return cmStrCat('<', variable, '>'); + } +}; +} + +cmRule::cmRule(cmMakefile& makefile, std::string name, cm::RuleScope scope) + : Makefile(&makefile) + , Name(std::move(name)) + , Scope(scope) +{ + // set some useful properties + this->SetProperty("VERBATIM", cmValue::True); + this->SetProperty("COMMAND_EXPAND_LISTS", cmValue::True); + this->SetProperty("OUTPUT_FILE_SET", + "__cmake_rule____outputs;SOURCES"); +} + +cmRule::cmRule(cmRule const& parent, cmMakefile& makefile, std::string name, + cm::RuleScope scope) + : Makefile(&makefile) + , Name(std::move(name)) + , Scope(scope) + , ConfiguratorsChain(parent.ConfiguratorsChain) + , Properties(parent.Properties) + , IncludeDirectories(parent.IncludeDirectories) + , CompileOptions(parent.CompileOptions) + , CompileDefinitions(parent.CompileDefinitions) + , Generation(parent.Generation) +{ +} +std::string const& cmRule::GetParentName() const +{ + static std::string empty; + + return empty; +} + +void cmRule::SetConfigurator(ConfiguratorType type, std::string configurator, + ChainConfigurators chain) +{ + this->Configurators[type] = std::move(configurator); + if (chain == ChainConfigurators::Yes) { + this->ConfiguratorsChain[type].emplace_back(&this->GetName(), + &this->Configurators[type]); + } else { + this->ConfiguratorsChain[type].assign( + 1, + ConfiguratorSet::value_type{ &this->GetName(), + &this->Configurators[type] }); + } +} +cmRule::ConfiguratorSet const& cmRule::GetConfigurators( + ConfiguratorType type) const +{ + static ConfiguratorSet emptySet; + + if (this->HasConfigurators(type)) { + return this->ConfiguratorsChain.at(type); + } + return emptySet; +} + +bool cmRule::HasConfigurators(ConfiguratorType type) const +{ + return cm::contains(this->ConfiguratorsChain, type); +} + +cmFileSet* cmRule::GetOutputFileSet(cmTarget* target, + cmFileSet const* fileSet) const +{ + cmList outputFileSet{ this->GetProperty(std::string{ OUTPUT_FILE_SET }) }; + FileSetNamePlaceholderExpander expander; + expander.Values["RULE"] = this->GetName(); + expander.Values["TARGET"] = target->GetName(); + expander.Values["FILE_SET"] = fileSet->GetName(); + + std::string fsName{ outputFileSet[0] }; + expander.ExpandVariables(fsName); + if (expander.InError) { + this->GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Output File Set name, for rule \"", this->GetName(), + "\", did not expand correctly:\n \"", fsName, "\".")); + return nullptr; + } + + auto result = target->GetOrCreateFileSet(fsName, outputFileSet[1], + fileSet->GetVisibility()); + if (!result.second && result.first->GetType() != outputFileSet[1]) { + this->GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The output file set \"", fsName, "\", for the target \"", + target->GetName(), "\", has the type \"", + result.first->GetType(), "\" instead of \"", outputFileSet[1], + "\", as specified by the rule \"", this->GetName(), "\".")); + return nullptr; + } + + return result.first; +} + +cmBTStringRange cmRule::GetIncludeDirectories() const +{ + return cmMakeRange(this->IncludeDirectories); +} + +cmBTStringRange cmRule::GetCompileOptions() const +{ + return cmMakeRange(this->CompileOptions); +} + +cmBTStringRange cmRule::GetCompileDefinitions() const +{ + return cmMakeRange(this->CompileDefinitions); +} + +void cmRule::SetProperty(std::string const& prop, cmValue value) +{ + if (!IsSettableProperty(prop, this)) { + return; + } + + if (prop == GLOBAL) { + if (!value.IsOn()) { + this->GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("GLOBAL property can't be set to FALSE on rules (\"", + this->GetName(), "\")")); + return; + } + /* no need to change anything if value does not change */ + if (!this->IsGloballyVisible()) { + this->Scope = cm::RuleScope::Global; + this->GetMakefile().GetGlobalGenerator()->IndexRule(this); + } + } else if (prop == OUTPUT_FILE_SET) { + cmList fileSet{ value }; + if (fileSet.size() != 2) { + this->GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + "OUTPUT_FILE_SET property require a list of 2 elements:\n " + "\"name;type\""); + return; + } + if (!cm::FileSetMetadata::IsKnownType(fileSet[1])) { + this->GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "specified file set type is erroneous. The supported types are: ", + cmJoin(cm::FileSetMetadata::GetKnownTypes(), ", "), '.')); + return; + } + this->Properties.SetProperty(prop, value); + } else if (prop == INCLUDE_DIRECTORIES) { + this->IncludeDirectories.clear(); + if (value) { + cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); + this->IncludeDirectories.emplace_back(value, lfbt); + } + } else if (prop == COMPILE_OPTIONS) { + this->CompileOptions.clear(); + if (value) { + cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); + this->CompileOptions.emplace_back(value, lfbt); + } + } else if (prop == COMPILE_DEFINITIONS) { + this->CompileDefinitions.clear(); + if (value) { + cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); + this->CompileDefinitions.emplace_back(value, lfbt); + } + } else { + this->Properties.SetProperty(prop, value); + } +} + +void cmRule::AppendProperty(std::string const& prop, std::string const& value, + bool asString) +{ + if (!IsSettableProperty(prop, this)) { + return; + } + + if (prop == GLOBAL) { + this->GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("GLOBAL property can't be appended, only set on rules (\"", + this->GetName(), "\")\n")); + return; + } + + if (prop == INCLUDE_DIRECTORIES) { + if (!value.empty()) { + cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); + this->IncludeDirectories.emplace_back(value, lfbt); + } + } else if (prop == COMPILE_OPTIONS) { + if (!value.empty()) { + cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); + this->CompileOptions.emplace_back(value, lfbt); + } + } else if (prop == COMPILE_DEFINITIONS) { + if (!value.empty()) { + cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); + this->CompileDefinitions.emplace_back(value, lfbt); + } + } else { + this->Properties.AppendProperty(prop, value, asString); + } +} + +cmValue cmRule::GetProperty(std::string const& prop) const +{ + static std::string value; + + if (prop == NAME) { + return cmValue{ this->GetName() }; + } + if (prop == OUTPUT) { + value = cmList::to_string(this->GetOutputs()); + return cmValue{ value }; + } + if (prop == COMMAND) { + value = cmList::to_string(this->GetCommands()[0]); + return cmValue{ value }; + } + if (prop == COMMAND_COUNT) { + value = std::to_string(this->GetCommands().size()); + return cmValue{ value }; + } + if (commandIndex.find(prop)) { + auto index = std::stoul(prop.substr(8)); + value = index >= this->GetCommands().size() + ? "NOTFOUND" + : cmList::to_string(this->GetCommands()[index]); + return cmValue{ value }; + } + if (prop == DEPENDS) { + value = cmList::to_string(this->GetDepends()); + return cmValue{ value }; + } + if (prop == BYPRODUCTS) { + value = cmList::to_string(this->GetByproducts()); + return cmValue{ value }; + } + if (prop == DEPFILE) { + return cmValue{ this->GetDepfile() }; + } + if (prop == GLOBAL) { + return this->IsGloballyVisible() ? cmValue::True : cmValue::False; + } + if (prop == PARENT_RULE) { + return cmValue{ this->GetParentName() }; + } + if (prop == FILE_SET_CONFIGURATORS || prop == SOURCE_CONFIGURATORS) { + ConfiguratorType type = prop == FILE_SET_CONFIGURATORS + ? ConfiguratorType::FileSet + : ConfiguratorType::Source; + + if (this->HasConfigurators(type)) { + auto cfgs_set = this->GetConfigurators(type); + std::vector cfgs; + cfgs.reserve(cfgs_set.size()); + + std::transform(cfgs_set.cbegin(), cfgs_set.cend(), + std::back_inserter(cfgs), + [](ConfiguratorSet::value_type cfg) -> std::string { + return *cfg.second; + }); + + value = cmList::to_string(cfgs); + return cmValue{ value }; + } + + return nullptr; + } + + // Check for the properties with backtraces. + if (prop == INCLUDE_DIRECTORIES) { + if (this->IncludeDirectories.empty()) { + return nullptr; + } + + value = cmList::to_string(this->IncludeDirectories); + return cmValue{ value }; + } + + if (prop == COMPILE_OPTIONS) { + if (this->CompileOptions.empty()) { + return nullptr; + } + + value = cmList::to_string(this->CompileOptions); + return cmValue{ value }; + } + + if (prop == COMPILE_DEFINITIONS) { + if (this->CompileDefinitions.empty()) { + return nullptr; + } + + value = cmList::to_string(this->CompileDefinitions); + return cmValue{ value }; + } + + return this->Properties.GetPropertyValue(prop); +} + +void cmRule::CheckProperty(std::string const& prop, cmMakefile& context) const +{ + // Certain properties need checking. + if (prop == GLOBAL) { + auto const& rules = context.GetOwnedRules(); + auto it = std::find_if(rules.begin(), rules.end(), + [&](std::unique_ptr const& rule) -> bool { + return this == rule.get(); + }); + if (it == rules.end()) { + context.IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Attempt to promote rule \"", this->GetName(), + "\" to global scope (by setting GLOBAL) " + "which is not created in this directory.")); + } + } +} + +namespace { +cmsys::RegularExpression PatternRegex{ "(^[A-Z][A-Z0-9_]+)=(.*)$" }; + +void UpdatePatterns(cmRule::PatternSet& patterns, cmList const& newPatterns) +{ + for (auto const& pattern : newPatterns) { + if (PatternRegex.find(pattern)) { + auto it = std::find_if(patterns.begin(), patterns.end(), + [](cmRule::Pattern const& item) { + return item.Name == PatternRegex.match(1); + }); + if (it == patterns.end()) { + patterns.emplace_back(PatternRegex.match(1), PatternRegex.match(2)); + } else { + it->Value = PatternRegex.match(2); + } + } + } +} + +bool ConfigureRule(cmRule::ConfiguratorSet const& configurators, + cmMakefile* mf, cmRule::PatternSet& patterns, + std::string const& rule, std::string const& target, + std::string const& fileSet, + std::string const& outputFileSet, + std::string const& source = {}) +{ + cm::string_view patternsVariable{ "_CMAKE_RULE_PATTERNS"_s }; + + for (auto const& item : configurators) { + // The validator command will be executed in an isolated scope. + cmMakefile::ScopePushPop varScope(mf); + cmMakefile::PolicyPushPop polScope(mf); + static_cast(varScope); + static_cast(polScope); + + std::vector args{ + cmListFileArgument{ rule, cmListFileArgument::Unquoted, 0 }, + cmListFileArgument{ target, cmListFileArgument::Unquoted, 0 }, + cmListFileArgument{ fileSet, cmListFileArgument::Unquoted, 0 }, + cmListFileArgument{ outputFileSet, cmListFileArgument::Unquoted, 0 } + }; + if (!source.empty()) { + args.emplace_back(source, cmListFileArgument::Quoted, 0); + } + args.emplace_back(patternsVariable, cmListFileArgument::Unquoted, 0); + + cmListFileFunction command(*item.second, 0, 0, args); + cmExecutionStatus status(*mf); + if (!mf->ExecuteCommand(command, status)) { + mf->IssueMessage(MessageType::FATAL_ERROR, + cmStrCat("Erroneous execution of the CONFIGURATOR \"", + *item.second, "\", from the RULE \"", + *item.first, "\", for the FILE_SET \"", + fileSet, "\" of TARGET \"", target, "\".")); + return false; + } + UpdatePatterns( + patterns, cmList{ mf->GetDefinition(std::string{ patternsVariable }) }); + } + + return true; +} +} + +bool cmRule::Instantiate(cmTarget const* target, cmFileSet const* fileSet, + cmFileSet const* outputFileSet, + PatternSet& patterns) const +{ + this->SwitchMode(); + + // First, take patterns from RULE_PATTERNS file set property, if any + if (cmValue fsPatterns = fileSet->GetProperty("RULE_PATTERNS")) { + UpdatePatterns(patterns, cmList{ fsPatterns, cmList::EmptyElements::No }); + } + + if (this->HasConfigurators(ConfiguratorType::FileSet)) { + // Finalize file set level configuration by calling user's commands + return ConfigureRule(this->GetConfigurators(ConfiguratorType::FileSet), + fileSet->GetMakefile(), patterns, this->GetName(), + target->GetName(), fileSet->GetName(), + outputFileSet->GetName()); + } + + return true; +} + +bool cmRule::Instantiate(cmTarget const* target, cmFileSet const* fileSet, + cmFileSet const* outputFileSet, cmSourceFile* source, + PatternSet& patterns) const +{ + // First, take patterns from _PATTERNS source file property, if any + if (cmValue sfPatterns = + source->GetProperty(cmStrCat(this->GetName(), "_PATTERNS"))) { + UpdatePatterns(patterns, cmList{ sfPatterns, cmList::EmptyElements::No }); + } + + if (this->HasConfigurators(ConfiguratorType::Source)) { + // Before custom commands generation, finalize source level + // configuration by calling user's commands + return ConfigureRule(this->GetConfigurators(ConfiguratorType::Source), + fileSet->GetMakefile(), patterns, this->GetName(), + target->GetName(), fileSet->GetName(), + outputFileSet->GetName(), source->GetFullPath()); + } + + return true; +} diff --git a/Source/cmRule.h b/Source/cmRule.h new file mode 100644 index 0000000000..06924cff04 --- /dev/null +++ b/Source/cmRule.h @@ -0,0 +1,242 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include +#include +#include +#include +#include +#include + +#include "cmAlgorithms.h" +#include "cmListFileCache.h" +#include "cmPropertyMap.h" +#include "cmValue.h" + +class cmMakefile; +class cmTarget; +class cmFileSet; +class cmSourceFile; + +namespace cm { + +enum class RuleScope +{ + Local, + Global +}; + +template +struct enum_hash +{ + typename std::enable_if::value, std::size_t>::type + operator()(E const key) const + { + return static_cast(key); + } +}; +} + +// +// cmRule: base class for various rule types +// +class cmRule +{ +public: + using CustomCommand = std::vector; + using CustomCommands = std::vector; + + virtual ~cmRule() = default; + + cmRule& operator=(cmRule const&) = delete; + + cmMakefile& GetMakefile() const { return *this->Makefile; } + + /** Get the name of the rule */ + std::string const& GetName() const { return this->Name; } + virtual std::string const& GetParentName() const; + + /** Get the command lines. */ + virtual CustomCommands const& GetCommands() const = 0; + /** Get the output files. */ + virtual std::vector const& GetOutputs() const = 0; + + /** Get the extra files produced by the command. */ + virtual std::vector const& GetByproducts() const = 0; + + /** Get the vector that holds the list of dependencies. */ + virtual std::vector const& GetDepends() const = 0; + /** Get the file that holds the list of dependencies. */ + virtual std::string const& GetDepfile() const = 0; + + enum class ConfiguratorType + { + FileSet, + Source + }; + enum class ChainConfigurators + { + No, + Yes + }; + using ConfiguratorSet = + std::vector>; + void SetConfigurator(ConfiguratorType type, std::string configurator, + ChainConfigurators chain = ChainConfigurators::No); + ConfiguratorSet const& GetConfigurators(ConfiguratorType type) const; + + bool HasConfigurators(ConfiguratorType type) const; + + bool IsGloballyVisible() const + { + return this->Scope == cm::RuleScope::Global; + } + + cmFileSet* GetOutputFileSet(cmTarget* target, + cmFileSet const* fileSet) const; + + // Special properties + cmBTStringRange GetIncludeDirectories() const; + + cmBTStringRange GetCompileOptions() const; + + cmBTStringRange GetCompileDefinitions() const; + + //! Set/Get a property of this rule + void SetProperty(std::string const& prop, cmValue value); + void SetProperty(std::string const& prop, std::nullptr_t) + { + this->SetProperty(prop, cmValue{ nullptr }); + } + void RemoveProperty(std::string const& prop) + { + this->SetProperty(prop, cmValue{ nullptr }); + } + void SetProperty(std::string const& prop, std::string const& value) + { + this->SetProperty(prop, cmValue{ value }); + } + void AppendProperty(std::string const& prop, std::string const& value, + bool asString = false); + cmValue GetProperty(std::string const& prop) const; + + void CheckProperty(std::string const& prop, cmMakefile& context) const; + + bool InGeneration() const { return this->Generation; } + + struct Pattern + { + Pattern(std::string name, std::string value) + : Name(std::move(name)) + , Value(std::move(value)) + { + } + + std::string Name; + std::string Value; + }; + using PatternSet = std::vector; + bool Instantiate(cmTarget const* target, cmFileSet const* fileSet, + cmFileSet const* outputFileSet, PatternSet& patterns) const; + bool Instantiate(cmTarget const* target, cmFileSet const* fileSet, + cmFileSet const* outputFileSet, cmSourceFile* source, + PatternSet& patterns) const; + +protected: + cmRule(cmMakefile& makefile, std::string name, cm::RuleScope scope); + cmRule(cmRule const& parent, cmMakefile& makefile, std::string name, + cm::RuleScope scope); + +private: + // switch context cmRule usage + void SwitchMode() const { this->Generation = true; } + + cmMakefile* Makefile; + std::string Name; + cm::RuleScope Scope = cm::RuleScope::Local; + std::unordered_map> + Configurators; + std::unordered_map> + ConfiguratorsChain; + cmPropertyMap Properties; + std::vector> IncludeDirectories; + std::vector> CompileOptions; + std::vector> CompileDefinitions; + // cmRule properties should not be changed during generation phase + // Use mutable field to track current phase independently of state of object + // (const or not) + mutable bool Generation = false; +}; + +// +// cmCustomRule: define a template used to generate custom commands +// +class cmCustomRule : public cmRule +{ +public: + cmCustomRule(cmMakefile& makefile, std::string name, CustomCommands commands, + std::vector outputs, + cm::RuleScope scope = cm::RuleScope::Local); + + cmCustomRule& operator=(cmCustomRule const&) = delete; + + /** Get the command lines. */ + CustomCommands const& GetCommands() const override; + /** Get the output files. */ + std::vector const& GetOutputs() const override; + + /** Set/Get the extra files produced by the command. */ + void SetByproducts(std::vector byproducts); + std::vector const& GetByproducts() const override; + + /** Set/Get the vector that holds the list of dependencies. */ + void SetDepends(std::vector depends); + std::vector const& GetDepends() const override; + /** Set/Get the file that holds the list of dependencies. */ + void SetDepfile(std::string depfile); + std::string const& GetDepfile() const override; + +private: + CustomCommands Commands; + std::vector Outputs; + std::vector Byproducts; + std::vector Depends; + std::string Depfile; +}; + +// +// cmSpecializedRule: rule embedding another rule and enabling properties +// customization. +// +class cmSpecializedRule : public cmRule +{ +public: + cmSpecializedRule(cmMakefile& makefile, std::string name, + cmRule const& parent, + cm::RuleScope scope = cm::RuleScope::Local); + + cmSpecializedRule& operator=(cmSpecializedRule const&) = delete; + + std::string const& GetParentName() const override; + + /** Get the command lines. */ + CustomCommands const& GetCommands() const override; + /** Get the output files. */ + std::vector const& GetOutputs() const override; + + /** Get the extra files produced by the command. */ + std::vector const& GetByproducts() const override; + + /** Get the vector that holds the list of dependencies. */ + std::vector const& GetDepends() const override; + /** Get the file that holds the list of dependencies. */ + std::string const& GetDepfile() const override; + +private: + cmRule const& ParentRule; +}; diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx index 749b591d06..8b76476a77 100644 --- a/Source/cmSetPropertyCommand.cxx +++ b/Source/cmSetPropertyCommand.cxx @@ -20,6 +20,7 @@ #include "cmPolicies.h" #include "cmProperty.h" #include "cmRange.h" +#include "cmRule.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmState.h" @@ -41,6 +42,11 @@ bool HandleDirectoryMode(cmExecutionStatus& status, std::string const& propertyName, std::string const& propertyValue, bool appendAsString, bool appendMode, bool remove); +bool HandleRuleMode(cmExecutionStatus& status, + std::set const& names, + std::string const& propertyName, + std::string const& propertyValue, bool appendAsString, + bool appendMode, bool remove); bool HandleTargetMode(cmExecutionStatus& status, std::set const& names, std::string const& propertyName, @@ -453,6 +459,8 @@ bool cmSetPropertyCommand(std::vector const& args, scope = cmProperty::GLOBAL; } else if (scopeName == "DIRECTORY") { scope = cmProperty::DIRECTORY; + } else if (scopeName == "RULE") { + scope = cmProperty::RULE; } else if (scopeName == "TARGET") { scope = cmProperty::TARGET; } else if (scopeName == "FILE_SET") { @@ -466,9 +474,10 @@ bool cmSetPropertyCommand(std::vector const& args, } else if (scopeName == "INSTALL") { scope = cmProperty::INSTALL; } else { - status.SetError(cmStrCat("given invalid scope ", scopeName, - ". Valid scopes are GLOBAL, DIRECTORY, TARGET, " - "FILE_SET, SOURCE, TEST, CACHE, INSTALL.")); + status.SetError( + cmStrCat("given invalid scope ", scopeName, + ". Valid scopes are GLOBAL, DIRECTORY, RULE, TARGET, " + "FILE_SET, SOURCE, TEST, CACHE, INSTALL.")); return false; } @@ -574,6 +583,9 @@ bool cmSetPropertyCommand(std::vector const& args, case cmProperty::DIRECTORY: return HandleDirectoryMode(status, names, propertyName, propertyValue, appendAsString, appendMode, remove); + case cmProperty::RULE: + return HandleRuleMode(status, names, propertyName, propertyValue, + appendAsString, appendMode, remove); case cmProperty::TARGET: return HandleTargetMode(status, names, propertyName, propertyValue, appendAsString, appendMode, remove); @@ -702,6 +714,36 @@ bool HandleDirectoryMode(cmExecutionStatus& status, return true; } +bool HandleRuleMode(cmExecutionStatus& status, + std::set const& names, + std::string const& propertyName, + std::string const& propertyValue, bool appendAsString, + bool appendMode, bool remove) +{ + for (std::string const& name : names) { + if (cmRule* rule = status.GetMakefile().FindRuleToUse(name)) { + // Handle the current rule. + // Set or append the property. + if (appendMode) { + rule->AppendProperty(propertyName, propertyValue, appendAsString); + } else { + if (remove) { + rule->SetProperty(propertyName, nullptr); + } else { + rule->SetProperty(propertyName, propertyValue); + } + } + // Check the resulting value. + rule->CheckProperty(propertyName, status.GetMakefile()); + } else { + status.SetError(cmStrCat("could not find RULE ", name, + ". Perhaps it has not yet been created.")); + return false; + } + } + return true; +} + bool HandleTargetMode(cmExecutionStatus& status, std::set const& names, std::string const& propertyName, diff --git a/Source/cmSpecializedRule.cxx b/Source/cmSpecializedRule.cxx new file mode 100644 index 0000000000..998d23ef98 --- /dev/null +++ b/Source/cmSpecializedRule.cxx @@ -0,0 +1,45 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file LICENSE.rst or https://cmake.org/licensing for details. */ + +#include +#include + +#include "cmRule.h" + +class cmMakefile; + +cmSpecializedRule::cmSpecializedRule(cmMakefile& makefile, std::string name, + cmRule const& parent, cm::RuleScope scope) + : cmRule(parent, makefile, std::move(name), scope) + , ParentRule(parent) +{ +} + +std::string const& cmSpecializedRule::GetParentName() const +{ + return this->ParentRule.GetName(); +} + +cmSpecializedRule::CustomCommands const& cmSpecializedRule::GetCommands() const +{ + return this->ParentRule.GetCommands(); +} +std::vector const& cmSpecializedRule::GetOutputs() const +{ + return this->ParentRule.GetOutputs(); +} + +std::vector const& cmSpecializedRule::GetByproducts() const +{ + return this->ParentRule.GetByproducts(); +} + +std::vector const& cmSpecializedRule::GetDepends() const +{ + return this->ParentRule.GetDepends(); +} + +std::string const& cmSpecializedRule::GetDepfile() const +{ + return this->ParentRule.GetDepfile(); +} diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx index 773004621a..0332212a00 100644 --- a/Source/cmStateDirectory.cxx +++ b/Source/cmStateDirectory.cxx @@ -386,6 +386,10 @@ cmValue cmStateDirectory::GetProperty(std::string const& prop, output = cmList::to_string(this->DirectoryState->ImportedTargetNames); return cmValue(output); } + if (prop == "RULES"_s) { + output = cmList::to_string(this->DirectoryState->RuleNames); + return cmValue(output); + } if (prop == "LISTFILE_STACK") { std::vector listFiles; @@ -462,3 +466,8 @@ void cmStateDirectory::AddImportedTargetName(std::string const& name) { this->DirectoryState->ImportedTargetNames.emplace_back(name); } + +void cmStateDirectory::AddRuleName(std::string const& name) +{ + this->DirectoryState->RuleNames.push_back(name); +} diff --git a/Source/cmStateDirectory.h b/Source/cmStateDirectory.h index 49ab4408a7..293fac78b3 100644 --- a/Source/cmStateDirectory.h +++ b/Source/cmStateDirectory.h @@ -75,6 +75,8 @@ public: void AddNormalTargetName(std::string const& name); void AddImportedTargetName(std::string const& name); + void AddRuleName(std::string const& name); + private: cmLinkedTree::iterator DirectoryState; diff --git a/Source/cmStatePrivate.h b/Source/cmStatePrivate.h index 3c7655ebee..b3ee5d17fe 100644 --- a/Source/cmStatePrivate.h +++ b/Source/cmStatePrivate.h @@ -105,6 +105,8 @@ struct cmStateDetail::BuildsystemDirectoryStateType std::vector NormalTargetNames; std::vector ImportedTargetNames; + std::vector RuleNames; + std::set Projects; std::string ProjectName; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 623bc41942..76e9d2f9a3 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -171,6 +171,15 @@ struct FileSetType cmTargetInternals const* impl) const; }; +struct FileSetRule +{ + std::vector> SelfEntries; + std::vector> InterfaceEntries; + + void AddFileSet(std::string const& name, cm::FileSetMetadata::Visibility vis, + cmListFileBacktrace bt); +}; + struct UsageRequirementProperty { enum class AppendEmpty @@ -675,6 +684,7 @@ public: UsageRequirementProperty ImportedCxxModulesLinkLibraries; std::unordered_map FileSetTypes; + std::unordered_map FileSetRules; cmTargetInternals(std::string name, cm::TargetType type, cmTarget::Visibility visibility, cmMakefile* mf, @@ -917,6 +927,18 @@ cmPropertyMap FileSetType::GetProperties(cmTarget const* tgt, return propertyMap; } +void FileSetRule::AddFileSet(std::string const& name, + cm::FileSetMetadata::Visibility vis, + cmListFileBacktrace bt) +{ + if (cm::FileSetMetadata::VisibilityIsForSelf(vis)) { + this->SelfEntries.emplace_back(name, bt); + } + if (cm::FileSetMetadata::VisibilityIsForInterface(vis)) { + this->InterfaceEntries.emplace_back(name, std::move(bt)); + } +} + template bool UsageRequirementProperty::Write( cmTargetInternals const* impl, cm::optional const& bt, @@ -3331,14 +3353,16 @@ cmFileSet* cmTarget::GetFileSet(std::string const& name) std::pair cmTarget::GetOrCreateFileSet( std::string const& name, std::string const& type, - cm::FileSetMetadata::Visibility vis) + cm::FileSetMetadata::Visibility vis, cmMakefile* mf) { - auto result = this->impl->FileSets.emplace( - name, cmFileSet(this->GetMakefile(), this, name, type, vis)); + auto result = + this->impl->FileSets.emplace(name, cmFileSet(mf, this, name, type, vis)); if (result.second) { auto bt = this->impl->Makefile->GetBacktrace(); if (cm::contains(this->impl->FileSetTypes, type)) { this->impl->FileSetTypes.at(type).AddFileSet(name, vis, std::move(bt)); + } else { + this->impl->FileSetRules[type].AddFileSet(name, vis, std::move(bt)); } } return std::make_pair(&result.first->second, result.second); @@ -3365,25 +3389,36 @@ std::string cmTarget::GetInterfaceFileSetsPropertyName( return ""; } -std::vector cmTarget::GetAllFileSetNames() const +std::vector cmTarget::GetAllFileSetNames( + cm::FileSetMetadata::FileSetDomainSet domains) const { std::vector result; + bool const useNative = + domains.contains(cm::FileSetMetadata::FileSetDomain::NATIVE); + bool const useRule = + domains.contains(cm::FileSetMetadata::FileSetDomain::RULE); + for (auto const& it : this->impl->FileSets) { - result.push_back(it.first); + bool nativeType = + cm::contains(this->impl->FileSetTypes, it.second.GetType()); + + if ((useNative && nativeType) || (useRule && !nativeType)) { + result.push_back(it.first); + } } return result; } namespace { -std::vector RetrieveFileSetNames( +void RetrieveFileSetNames( std::unordered_map const& fileSetTypes, std::function< std::vector> const&(FileSetType const& fileSetType)> - GetFileSets) + GetFileSets, + std::vector& result) { - std::vector result; auto inserter = std::back_inserter(result); auto appendEntries = [=](std::vector> const& entries) { @@ -3396,27 +3431,79 @@ std::vector RetrieveFileSetNames( for (auto const& fileSetType : fileSetTypes) { appendEntries(GetFileSets(fileSetType.second)); } +} + +void RetrieveFileSetNames( + std::unordered_map const& fileSetRules, + std::function< + std::vector> const&(FileSetRule const& fileSetRule)> + GetFileSets, + std::vector& result) +{ + auto appendEntries = [&result](std::vector> const& entries) { + for (auto const& entry : entries) { + result.push_back(entry.Value); + } + }; + + for (auto const& fileSetRule : fileSetRules) { + appendEntries(GetFileSets(fileSetRule.second)); + } +} +} + +std::vector cmTarget::GetAllPrivateFileSets( + cm::FileSetMetadata::FileSetDomainSet domains) const +{ + std::vector result; + + if (domains.contains(cm::FileSetMetadata::FileSetDomain::NATIVE)) { + RetrieveFileSetNames( + this->impl->FileSetTypes, + [](FileSetType const& fileSetType) + -> std::vector> const& { + return fileSetType.SelfEntries.Entries; + }, + result); + } + if (domains.contains(cm::FileSetMetadata::FileSetDomain::RULE)) { + RetrieveFileSetNames( + this->impl->FileSetRules, + [](FileSetRule const& fileSetRule) + -> std::vector> const& { + return fileSetRule.SelfEntries; + }, + result); + } return result; } -} -std::vector cmTarget::GetAllPrivateFileSets() const +std::vector cmTarget::GetAllInterfaceFileSets( + cm::FileSetMetadata::FileSetDomainSet domains) const { - return RetrieveFileSetNames( - this->impl->FileSetTypes, - [](FileSetType const& fileSetType) -> std::vector> const& { - return fileSetType.SelfEntries.Entries; - }); -} + std::vector result; -std::vector cmTarget::GetAllInterfaceFileSets() const -{ - return RetrieveFileSetNames( - this->impl->FileSetTypes, - [](FileSetType const& fileSetType) -> std::vector> const& { - return fileSetType.InterfaceEntries.Entries; - }); + if (domains.contains(cm::FileSetMetadata::FileSetDomain::NATIVE)) { + RetrieveFileSetNames( + this->impl->FileSetTypes, + [](FileSetType const& fileSetType) + -> std::vector> const& { + return fileSetType.InterfaceEntries.Entries; + }, + result); + } + if (domains.contains(cm::FileSetMetadata::FileSetDomain::RULE)) { + RetrieveFileSetNames( + this->impl->FileSetRules, + [](FileSetRule const& fileSetRule) + -> std::vector> const& { + return fileSetRule.InterfaceEntries; + }, + result); + } + + return result; } bool cmTarget::HasFileSets() const diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 0997fdef6f..9d022bf85d 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -17,6 +17,7 @@ #include #include "cmAlgorithms.h" +#include "cmFileSetMetadata.h" #include "cmListFileCache.h" #include "cmPolicies.h" #include "cmStateTypes.h" @@ -25,12 +26,6 @@ #include "cmTargetTypes.h" #include "cmValue.h" -namespace cm { -namespace FileSetMetadata { -enum class Visibility; -} -} - class cmCustomCommand; class cmFileSet; class cmFindPackageStack; @@ -385,11 +380,23 @@ public: cmFileSet* GetFileSet(std::string const& name); std::pair GetOrCreateFileSet( std::string const& name, std::string const& type, - cm::FileSetMetadata::Visibility vis); + cm::FileSetMetadata::Visibility vis) + { + return this->GetOrCreateFileSet(name, type, vis, this->GetMakefile()); + } + std::pair GetOrCreateFileSet( + std::string const& name, std::string const& type, + cm::FileSetMetadata::Visibility vis, cmMakefile* mf); - std::vector GetAllFileSetNames() const; - std::vector GetAllPrivateFileSets() const; - std::vector GetAllInterfaceFileSets() const; + std::vector GetAllFileSetNames( + cm::FileSetMetadata::FileSetDomainSet domains = { + cm::FileSetMetadata::FileSetDomain::NATIVE }) const; + std::vector GetAllPrivateFileSets( + cm::FileSetMetadata::FileSetDomainSet domains = { + cm::FileSetMetadata::FileSetDomain::NATIVE }) const; + std::vector GetAllInterfaceFileSets( + cm::FileSetMetadata::FileSetDomainSet domains = { + cm::FileSetMetadata::FileSetDomain::NATIVE }) const; std::string GetFileSetsPropertyName(std::string const& type) const; std::string GetInterfaceFileSetsPropertyName(std::string const& type) const; diff --git a/Source/cmTargetSourcesCommand.cxx b/Source/cmTargetSourcesCommand.cxx index 5d178268c7..5353e14e79 100644 --- a/Source/cmTargetSourcesCommand.cxx +++ b/Source/cmTargetSourcesCommand.cxx @@ -204,24 +204,22 @@ bool TargetSourcesImpl::HandleOneFileSet( if (!unparsed.empty()) { this->SetError( - cmStrCat("Unrecognized keyword: \"", unparsed.front(), '"')); + cmStrCat("Unrecognized keyword: \"", unparsed.front(), "\".")); return false; } if (args.FileSet.empty()) { - this->SetError("FILE_SET must not be empty"); + this->SetError("FILE_SET must not be empty."); return false; } - if (this->Target->GetType() == cm::TargetType::UTILITY) { - this->SetError("FILE_SETs may not be added to custom targets"); - return false; - } - - if (!args.Type.empty() && !cm::FileSetMetadata::IsKnownType(args.Type)) { + if (!args.Type.empty() && !cm::FileSetMetadata::IsKnownType(args.Type) && + // rule must be known from the target directory + !this->Makefile->FindRuleToUse(args.Type)) { this->SetError( - cmStrCat("File set TYPE may only be \"", - cmJoin(cm::FileSetMetadata::GetKnownTypes(), "\", \""), '"')); + cmStrCat("File set TYPE may only be one of the built-in types \"", + cmJoin(cm::FileSetMetadata::GetKnownTypes(), "\", \""), + "\" or a RULE visible from the current directory.")); return false; } if (args.Type.empty() && args.FileSet[0] >= 'A' && args.FileSet[0] <= 'Z' && @@ -229,14 +227,14 @@ bool TargetSourcesImpl::HandleOneFileSet( this->SetError( cmStrCat("FILE_SET names starting with a capital letter are reserved " "for built-in file sets and may only be \"", - cmJoin(cm::FileSetMetadata::GetKnownTypes(), "\", \""), '"')); + cmJoin(cm::FileSetMetadata::GetKnownTypes(), "\", \""), "\".")); return false; } if (!args.Type.empty() && args.FileSet[0] >= 'A' && args.FileSet[0] <= 'Z' && args.Type != args.FileSet) { this->SetError(cmStrCat("FILE_SET name starting with a capital letter " "must match the TYPE name \"", - args.Type, '"')); + args.Type, "\".")); return false; } @@ -246,26 +244,36 @@ bool TargetSourcesImpl::HandleOneFileSet( if (!isDefault && !cm::FileSetMetadata::IsValidName(args.FileSet)) { this->SetError("Non-default file set name must contain only letters, " "numbers, and underscores, and must not start with a " - "capital letter or underscore"); + "capital letter or underscore."); return false; } std::string type = isDefault ? args.FileSet : args.Type; + + if (cm::FileSetMetadata::IsKnownType(type) && + this->Target->GetType() == cm::TargetType::UTILITY) { + this->SetError( + cmStrCat("FILE_SETs of type \"", + cmJoin(cm::FileSetMetadata::GetKnownTypes(), "\", \""), + "\" may not be added to custom targets.")); + return false; + } + cm::FileSetMetadata::Visibility visibility = cm::FileSetMetadata::VisibilityFromName(scope, this->Makefile); if (this->Target->IsFrameworkOnApple() && !cm::FileSetMetadata::IsFrameworkSupported(type)) { this->SetError(cmStrCat(R"(FILE_SETs, of type ")", type, - R"(", may not be added to FRAMEWORK targets)")); + R"(", may not be added to FRAMEWORK targets.)")); return false; } - auto fileSet = - this->Target->GetOrCreateFileSet(args.FileSet, type, visibility); + auto fileSet = this->Target->GetOrCreateFileSet(args.FileSet, type, + visibility, this->Makefile); if (fileSet.second) { if (type.empty()) { - this->SetError("Must specify a TYPE when creating file set"); + this->SetError("Must specify a TYPE when creating file set."); return false; } @@ -289,7 +297,7 @@ bool TargetSourcesImpl::HandleOneFileSet( if (type == cm::FileSetMetadata::CXX_MODULES) { this->SetError(cmStrCat(R"(File set TYPE ")", cm::FileSetMetadata::CXX_MODULES, - R"(" may not have "INTERFACE" scope)")); + R"(" may not have "INTERFACE" scope.)")); return false; } } @@ -312,16 +320,16 @@ bool TargetSourcesImpl::HandleOneFileSet( if (!args.Type.empty() && args.Type != type) { this->SetError(cmStrCat( "Type \"", args.Type, "\" for file set \"", fileSet.first->GetName(), - "\" does not match original type \"", type, '"')); + "\" does not match original type \"", type, "\".")); return false; } if (visibility != fileSet.first->GetVisibility()) { - this->SetError(cmStrCat("Scope ", scope, " for file set \"", - args.FileSet, - "\" does not match original scope ", - cm::FileSetMetadata::VisibilityToName( - fileSet.first->GetVisibility()))); + this->SetError(cmStrCat( + "Scope ", scope, " for file set \"", args.FileSet, + "\" does not match original scope ", + cm::FileSetMetadata::VisibilityToName(fileSet.first->GetVisibility()), + '.')); return false; } } diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 400304cfe3..a172849c6b 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -573,6 +573,7 @@ add_RunCMake_test(GenEx-LINK_GROUP) add_RunCMake_test(GenEx-TARGET_FILE -DLINKER_SUPPORTS_PDB=${LINKER_SUPPORTS_PDB}) add_RunCMake_test(GenEx-TARGET_IMPORT_FILE) add_RunCMake_test(GenEx-GENEX_EVAL) +add_RunCMake_test(GenEx-RULE_PROPERTY) add_RunCMake_test(GenEx-SOURCE_EXISTS) add_RunCMake_test(GenEx-SOURCE_PROPERTY) add_RunCMake_test(GenEx-FILE_SET_EXISTS) @@ -724,6 +725,8 @@ add_RunCMake_test(MaxRecursionDepth) add_RunCMake_test(add_custom_command) add_RunCMake_test(add_custom_target) +add_RunCMake_test(add_custom_rule) +add_RunCMake_test(CustomRule -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}) add_RunCMake_test(add_dependencies) add_RunCMake_test(add_executable) add_RunCMake_test(add_library) diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnInterface-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnInterface-stderr.txt index 607d638001..a957d3f0f0 100644 --- a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnInterface-stderr.txt +++ b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnInterface-stderr.txt @@ -1,4 +1,4 @@ CMake Error at FileSetModulesInterfaceOnInterface\.cmake:[0-9]+ \(target_sources\): - target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE" scope + target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE" scope\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnStatic-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnStatic-stderr.txt index 606c6792af..e99ea97a29 100644 --- a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnStatic-stderr.txt +++ b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceOnStatic-stderr.txt @@ -1,4 +1,4 @@ CMake Error at FileSetModulesInterfaceOnStatic\.cmake:[0-9]+ \(target_sources\): - target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE" scope + target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE" scope\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/CustomRule/CMakeLists.txt b/Tests/RunCMake/CustomRule/CMakeLists.txt new file mode 100644 index 0000000000..0ceb19a24c --- /dev/null +++ b/Tests/RunCMake/CustomRule/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.4) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CustomRule/Configurators.cmake b/Tests/RunCMake/CustomRule/Configurators.cmake new file mode 100644 index 0000000000..ee20b9a77a --- /dev/null +++ b/Tests/RunCMake/CustomRule/Configurators.cmake @@ -0,0 +1,64 @@ + +enable_language(C) + +if (NOT CMAKE_C_CREATE_PREPROCESSED_SOURCE) + return() +endif() + +if (NOT CMAKE_C_DEFINE_FLAG) + set(CMAKE_C_DEFINE_FLAG "-D") +endif() + +# clean-up the command line +string(REPLACE "${CMAKE_START_TEMP_FILE}" "" create_preprocessed_source "${CMAKE_C_CREATE_PREPROCESSED_SOURCE}") +string(REPLACE "${CMAKE_END_TEMP_FILE}" "" create_preprocessed_source "${create_preprocessed_source}") +string(REPLACE "" "" create_preprocessed_source "${create_preprocessed_source}") +separate_arguments(create_preprocessed_source UNIX_COMMAND "${create_preprocessed_source}") + + +# Use file set configurator +function(fs_configurator rule target fileset outputFileset patterns) + set(${patterns} "INPUT=$>;OUTPUT_DIR=/${target};CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$/.c>;INCLUDES=;DEFINES=${CMAKE_C_DEFINE_FLAG}RULE_PATTERN=1;FLAGS=" PARENT_SCOPE) +endfunction() + +add_custom_rule(preprocess1 OUTPUT /.c + COMMAND "${CMAKE_COMMAND}" -E make_directory "" + COMMAND ${create_preprocessed_source} + CONFIGURATOR FOR_FILE_SET fs_configurator) + +add_library(foo1 STATIC) + +target_sources(foo1 PRIVATE FILE_SET fs TYPE preprocess1 FILES file1.c) + +# Use source file configurator +function(src_configurator rule target fileset outputFileset source patterns) + set(${patterns} "INPUT=$>;OUTPUT_DIR=/${target};CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$/.c>;INCLUDES=;DEFINES=${CMAKE_C_DEFINE_FLAG}RULE_PATTERN=1;FLAGS=" PARENT_SCOPE) +endfunction() + +add_custom_rule(preprocess2 OUTPUT /.c + COMMAND "${CMAKE_COMMAND}" -E make_directory "" + COMMAND ${create_preprocessed_source} + CONFIGURATOR FOR_SOURCE src_configurator) + +add_library(foo2 STATIC) + +target_sources(foo2 PRIVATE FILE_SET fs TYPE preprocess2 FILES file1.c) + + +# source file configurator override file set configurator +function(fs_configurator2 rule target fileset outputFileset patterns) + set(${patterns} "INPUT=$>;OUTPUT_DIR=/${target};CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$/.c>;INCLUDES=;DEFINES=${CMAKE_C_DEFINE_FLAG}WRONG=1;FLAGS=" PARENT_SCOPE) +endfunction() + +function(src_configurator2 rule target fileset outputFileset source patterns) + set(${patterns} "DEFINES=${CMAKE_C_DEFINE_FLAG}RULE_PATTERN=1" PARENT_SCOPE) +endfunction() + +add_custom_rule(preprocess3 OUTPUT /.c + COMMAND "${CMAKE_COMMAND}" -E make_directory "" + COMMAND ${create_preprocessed_source} + CONFIGURATOR FOR_FILE_SET fs_configurator2 FOR_SOURCE src_configurator2) + +add_library(foo3 STATIC) + +target_sources(foo3 PRIVATE FILE_SET fs TYPE preprocess3 FILES file1.c) diff --git a/Tests/RunCMake/CustomRule/CustomTarget-build-check.cmake b/Tests/RunCMake/CustomRule/CustomTarget-build-check.cmake new file mode 100644 index 0000000000..576b770597 --- /dev/null +++ b/Tests/RunCMake/CustomRule/CustomTarget-build-check.cmake @@ -0,0 +1,4 @@ + +if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/file.i") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/file.i is missing.") +endif() diff --git a/Tests/RunCMake/CustomRule/CustomTarget.cmake b/Tests/RunCMake/CustomRule/CustomTarget.cmake new file mode 100644 index 0000000000..b9d74de250 --- /dev/null +++ b/Tests/RunCMake/CustomRule/CustomTarget.cmake @@ -0,0 +1,9 @@ + +enable_language(C) + +add_custom_rule(simple OUTPUT / + COMMAND "${CMAKE_COMMAND}" -E copy ) + +add_custom_target(foo ALL) + +target_sources(foo PRIVATE FILE_SET fs TYPE simple FILES file.i) diff --git a/Tests/RunCMake/CustomRule/DerivedRule.cmake b/Tests/RunCMake/CustomRule/DerivedRule.cmake new file mode 100644 index 0000000000..153fa7b2e0 --- /dev/null +++ b/Tests/RunCMake/CustomRule/DerivedRule.cmake @@ -0,0 +1,104 @@ + +enable_language(C) + +if (NOT CMAKE_C_CREATE_PREPROCESSED_SOURCE) + return() +endif() + +if (NOT CMAKE_C_DEFINE_FLAG) + set(CMAKE_C_DEFINE_FLAG "-D") +endif() + +# clean-up the command line +string(REPLACE "${CMAKE_START_TEMP_FILE}" "" create_preprocessed_source "${CMAKE_C_CREATE_PREPROCESSED_SOURCE}") +string(REPLACE "${CMAKE_END_TEMP_FILE}" "" create_preprocessed_source "${create_preprocessed_source}") +string(REPLACE "" "" create_preprocessed_source "${create_preprocessed_source}") +separate_arguments(create_preprocessed_source UNIX_COMMAND "${create_preprocessed_source}") + +add_custom_rule(preprocess1 OUTPUT /.c + COMMAND "${CMAKE_COMMAND}" -E make_directory "" + COMMAND ${create_preprocessed_source}) + +set_property(RULE preprocess1 PROPERTY COMPILE_DEFINITIONS RULE_PATTERN=1) + +# derived rule take snapshot of properties +add_custom_rule(derived_preprocess1 FROM_RULE preprocess1) + +add_library(foo1 STATIC) + +target_sources(foo1 PRIVATE FILE_SET fs TYPE derived_preprocess1 FILES file1.c) + +set_property(FILE_SET fs TARGET foo1 PROPERTY RULE_PATTERNS + "INPUT=$>;OUTPUT_DIR=/;CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$/.c>;INCLUDES=;DEFINES=$,PREPEND,${CMAKE_C_DEFINE_FLAG}>;FLAGS=") + +# Update root rule with wrong compile definition +# so preprocess rule is no longer usable but derived_preprocess rule is OK +set_property(RULE preprocess1 PROPERTY COMPILE_DEFINITIONS WRONG=1) + + +# Use derived rule configurator to restore correct behavior +function(fs_configurator1 rule target fileset patterns) + set_property(FILE_SET ${fileset} TARGET ${target} PROPERTY COMPILE_DEFINITIONS RULE_PATTERN=1) +endfunction() + +add_custom_rule(derived_preprocess2 FROM_RULE preprocess1 + CONFIGURATOR FOR_FILE_SET fs_configurator1) + +add_library(foo2 STATIC) + +target_sources(foo2 PRIVATE FILE_SET fs TYPE derived_preprocess2 FILES file1.c) + +set_property(FILE_SET fs TARGET foo2 PROPERTY RULE_PATTERNS + "INPUT=$>;OUTPUT_DIR=/;CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$/.c>;INCLUDES=;DEFINES=$,PREPEND,${CMAKE_C_DEFINE_FLAG}>;FLAGS=") + + +# Check OVERRIDE behavior +function(fs_configurator2 rule target fileset patterns) + set_property(FILE_SET ${fileset} TARGET ${target} PROPERTY COMPILE_DEFINITIONS WRONG=1) +endfunction() + +add_custom_rule(preprocess2 OUTPUT /.c + COMMAND "${CMAKE_COMMAND}" -E make_directory "" + COMMAND ${create_preprocessed_source} + CONFIGURATOR FOR_FILE_SET fs_configurator2) + + +function(fs_configurator3 rule target fileset outputFileset patterns) + set_property(FILE_SET ${fileset} TARGET ${target} APPEND PROPERTY COMPILE_DEFINITIONS DEF=1) +endfunction() + +add_custom_rule(derived_preprocess3 FROM_RULE preprocess2 + CONFIGURATOR FOR_FILE_SET fs_configurator3 OVERRIDE) + +add_library(foo3 STATIC) + +target_sources(foo3 PRIVATE FILE_SET fs TYPE derived_preprocess3 FILES file2.c) + +set_property(FILE_SET fs TARGET foo3 PROPERTY RULE_PATTERNS + "INPUT=$>;OUTPUT_DIR=/;CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$/.c>;INCLUDES=;DEFINES=$,PREPEND,${CMAKE_C_DEFINE_FLAG}>;FLAGS=") + + +# Check CHAIN behavior +function(fs_configurator4 rule target fileset outputFileset patterns) + set_property(FILE_SET ${fileset} TARGET ${target} PROPERTY COMPILE_DEFINITIONS DEF1=1) +endfunction() + +add_custom_rule(preprocess3 OUTPUT /.c + COMMAND "${CMAKE_COMMAND}" -E make_directory "" + COMMAND ${create_preprocessed_source} + CONFIGURATOR FOR_FILE_SET fs_configurator4) + + +function(fs_configurator5 rule target fileset outputFileset patterns) + set_property(FILE_SET ${fileset} TARGET ${target} APPEND PROPERTY COMPILE_DEFINITIONS DEF2=1) +endfunction() + +add_custom_rule(derived_preprocess4 FROM_RULE preprocess3 + CONFIGURATOR FOR_FILE_SET fs_configurator5 CHAIN) + +add_library(foo4 STATIC) + +target_sources(foo4 PRIVATE FILE_SET fs TYPE derived_preprocess4 FILES file3.c) + +set_property(FILE_SET fs TARGET foo4 PROPERTY RULE_PATTERNS + "INPUT=$>;OUTPUT_DIR=/;CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$/.c>;INCLUDES=;DEFINES=$,PREPEND,${CMAKE_C_DEFINE_FLAG}>;FLAGS=") diff --git a/Tests/RunCMake/CustomRule/Global.cmake b/Tests/RunCMake/CustomRule/Global.cmake new file mode 100644 index 0000000000..543e2d45e0 --- /dev/null +++ b/Tests/RunCMake/CustomRule/Global.cmake @@ -0,0 +1,8 @@ + +enable_language(C) + +add_library(foo STATIC) + +add_subdirectory(subdir1) + +target_sources(foo PRIVATE FILE_SET fs TYPE simple FILES file.i) diff --git a/Tests/RunCMake/CustomRule/Local.cmake b/Tests/RunCMake/CustomRule/Local.cmake new file mode 100644 index 0000000000..f29357b75e --- /dev/null +++ b/Tests/RunCMake/CustomRule/Local.cmake @@ -0,0 +1,6 @@ + +enable_language(C) + +add_library(foo STATIC) + +add_subdirectory(subdir2) diff --git a/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadPattern-result.txt b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadPattern-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadPattern-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadPattern-stderr.txt b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadPattern-stderr.txt new file mode 100644 index 0000000000..fd4d1b016c --- /dev/null +++ b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadPattern-stderr.txt @@ -0,0 +1,4 @@ +CMake Error in CMakeLists\.txt: + Output File Set name, for rule "foo", did not expand correctly: + + "bad__foo_bar_fs" diff --git a/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadPattern.cmake b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadPattern.cmake new file mode 100644 index 0000000000..1a66a80c3a --- /dev/null +++ b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadPattern.cmake @@ -0,0 +1,8 @@ + +add_custom_rule(foo OUTPUT out1 COMMAND cmd arg1 arg2) + + +set_property(RULE foo PROPERTY OUTPUT_FILE_SET bad____ SOURCES) + +add_library(bar) +target_sources(bar PRIVATE FILE_SET fs TYPE foo FILES file1.c) diff --git a/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadType-result.txt b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadType-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadType-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadType-stderr.txt b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadType-stderr.txt new file mode 100644 index 0000000000..bffdda0153 --- /dev/null +++ b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadType-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at OUTPUT_FILE_SET-BadType\.cmake:[0-9]+ \(set_property\): + specified file set type is erroneous\. The supported types are: HEADERS, + SOURCES, CXX_MODULES\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadType.cmake b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadType.cmake new file mode 100644 index 0000000000..4eafe99cbc --- /dev/null +++ b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-BadType.cmake @@ -0,0 +1,8 @@ + +add_custom_rule(foo OUTPUT out1 COMMAND cmd arg1 arg2) + + +set_property(RULE foo PROPERTY OUTPUT_FILE_SET __ FOO) + +add_library(bar) +target_sources(bar PRIVATE FILE_SET fs TYPE foo FILES file1.c) diff --git a/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-TypeMismatch-result.txt b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-TypeMismatch-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-TypeMismatch-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-TypeMismatch-stderr.txt b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-TypeMismatch-stderr.txt new file mode 100644 index 0000000000..e5d67e6ca5 --- /dev/null +++ b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-TypeMismatch-stderr.txt @@ -0,0 +1,3 @@ +CMake Error in CMakeLists\.txt: + The output file set "foo_bar_fs", for the target "bar", has the type + "HEADERS" instead of "SOURCES", as specified by the rule "foo"\. diff --git a/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-TypeMismatch.cmake b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-TypeMismatch.cmake new file mode 100644 index 0000000000..2c7123a948 --- /dev/null +++ b/Tests/RunCMake/CustomRule/OUTPUT_FILE_SET-TypeMismatch.cmake @@ -0,0 +1,10 @@ + +add_custom_rule(foo OUTPUT out1 COMMAND cmd arg1 arg2) + + +set_property(RULE foo PROPERTY OUTPUT_FILE_SET __ SOURCES) + +add_library(bar) +target_sources(bar PRIVATE FILE_SET fs TYPE foo FILES file1.c) + +target_sources(bar PRIVATE FILE_SET foo_bar_fs TYPE HEADERS) diff --git a/Tests/RunCMake/CustomRule/Patterns.cmake b/Tests/RunCMake/CustomRule/Patterns.cmake new file mode 100644 index 0000000000..cbf27444fd --- /dev/null +++ b/Tests/RunCMake/CustomRule/Patterns.cmake @@ -0,0 +1,47 @@ + +enable_language(C) + +if (NOT CMAKE_C_CREATE_PREPROCESSED_SOURCE) + return() +endif() + +if (NOT CMAKE_C_DEFINE_FLAG) + set(CMAKE_C_DEFINE_FLAG "-D") +endif() + +# clean-up the command line +string(REPLACE "${CMAKE_START_TEMP_FILE}" "" create_preprocessed_source "${CMAKE_C_CREATE_PREPROCESSED_SOURCE}") +string(REPLACE "${CMAKE_END_TEMP_FILE}" "" create_preprocessed_source "${create_preprocessed_source}") +string(REPLACE "" "" create_preprocessed_source "${create_preprocessed_source}") +separate_arguments(create_preprocessed_source UNIX_COMMAND "${create_preprocessed_source}") + +add_custom_rule(preprocess OUTPUT /.c + COMMAND "${CMAKE_COMMAND}" -E make_directory "" + COMMAND ${create_preprocessed_source}) + + +# Use RULE_PATTERNS file set property +add_library(foo1 STATIC) + +target_sources(foo1 PRIVATE FILE_SET fs TYPE preprocess FILES file1.c) + +set_property(FILE_SET fs TARGET foo1 PROPERTY RULE_PATTERNS + "INPUT=$>;OUTPUT_DIR=/;CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$/.c>;INCLUDES=;DEFINES=${CMAKE_C_DEFINE_FLAG}RULE_PATTERN=1;FLAGS=") + + +# Use _PATTERNS source file property +add_library(foo2 STATIC) + +target_sources(foo2 PRIVATE FILE_SET fs TYPE preprocess FILES file1.c) + +set_property(SOURCE file1.c TARGET_DIRECTORY foo2 PROPERTY preprocess_PATTERNS + "INPUT=$>;OUTPUT_DIR=/;CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$/.c>;INCLUDES=;DEFINES=${CMAKE_C_DEFINE_FLAG}RULE_PATTERN=1;FLAGS=") + + +# _PATTERNS source file property override RULE_PATTERNS file set property +add_library(foo3 STATIC) + +target_sources(foo3 PRIVATE FILE_SET fs TYPE preprocess FILES file1.c) + +set_property(FILE_SET fs TARGET foo3 PROPERTY RULE_PATTERNS + "DEFINES=${CMAKE_C_DEFINE_FLAG}WRONG=1") diff --git a/Tests/RunCMake/CustomRule/RuleProperties1-result.txt b/Tests/RunCMake/CustomRule/RuleProperties1-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CustomRule/RuleProperties1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CustomRule/RuleProperties1-stderr.txt b/Tests/RunCMake/CustomRule/RuleProperties1-stderr.txt new file mode 100644 index 0000000000..8c5cefc991 --- /dev/null +++ b/Tests/RunCMake/CustomRule/RuleProperties1-stderr.txt @@ -0,0 +1,19 @@ +CMake Error at RuleProperties1\.cmake:[0-9]+ \(set_property\): + OUTPUT_FILE_SET property require a list of 2 elements: + + "name;type" +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) + + +CMake Error at RuleProperties1\.cmake:[0-9]+ \(set_property\): + specified file set type is erroneous. The supported types are: HEADERS, + SOURCES, CXX_MODULES\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) + + +CMake Error at RuleProperties1\.cmake:[0-9]+ \(set_property\): + GLOBAL property can't be set to FALSE on rules \("foo"\) +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/CustomRule/RuleProperties1.cmake b/Tests/RunCMake/CustomRule/RuleProperties1.cmake new file mode 100644 index 0000000000..0b5b63afd0 --- /dev/null +++ b/Tests/RunCMake/CustomRule/RuleProperties1.cmake @@ -0,0 +1,9 @@ + +add_custom_rule(foo OUTPUT out1 COMMAND cmd arg1 arg2) + + +set_property(RULE foo PROPERTY OUTPUT_FILE_SET wrong) +set_property(RULE foo PROPERTY OUTPUT_FILE_SET name wrong_type) + + +set_property(RULE foo PROPERTY GLOBAL false) diff --git a/Tests/RunCMake/CustomRule/RuleProperties2-result.txt b/Tests/RunCMake/CustomRule/RuleProperties2-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CustomRule/RuleProperties2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CustomRule/RuleProperties2-stderr.txt b/Tests/RunCMake/CustomRule/RuleProperties2-stderr.txt new file mode 100644 index 0000000000..c88cb0f540 --- /dev/null +++ b/Tests/RunCMake/CustomRule/RuleProperties2-stderr.txt @@ -0,0 +1,28 @@ +CMake Error at RuleProperties2\.cmake:[0-9]+ \(set_property\): + "COMMENT" property can't be set during generation for rules \("foo1"\) + +Call Stack \(most recent call first\): + CMakeLists\.txt + + +CMake Error in CMakeLists\.txt: + Erroneous execution of the CONFIGURATOR "configurator1", from the RULE + "foo1", for the FILE_SET "fs1" of TARGET "foo"\. + + +CMake Error at RuleProperties2\.cmake:[0-9]+ \(set_property\): + rule properties cannot be changed during generation \("foo2"\) + +Call Stack \(most recent call first\): + CMakeLists\.txt + + +CMake Error in CMakeLists\.txt: + Erroneous execution of the CONFIGURATOR "configurator2", from the RULE + "foo2", for the FILE_SET "fs2" of TARGET "foo"\. + + +CMake Error at RuleProperties2\.cmake:[0-9]+ \(add_library\): + No SOURCES given to target: foo +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/CustomRule/RuleProperties2.cmake b/Tests/RunCMake/CustomRule/RuleProperties2.cmake new file mode 100644 index 0000000000..1ec112304f --- /dev/null +++ b/Tests/RunCMake/CustomRule/RuleProperties2.cmake @@ -0,0 +1,16 @@ + +function(configurator1 rule target fileset patterns) + set_property(RULE ${rule} PROPERTY COMMENT "bar") +endfunction() + +add_custom_rule(foo1 OUTPUT out1 COMMAND cmd arg1 arg2 CONFIGURATOR FOR_FILE_SET configurator1) + +function(configurator2 rule target fileset patterns) + set_property(RULE ${rule} PROPERTY CUSTOM "bar") +endfunction() + +add_custom_rule(foo2 OUTPUT out1 COMMAND cmd arg1 arg2 CONFIGURATOR FOR_FILE_SET configurator2) + +add_library(foo) +target_sources(foo PRIVATE FILE_SET fs1 TYPE foo1 FILES file1.c) +target_sources(foo PRIVATE FILE_SET fs2 TYPE foo2 FILES file2.c) diff --git a/Tests/RunCMake/CustomRule/RunCMakeTest.cmake b/Tests/RunCMake/CustomRule/RunCMakeTest.cmake new file mode 100644 index 0000000000..d6359a6008 --- /dev/null +++ b/Tests/RunCMake/CustomRule/RunCMakeTest.cmake @@ -0,0 +1,26 @@ +include(RunCMake) + +function(run_configure_and_build name) + run_cmake(${name}) + set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${name}-build") + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(${name}-build ${CMAKE_COMMAND} --build . --config Debug) +endfunction() + +run_cmake(OUTPUT_FILE_SET-BadPattern) +run_cmake(OUTPUT_FILE_SET-BadType) +run_cmake(OUTPUT_FILE_SET-TypeMismatch) + +run_cmake(RuleProperties1) +run_cmake(RuleProperties2) + +if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|MSVC|SunPro|XL|HP") + run_configure_and_build(Simple) + run_configure_and_build(Global) + run_configure_and_build(Local) + run_configure_and_build(CustomTarget) + run_configure_and_build(Patterns) + run_configure_and_build(Configurators) + run_configure_and_build(DerivedRule) +endif() diff --git a/Tests/RunCMake/CustomRule/Simple.cmake b/Tests/RunCMake/CustomRule/Simple.cmake new file mode 100644 index 0000000000..d4e60f9459 --- /dev/null +++ b/Tests/RunCMake/CustomRule/Simple.cmake @@ -0,0 +1,10 @@ + +enable_language(C) + +add_custom_rule(simple OUTPUT /.c + COMMAND "${CMAKE_COMMAND}" -E copy + COMMAND "${CMAKE_COMMAND}" -E rename / /.c) + +add_library(foo STATIC) + +target_sources(foo PRIVATE FILE_SET fs TYPE simple FILES file.i) diff --git a/Tests/RunCMake/CustomRule/file.i b/Tests/RunCMake/CustomRule/file.i new file mode 100644 index 0000000000..7d75e37485 --- /dev/null +++ b/Tests/RunCMake/CustomRule/file.i @@ -0,0 +1,4 @@ + +void foo(void) +{ +} diff --git a/Tests/RunCMake/CustomRule/file1.c b/Tests/RunCMake/CustomRule/file1.c new file mode 100644 index 0000000000..a2d3fdd2a4 --- /dev/null +++ b/Tests/RunCMake/CustomRule/file1.c @@ -0,0 +1,8 @@ + +#ifndef RULE_PATTERN +# error "RULE_PATTERN not defined" +#endif + +void foo(void) +{ +} diff --git a/Tests/RunCMake/CustomRule/file2.c b/Tests/RunCMake/CustomRule/file2.c new file mode 100644 index 0000000000..aad173bb2d --- /dev/null +++ b/Tests/RunCMake/CustomRule/file2.c @@ -0,0 +1,12 @@ + +#ifdef WRONG +# error "WRONG defined" +#endif + +#ifndef DEF +# error "DEF not defined" +#endif + +void foo(void) +{ +} diff --git a/Tests/RunCMake/CustomRule/file3.c b/Tests/RunCMake/CustomRule/file3.c new file mode 100644 index 0000000000..0b4d74db18 --- /dev/null +++ b/Tests/RunCMake/CustomRule/file3.c @@ -0,0 +1,12 @@ + +#ifndef DEF1 +# error "DEF1 not defined" +#endif + +#ifndef DEF2 +# error "DEF2 not defined" +#endif + +void foo(void) +{ +} diff --git a/Tests/RunCMake/CustomRule/subdir1/CMakeLists.txt b/Tests/RunCMake/CustomRule/subdir1/CMakeLists.txt new file mode 100644 index 0000000000..445ac1af89 --- /dev/null +++ b/Tests/RunCMake/CustomRule/subdir1/CMakeLists.txt @@ -0,0 +1,5 @@ + +add_custom_rule(simple OUTPUT /.c + COMMAND "${CMAKE_COMMAND}" -E copy + COMMAND "${CMAKE_COMMAND}" -E rename / /.c + GLOBAL) diff --git a/Tests/RunCMake/CustomRule/subdir2/CMakeLists.txt b/Tests/RunCMake/CustomRule/subdir2/CMakeLists.txt new file mode 100644 index 0000000000..53cd3fcb49 --- /dev/null +++ b/Tests/RunCMake/CustomRule/subdir2/CMakeLists.txt @@ -0,0 +1,6 @@ + +add_custom_rule(simple OUTPUT /.c + COMMAND "${CMAKE_COMMAND}" -E copy + COMMAND "${CMAKE_COMMAND}" -E rename / /.c) + +target_sources(foo PRIVATE FILE_SET fs TYPE simple BASE_DIRS ${CMAKE_SOURCE_DIR} FILES ../file.i) diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/CMakeLists.txt b/Tests/RunCMake/GenEx-RULE_PROPERTY/CMakeLists.txt new file mode 100644 index 0000000000..7a948f9be7 --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 4.0...4.4) + +project(${RunCMake_TEST} NONE) + +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/RULE_PROPERTY-build-check.cmake b/Tests/RunCMake/GenEx-RULE_PROPERTY/RULE_PROPERTY-build-check.cmake new file mode 100644 index 0000000000..0db89f3fc4 --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/RULE_PROPERTY-build-check.cmake @@ -0,0 +1,4 @@ + +include ("${RunCMake_TEST_BINARY_DIR}/RULE_PROPERTY-properties.cmake") + +include ("${RunCMake_TEST_BINARY_DIR}/RULE_PROPERTY-validation.cmake") diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/RULE_PROPERTY.cmake b/Tests/RunCMake/GenEx-RULE_PROPERTY/RULE_PROPERTY.cmake new file mode 100644 index 0000000000..c70195812c --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/RULE_PROPERTY.cmake @@ -0,0 +1,56 @@ + +add_custom_rule(write_properties OUTPUT "/RULE_PROPERTY-properties.cmake" + COMMAND "${CMAKE_COMMAND}" "-DOUTPUT_FILE=/RULE_PROPERTY-properties.cmake" -P "${CMAKE_CURRENT_SOURCE_DIR}/write_properties.cmake" -- "NAME=$,NAME>" + "PARENT_RULE=$,PARENT_RULE>" + "GLOBAL=$,GLOBAL>" + "VERBATIM=$,VERBATIM>" + "OUTPUT_FILE_SET=$,OUTPUT_FILE_SET>,:>" + "FOO=$,FOO>" + "VOID=$,VOID>") + +set_property(RULE write_properties PROPERTY FOO BAR) + + +add_custom_target(write_properties ALL) + +target_sources(write_properties PRIVATE FILE_SET fs TYPE write_properties FILES foo.txt) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/RULE_PROPERTY-validation.cmake" + [[ +macro (CHECK_VALUE test_msg value expected) + if (NOT "${value}" STREQUAL "${expected}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n") + endif() +endmacro() + +]]) + + +# predefined properties +set(reference "write_properties") +string (APPEND GENERATE_CONTENT + "check_value (\",NAME>\" \"\${NAME}\" \"${reference}\")\n") +set(reference "") +string (APPEND GENERATE_CONTENT + "check_value (\",PARENT_RULE>\" \"\${PARENT_RULE}\" \"${reference}\")\n") +set(reference "0") +string (APPEND GENERATE_CONTENT + "check_value (\",GLOBAL>\" \"\${GLOBAL}\" \"${reference}\")\n") +set(reference "1") +string (APPEND GENERATE_CONTENT + "check_value (\",VERBATIM>\" \"\${VERBATIM}\" \"${reference}\")\n") +set(reference "__cmake_rule_write_properties_write_properties_fs_outputs:SOURCES") +string (APPEND GENERATE_CONTENT + "check_value (\",OUTPUT_FILE_SET>\" \"\${OUTPUT_FILE_SET}\" \"${reference}\")\n") + + +get_property(reference RULE write_properties PROPERTY FOO) +string (APPEND GENERATE_CONTENT + "check_value (\",FOO>\" \"\${FOO}\" \"${reference}\")\n") + +get_property(reference RULE write_properties PROPERTY VOID) +string (APPEND GENERATE_CONTENT + "check_value (\",VOID>\" \"\${VOID}\" \"${reference}\")\n") + + +file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/RULE_PROPERTY-validation.cmake" "${GENERATE_CONTENT}") diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-RULE_PROPERTY/RunCMakeTest.cmake new file mode 100644 index 0000000000..070a5c1f02 --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/RunCMakeTest.cmake @@ -0,0 +1,15 @@ + +include(RunCMake) + +function(run_configure_and_build name) + run_cmake(${name}) + set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${name}-build") + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${name}-build ${CMAKE_COMMAND} --build . --config Debug) +endfunction() + +run_cmake(no-arguments) +run_cmake(no-rule) +run_cmake(no-property) + +run_configure_and_build(RULE_PROPERTY) diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/foo.txt b/Tests/RunCMake/GenEx-RULE_PROPERTY/foo.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/no-arguments-result.txt b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-arguments-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-arguments-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/no-arguments-stderr.txt b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-arguments-stderr.txt new file mode 100644 index 0000000000..646c1eaf39 --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-arguments-stderr.txt @@ -0,0 +1,9 @@ +CMake Error at no-arguments\.cmake:[0-9]+ \(file\): + Error evaluating generator expression: + + \$ + + \$ expression requires 2 comma separated parameters, but got + 1 instead\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/no-arguments.cmake b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-arguments.cmake new file mode 100644 index 0000000000..815bf9abee --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-arguments.cmake @@ -0,0 +1,2 @@ + +file(GENERATE OUTPUT result.txt CONTENT "$") diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/no-property-result.txt b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-property-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-property-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/no-property-stderr.txt b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-property-stderr.txt new file mode 100644 index 0000000000..45f922f8fc --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-property-stderr.txt @@ -0,0 +1,8 @@ +CMake Error at no-property\.cmake:[0-9]+ \(file\): + Error evaluating generator expression: + + \$ + + \$ expression requires a non-empty property name\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/no-property.cmake b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-property.cmake new file mode 100644 index 0000000000..e8845c410f --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-property.cmake @@ -0,0 +1,2 @@ + +file(GENERATE OUTPUT result.txt CONTENT "$") diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/no-rule-result.txt b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-rule-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-rule-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/no-rule-stderr.txt b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-rule-stderr.txt new file mode 100644 index 0000000000..42a040f650 --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-rule-stderr.txt @@ -0,0 +1,8 @@ +CMake Error at no-rule.cmake:[0-9]+ \(file\): + Error evaluating generator expression: + + \$ + + \$ expression requires a non-empty rule name\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/no-rule.cmake b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-rule.cmake new file mode 100644 index 0000000000..94862a979a --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/no-rule.cmake @@ -0,0 +1,2 @@ + +file(GENERATE OUTPUT result.txt CONTENT "$") diff --git a/Tests/RunCMake/GenEx-RULE_PROPERTY/write_properties.cmake b/Tests/RunCMake/GenEx-RULE_PROPERTY/write_properties.cmake new file mode 100644 index 0000000000..cbc59fe681 --- /dev/null +++ b/Tests/RunCMake/GenEx-RULE_PROPERTY/write_properties.cmake @@ -0,0 +1,10 @@ + +math(EXPR end "${CMAKE_ARGC} - 1") + +file(WRITE "${OUTPUT_FILE}" "# Rule Properties\n") + +foreach (index RANGE 5 ${end}) + if (CMAKE_ARGV${index} MATCHES "^([A-Z_]+)=(.*)$") + file(APPEND "${OUTPUT_FILE}" "set(${CMAKE_MATCH_1} \"${CMAKE_MATCH_2}\")\n") + endif() +endforeach() diff --git a/Tests/RunCMake/add_custom_rule/CMakeLists.txt b/Tests/RunCMake/add_custom_rule/CMakeLists.txt new file mode 100644 index 0000000000..0ceb19a24c --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.4) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-CONFIGURATOR-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-CONFIGURATOR-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-CONFIGURATOR-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-CONFIGURATOR-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-CONFIGURATOR-stderr.txt new file mode 100644 index 0000000000..2643e7b4ba --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-CONFIGURATOR-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at FROM_RULE-InvalidDuplicateOption-CONFIGURATOR\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule only one occurrence of "FROM_RULE" or "CONFIGURATOR" + options is allowed. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-CONFIGURATOR.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-CONFIGURATOR.cmake new file mode 100644 index 0000000000..d39ffbea61 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-CONFIGURATOR.cmake @@ -0,0 +1,3 @@ + + +add_custom_rule(rule FROM_RULE foo CONFIGURATOR xxx CONFIGURATOR yyy) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_FILE_SET-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_FILE_SET-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_FILE_SET-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_FILE_SET-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_FILE_SET-stderr.txt new file mode 100644 index 0000000000..4643522281 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_FILE_SET-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at FROM_RULE-InvalidDuplicateOption-FOR_FILE_SET\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule only one occurrence of "FOR_FILE_SET" or "FOR_SOURCE" + sub-options of "CONFIGURATOR" option is allowed. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_FILE_SET.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_FILE_SET.cmake new file mode 100644 index 0000000000..7c47afbd6b --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_FILE_SET.cmake @@ -0,0 +1,5 @@ + +function(for_fs) +endfunction() + +add_custom_rule(rule FROM_RULE base CONFIGURATOR FOR_FILE_SET for_fs FOR_FILE_SET for_fs) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_SOURCE-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_SOURCE-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_SOURCE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_SOURCE-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_SOURCE-stderr.txt new file mode 100644 index 0000000000..5f2f92eb23 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_SOURCE-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at FROM_RULE-InvalidDuplicateOption-FOR_SOURCE\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule only one occurrence of "FOR_FILE_SET" or "FOR_SOURCE" + sub-options of "CONFIGURATOR" option is allowed. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_SOURCE.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_SOURCE.cmake new file mode 100644 index 0000000000..8571199fa2 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidDuplicateOption-FOR_SOURCE.cmake @@ -0,0 +1,5 @@ + +function(for_source) +endfunction() + +add_custom_rule(rule FROM_RULE base CONFIGURATOR FOR_SOURCE for_source FOR_SOURCE for_source) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-BYPRODUCTS-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-BYPRODUCTS-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-BYPRODUCTS-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-BYPRODUCTS-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-BYPRODUCTS-stderr.txt new file mode 100644 index 0000000000..435650ee58 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-BYPRODUCTS-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at FROM_RULE-InvalidOption-BYPRODUCTS\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule given unknown argument: "BYPRODUCTS". +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-BYPRODUCTS.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-BYPRODUCTS.cmake new file mode 100644 index 0000000000..7df0c16188 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-BYPRODUCTS.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(foo FROM_RULE base BYPRODUCTS products) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-COMMAND-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-COMMAND-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-COMMAND-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-COMMAND-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-COMMAND-stderr.txt new file mode 100644 index 0000000000..76f4b88bfa --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-COMMAND-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at FROM_RULE-InvalidOption-COMMAND\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule given unknown argument: "COMMAND". +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-COMMAND.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-COMMAND.cmake new file mode 100644 index 0000000000..61e4f75ab3 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-COMMAND.cmake @@ -0,0 +1 @@ +add_custom_rule(foo FROM_RULE base COMMAND cmd) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPENDS-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPENDS-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPENDS-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPENDS-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPENDS-stderr.txt new file mode 100644 index 0000000000..eca9afc435 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPENDS-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at FROM_RULE-InvalidOption-DEPENDS\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule given unknown argument: "DEPENDS". +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPENDS.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPENDS.cmake new file mode 100644 index 0000000000..d21c6a0804 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPENDS.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(foo FROM_RULE base DEPENDS bar) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPFILE-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPFILE-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPFILE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPFILE-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPFILE-stderr.txt new file mode 100644 index 0000000000..349d6d7f08 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPFILE-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at FROM_RULE-InvalidOption-DEPFILE\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule given unknown argument: "DEPFILE". +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPFILE.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPFILE.cmake new file mode 100644 index 0000000000..636caf676f --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-DEPFILE.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(foo FROM_RULE base DEPFILE depfile) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_FILE_SET-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_FILE_SET-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_FILE_SET-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_FILE_SET-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_FILE_SET-stderr.txt new file mode 100644 index 0000000000..1f96061767 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_FILE_SET-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at FROM_RULE-InvalidOption-FOR_FILE_SET\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule cannot create RULE "rule" because the "CHAIN" and + "OVERRIDE" options of CONFIGURATOR "for_fs" are mutually exclusive\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_FILE_SET.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_FILE_SET.cmake new file mode 100644 index 0000000000..935c10231d --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_FILE_SET.cmake @@ -0,0 +1,5 @@ + +function(for_fs) +endfunction() + +add_custom_rule(rule FROM_RULE base CONFIGURATOR FOR_FILE_SET for_fs CHAIN OVERRIDE) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_SOURCE-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_SOURCE-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_SOURCE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_SOURCE-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_SOURCE-stderr.txt new file mode 100644 index 0000000000..b1983dcdcc --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_SOURCE-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at FROM_RULE-InvalidOption-FOR_SOURCE\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule cannot create RULE "rule" because the "CHAIN" and + "OVERRIDE" options of CONFIGURATOR "for_sf" are mutually exclusive\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_SOURCE.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_SOURCE.cmake new file mode 100644 index 0000000000..1c60100178 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-FOR_SOURCE.cmake @@ -0,0 +1,5 @@ + +function(for_sf) +endfunction() + +add_custom_rule(rule FROM_RULE base CONFIGURATOR FOR_FILE_SET for_sf CHAIN OVERRIDE) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-OUTPUT-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-OUTPUT-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-OUTPUT-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-OUTPUT-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-OUTPUT-stderr.txt new file mode 100644 index 0000000000..59544df5fe --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-OUTPUT-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at FROM_RULE-InvalidOption-OUTPUT\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule given unknown argument: "OUTPUT". +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-OUTPUT.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-OUTPUT.cmake new file mode 100644 index 0000000000..83e5e95df8 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidOption-OUTPUT.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(foo FROM_RULE base OUTPUT out) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue-FROM_RULE-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue-FROM_RULE-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue-FROM_RULE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue-FROM_RULE-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue-FROM_RULE-stderr.txt new file mode 100644 index 0000000000..2a361935ea --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue-FROM_RULE-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at FROM_RULE-InvalidValue-FROM_RULE\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule cannot create RULE "rule" because the RULE "base" does not + exist or is not accessible\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue-FROM_RULE.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue-FROM_RULE.cmake new file mode 100644 index 0000000000..12cb766217 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue-FROM_RULE.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule FROM_RULE base) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_FILE_SET-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_FILE_SET-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_FILE_SET-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_FILE_SET-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_FILE_SET-stderr.txt new file mode 100644 index 0000000000..62228bcc00 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_FILE_SET-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at FROM_RULE-InvalidValue1-FOR_FILE_SET\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule command specified for "FOR_FILE_SET" does not exist: fs\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_FILE_SET.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_FILE_SET.cmake new file mode 100644 index 0000000000..ba225d27ab --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_FILE_SET.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule FROM_RULE base CONFIGURATOR FOR_FILE_SET fs) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_SOURCE-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_SOURCE-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_SOURCE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_SOURCE-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_SOURCE-stderr.txt new file mode 100644 index 0000000000..d60f4f1653 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_SOURCE-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at FROM_RULE-InvalidValue1-FOR_SOURCE\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule command specified for "FOR_SOURCE" does not exist: src\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_SOURCE.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_SOURCE.cmake new file mode 100644 index 0000000000..cd8ec32f3d --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue1-FOR_SOURCE.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule FROM_RULE base CONFIGURATOR FOR_SOURCE src) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_FILE_SET-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_FILE_SET-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_FILE_SET-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_FILE_SET-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_FILE_SET-stderr.txt new file mode 100644 index 0000000000..5ae4dcc993 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_FILE_SET-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at FROM_RULE-InvalidValue2-FOR_FILE_SET\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule command specified for "FOR_FILE_SET" is not a function: fs\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_FILE_SET.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_FILE_SET.cmake new file mode 100644 index 0000000000..ab8aafcaee --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_FILE_SET.cmake @@ -0,0 +1,5 @@ + +macro(fs) +endmacro() + +add_custom_rule(rule FROM_RULE base CONFIGURATOR FOR_FILE_SET fs) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_SOURCE-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_SOURCE-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_SOURCE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_SOURCE-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_SOURCE-stderr.txt new file mode 100644 index 0000000000..86cfd58b5a --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_SOURCE-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at FROM_RULE-InvalidValue2-FOR_SOURCE\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule command specified for "FOR_SOURCE" is not a function: src\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_SOURCE.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_SOURCE.cmake new file mode 100644 index 0000000000..63dd8864ba --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-InvalidValue2-FOR_SOURCE.cmake @@ -0,0 +1,5 @@ + +macro(src) +endmacro() + +add_custom_rule(rule FROM_RULE base CONFIGURATOR FOR_SOURCE src) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_FILE_SET-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_FILE_SET-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_FILE_SET-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_FILE_SET-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_FILE_SET-stderr.txt new file mode 100644 index 0000000000..edc36748ce --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_FILE_SET-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at FROM_RULE-MissingRequiredValue-FOR_FILE_SET\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule given invalid argument: + + FOR_FILE_SET: missing required value +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_FILE_SET.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_FILE_SET.cmake new file mode 100644 index 0000000000..4c5b7e6a5e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_FILE_SET.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule FROM_RULE base CONFIGURATOR FOR_FILE_SET) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_SOURCE-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_SOURCE-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_SOURCE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_SOURCE-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_SOURCE-stderr.txt new file mode 100644 index 0000000000..0cc88b1241 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_SOURCE-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at FROM_RULE-MissingRequiredValue-FOR_SOURCE\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule given invalid argument: + + FOR_SOURCE: missing required value +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_SOURCE.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_SOURCE.cmake new file mode 100644 index 0000000000..426f0c064c --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FOR_SOURCE.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule FROM_RULE base CONFIGURATOR FOR_SOURCE) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FROM_RULE-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FROM_RULE-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FROM_RULE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FROM_RULE-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FROM_RULE-stderr.txt new file mode 100644 index 0000000000..fe08a82b88 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FROM_RULE-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at FROM_RULE-MissingRequiredValue-FROM_RULE\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule given invalid argument: + + FROM_RULE: missing required value +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FROM_RULE.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FROM_RULE.cmake new file mode 100644 index 0000000000..bf0b77e9d2 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-MissingRequiredValue-FROM_RULE.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule FROM_RULE) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-NoRuleName-result.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-NoRuleName-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-NoRuleName-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-NoRuleName-stderr.txt b/Tests/RunCMake/add_custom_rule/FROM_RULE-NoRuleName-stderr.txt new file mode 100644 index 0000000000..707fa50425 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-NoRuleName-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at FROM_RULE-NoRuleName\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule rule name is missing. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/FROM_RULE-NoRuleName.cmake b/Tests/RunCMake/add_custom_rule/FROM_RULE-NoRuleName.cmake new file mode 100644 index 0000000000..58c4d3ff78 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/FROM_RULE-NoRuleName.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(FROM_RULE foo) diff --git a/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-CONFIGURATOR-result.txt b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-CONFIGURATOR-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-CONFIGURATOR-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-CONFIGURATOR-stderr.txt b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-CONFIGURATOR-stderr.txt new file mode 100644 index 0000000000..38941ee48d --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-CONFIGURATOR-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at InvalidDuplicateOption-CONFIGURATOR\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule only one occurrence of "DEPFILE" or "CONFIGURATOR" options + is allowed\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-CONFIGURATOR.cmake b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-CONFIGURATOR.cmake new file mode 100644 index 0000000000..998e52f54a --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-CONFIGURATOR.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule COMMAND foo OUTPUT out CONFIGURATOR xxx CONFIGURATOR yyy) diff --git a/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-DEPFILE-result.txt b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-DEPFILE-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-DEPFILE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-DEPFILE-stderr.txt b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-DEPFILE-stderr.txt new file mode 100644 index 0000000000..f43d49e40c --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-DEPFILE-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at InvalidDuplicateOption-DEPFILE\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule only one occurrence of "DEPFILE" or "CONFIGURATOR" options + is allowed\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-DEPFILE.cmake b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-DEPFILE.cmake new file mode 100644 index 0000000000..d75e6ba029 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-DEPFILE.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule COMMAND foo OUTPUT out DEPFILE d1 DEPFILE d2) diff --git a/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_FILE_SET-result.txt b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_FILE_SET-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_FILE_SET-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_FILE_SET-stderr.txt b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_FILE_SET-stderr.txt new file mode 100644 index 0000000000..f5598004e9 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_FILE_SET-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at InvalidDuplicateOption-FOR_FILE_SET\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule only one occurrence of "FOR_FILE_SET" or "FOR_SOURCE" + sub-options of "CONFIGURATOR" option is allowed\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_FILE_SET.cmake b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_FILE_SET.cmake new file mode 100644 index 0000000000..275fda9b28 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_FILE_SET.cmake @@ -0,0 +1,5 @@ + +function(for_fs) +endfunction() + +add_custom_rule(rule COMMAND foo OUTPUT out CONFIGURATOR FOR_FILE_SET for_fs FOR_FILE_SET for_fs) diff --git a/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_SOURCE-result.txt b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_SOURCE-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_SOURCE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_SOURCE-stderr.txt b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_SOURCE-stderr.txt new file mode 100644 index 0000000000..6c292d4979 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_SOURCE-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at InvalidDuplicateOption-FOR_SOURCE\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule only one occurrence of "FOR_FILE_SET" or "FOR_SOURCE" + sub-options of "CONFIGURATOR" option is allowed\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_SOURCE.cmake b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_SOURCE.cmake new file mode 100644 index 0000000000..7cea6a04e3 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidDuplicateOption-FOR_SOURCE.cmake @@ -0,0 +1,5 @@ + +function(for_source) +endfunction() + +add_custom_rule(rule COMMAND foo OUTPUT out CONFIGURATOR FOR_SOURCE for_source FOR_SOURCE for_source) diff --git a/Tests/RunCMake/add_custom_rule/InvalidOptions5-result.txt b/Tests/RunCMake/add_custom_rule/InvalidOptions5-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidOptions5-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/InvalidRuleName-result.txt b/Tests/RunCMake/add_custom_rule/InvalidRuleName-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidRuleName-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/InvalidRuleName-stderr.txt b/Tests/RunCMake/add_custom_rule/InvalidRuleName-stderr.txt new file mode 100644 index 0000000000..289a046ed3 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidRuleName-stderr.txt @@ -0,0 +1,10 @@ +CMake Error at InvalidRuleName.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule names in all uppercase are reserved for CMake\. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Error at InvalidRuleName.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule invalid name for RULE: @\?\. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/InvalidRuleName.cmake b/Tests/RunCMake/add_custom_rule/InvalidRuleName.cmake new file mode 100644 index 0000000000..640a5b0f70 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidRuleName.cmake @@ -0,0 +1,4 @@ + +add_custom_rule(ALL_CAPITAL COMMAND tool OUTPUT output) + +add_custom_rule(@? COMMAND tool OUTPUT output) diff --git a/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_FILE_SET-result.txt b/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_FILE_SET-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_FILE_SET-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_FILE_SET-stderr.txt b/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_FILE_SET-stderr.txt new file mode 100644 index 0000000000..b1ea3642dd --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_FILE_SET-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at InvalidValue1-FOR_FILE_SET\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule command specified for "FOR_FILE_SET" does not exist: fs\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_FILE_SET.cmake b/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_FILE_SET.cmake new file mode 100644 index 0000000000..e438988db4 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_FILE_SET.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule OUTPUT out COMMAND cmd CONFIGURATOR FOR_FILE_SET fs) diff --git a/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_SOURCE-result.txt b/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_SOURCE-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_SOURCE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_SOURCE-stderr.txt b/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_SOURCE-stderr.txt new file mode 100644 index 0000000000..0fed2733a7 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_SOURCE-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at InvalidValue1-FOR_SOURCE\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule command specified for "FOR_SOURCE" does not exist: src\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_SOURCE.cmake b/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_SOURCE.cmake new file mode 100644 index 0000000000..f5bf530c43 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidValue1-FOR_SOURCE.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule OUTPUT out COMMAND cmd CONFIGURATOR FOR_SOURCE src) diff --git a/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_FILE_SET-result.txt b/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_FILE_SET-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_FILE_SET-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_FILE_SET-stderr.txt b/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_FILE_SET-stderr.txt new file mode 100644 index 0000000000..e38c50080c --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_FILE_SET-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at InvalidValue2-FOR_FILE_SET\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule command specified for "FOR_FILE_SET" is not a function: fs\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_FILE_SET.cmake b/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_FILE_SET.cmake new file mode 100644 index 0000000000..0489bafc71 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_FILE_SET.cmake @@ -0,0 +1,5 @@ + +macro(fs) +endmacro() + +add_custom_rule(rule OUTPUT out COMMAND cmd CONFIGURATOR FOR_FILE_SET fs) diff --git a/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_SOURCE-result.txt b/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_SOURCE-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_SOURCE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_SOURCE-stderr.txt b/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_SOURCE-stderr.txt new file mode 100644 index 0000000000..1e426c709a --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_SOURCE-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at InvalidValue2-FOR_SOURCE\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule command specified for "FOR_SOURCE" is not a function: src\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_SOURCE.cmake b/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_SOURCE.cmake new file mode 100644 index 0000000000..e46b2a8d3e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/InvalidValue2-FOR_SOURCE.cmake @@ -0,0 +1,5 @@ + +macro(src) +endmacro() + +add_custom_rule(rule OUTPUT out COMMAND cmd CONFIGURATOR FOR_SOURCE src) diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredArguments.cmake b/Tests/RunCMake/add_custom_rule/MissingRequiredArguments.cmake new file mode 100644 index 0000000000..6e0ab7ee6e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredArguments.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule COMMAND OUTPUT) diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredOption-COMMAND-result.txt b/Tests/RunCMake/add_custom_rule/MissingRequiredOption-COMMAND-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredOption-COMMAND-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredOption-COMMAND-stderr.txt b/Tests/RunCMake/add_custom_rule/MissingRequiredOption-COMMAND-stderr.txt new file mode 100644 index 0000000000..bcf5296f30 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredOption-COMMAND-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at MissingRequiredOption-COMMAND\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule cannot create RULE "rule" because the mandatory options + "COMMAND" or "OUTPUT" are missing\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredOption-COMMAND.cmake b/Tests/RunCMake/add_custom_rule/MissingRequiredOption-COMMAND.cmake new file mode 100644 index 0000000000..989689ba4d --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredOption-COMMAND.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule OUTPUT output) diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredOption-OUTPUT-result.txt b/Tests/RunCMake/add_custom_rule/MissingRequiredOption-OUTPUT-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredOption-OUTPUT-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredOption-OUTPUT-stderr.txt b/Tests/RunCMake/add_custom_rule/MissingRequiredOption-OUTPUT-stderr.txt new file mode 100644 index 0000000000..c241e3e803 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredOption-OUTPUT-stderr.txt @@ -0,0 +1,5 @@ +CMake Error at MissingRequiredOption-OUTPUT\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule cannot create RULE "rule" because the mandatory options + "COMMAND" or "OUTPUT" are missing\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredOption-OUTPUT.cmake b/Tests/RunCMake/add_custom_rule/MissingRequiredOption-OUTPUT.cmake new file mode 100644 index 0000000000..7cd7c98855 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredOption-OUTPUT.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule COMMAND cmd) diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredValues1-result.txt b/Tests/RunCMake/add_custom_rule/MissingRequiredValues1-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredValues1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredValues1-stderr.txt b/Tests/RunCMake/add_custom_rule/MissingRequiredValues1-stderr.txt new file mode 100644 index 0000000000..be51d7f8a5 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredValues1-stderr.txt @@ -0,0 +1,8 @@ +CMake Error at MissingRequiredValues1\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule given invalid arguments: + + COMMAND: missing required value + DEPFILE: missing required value + OUTPUT: missing required value +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredValues1.cmake b/Tests/RunCMake/add_custom_rule/MissingRequiredValues1.cmake new file mode 100644 index 0000000000..12e482d08b --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredValues1.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule COMMAND OUTPUT DEPFILE) diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredValues2-result.txt b/Tests/RunCMake/add_custom_rule/MissingRequiredValues2-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredValues2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredValues2-stderr.txt b/Tests/RunCMake/add_custom_rule/MissingRequiredValues2-stderr.txt new file mode 100644 index 0000000000..846619f2c8 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredValues2-stderr.txt @@ -0,0 +1,7 @@ +CMake Error at MissingRequiredValues2\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule given invalid arguments: + + FOR_FILE_SET: missing required value + FOR_SOURCE: missing required value +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/MissingRequiredValues2.cmake b/Tests/RunCMake/add_custom_rule/MissingRequiredValues2.cmake new file mode 100644 index 0000000000..b691e8ae0a --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/MissingRequiredValues2.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(rule COMMAND cmd OUTPUT output CONFIGURATOR FOR_FILE_SET FOR_SOURCE) diff --git a/Tests/RunCMake/add_custom_rule/NoArguments-result.txt b/Tests/RunCMake/add_custom_rule/NoArguments-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/NoArguments-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/NoArguments-stderr.txt b/Tests/RunCMake/add_custom_rule/NoArguments-stderr.txt new file mode 100644 index 0000000000..f44987dae9 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/NoArguments-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at NoArguments\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule called with incorrect number of arguments +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/NoArguments.cmake b/Tests/RunCMake/add_custom_rule/NoArguments.cmake new file mode 100644 index 0000000000..4a5a74d666 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/NoArguments.cmake @@ -0,0 +1,2 @@ + +add_custom_rule() diff --git a/Tests/RunCMake/add_custom_rule/NoRuleName-result.txt b/Tests/RunCMake/add_custom_rule/NoRuleName-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/NoRuleName-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_rule/NoRuleName-stderr.txt b/Tests/RunCMake/add_custom_rule/NoRuleName-stderr.txt new file mode 100644 index 0000000000..6e79c05fc9 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/NoRuleName-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at NoRuleName\.cmake:[0-9]+ \(add_custom_rule\): + add_custom_rule rule name is missing\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/add_custom_rule/NoRuleName.cmake b/Tests/RunCMake/add_custom_rule/NoRuleName.cmake new file mode 100644 index 0000000000..62caff40b7 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/NoRuleName.cmake @@ -0,0 +1,2 @@ + +add_custom_rule(COMMAND tool OUTPUT output) diff --git a/Tests/RunCMake/add_custom_rule/Rule-BYPRODUCTS.cmake b/Tests/RunCMake/add_custom_rule/Rule-BYPRODUCTS.cmake new file mode 100644 index 0000000000..e81785b0a4 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/Rule-BYPRODUCTS.cmake @@ -0,0 +1,19 @@ +include(check_property.cmake) + +unset(TEST_FAILED) + +add_custom_rule(rule1 OUTPUT out1 COMMAND cmd arg1 arg2) +check_rule_property(rule1 BYPRODUCTS "") + +add_custom_rule(rule2 OUTPUT out1 COMMAND cmd arg1 arg2 BYPRODUCTS bp1 bp2) +check_rule_property(rule2 BYPRODUCTS "bp1;bp2") + +add_custom_rule(rule3 BYPRODUCTS bp1 bp2 OUTPUT out1 out2 COMMAND cmd arg1 arg2) +check_rule_property(rule3 BYPRODUCTS "bp1;bp2") + +add_custom_rule(rule4 OUTPUT out1 out2 BYPRODUCTS bp1 bp2 COMMAND cmd arg1 arg2 BYPRODUCTS bp3 bp4) +check_rule_property(rule4 BYPRODUCTS "bp1;bp2;bp3;bp4") + +if(TEST_FAILED) + message(FATAL_ERROR "${TEST_FAILED}") +endif() diff --git a/Tests/RunCMake/add_custom_rule/Rule-COMMAND.cmake b/Tests/RunCMake/add_custom_rule/Rule-COMMAND.cmake new file mode 100644 index 0000000000..0b42e4bfaf --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/Rule-COMMAND.cmake @@ -0,0 +1,28 @@ + +include(check_property.cmake) + +unset(TEST_FAILED) + +add_custom_rule(rule1 OUTPUT out1 COMMAND cmd arg1 arg2) +check_rule_property(rule1 COMMAND "cmd;arg1;arg2") +check_rule_property(rule1 COMMAND_COUNT "1") +check_rule_property(rule1 COMMAND_0 "cmd;arg1;arg2") +check_rule_property(rule1 COMMAND_1 "NOTFOUND") + +add_custom_rule(rule2 OUTPUT out1 COMMAND cmd1 arg1 arg2 COMMAND cmd2 arg1 arg2) +check_rule_property(rule2 COMMAND "cmd1;arg1;arg2") +check_rule_property(rule2 COMMAND_COUNT "2") +check_rule_property(rule2 COMMAND_0 "cmd1;arg1;arg2") +check_rule_property(rule2 COMMAND_1 "cmd2;arg1;arg2") +check_rule_property(rule2 COMMAND_2 "NOTFOUND") + +add_custom_rule(rule3 OUTPUT out1 COMMAND cmd1 arg1 arg2 OUTPUT out2 COMMAND cmd2 arg1 arg2) +check_rule_property(rule3 COMMAND "cmd1;arg1;arg2") +check_rule_property(rule3 COMMAND_COUNT "2") +check_rule_property(rule3 COMMAND_0 "cmd1;arg1;arg2") +check_rule_property(rule3 COMMAND_1 "cmd2;arg1;arg2") +check_rule_property(rule3 COMMAND_2 "NOTFOUND") + +if(TEST_FAILED) + message(FATAL_ERROR "${TEST_FAILED}") +endif() diff --git a/Tests/RunCMake/add_custom_rule/Rule-CONFIGURATOR.cmake b/Tests/RunCMake/add_custom_rule/Rule-CONFIGURATOR.cmake new file mode 100644 index 0000000000..28d3345db5 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/Rule-CONFIGURATOR.cmake @@ -0,0 +1,63 @@ +include(check_property.cmake) + +unset(TEST_FAILED) + +function(fileset1_configurator) +endfunction() +function(fileset2_configurator) +endfunction() +function(fileset3_configurator) +endfunction() + +function(source1_configurator) +endfunction() +function(source2_configurator) +endfunction() +function(source3_configurator) +endfunction() + +add_custom_rule(rule1 OUTPUT out1 COMMAND cmd arg1 arg2) +check_rule_property(rule1 FILE_SET_CONFIGURATORS "") +check_rule_property(rule1 SOURCE_CONFIGURATORS "") + +add_custom_rule(rule2 OUTPUT out1 COMMAND cmd arg1 arg2 CONFIGURATOR FOR_FILE_SET fileset1_configurator) +check_rule_property(rule2 FILE_SET_CONFIGURATORS "fileset1_configurator") +check_rule_property(rule2 SOURCE_CONFIGURATORS "") + +add_custom_rule(rule3 OUTPUT out1 COMMAND cmd arg1 arg2 CONFIGURATOR FOR_SOURCE source1_configurator) +check_rule_property(rule3 FILE_SET_CONFIGURATORS "") +check_rule_property(rule3 SOURCE_CONFIGURATORS "source1_configurator") + +add_custom_rule(rule4 OUTPUT out1 COMMAND cmd arg1 arg2 CONFIGURATOR FOR_FILE_SET fileset1_configurator + FOR_SOURCE source1_configurator) +check_rule_property(rule4 FILE_SET_CONFIGURATORS "fileset1_configurator") +check_rule_property(rule4 SOURCE_CONFIGURATORS "source1_configurator") + + +add_custom_rule(rule5 FROM_RULE rule4) +check_rule_property(rule5 FILE_SET_CONFIGURATORS "fileset1_configurator") +check_rule_property(rule5 SOURCE_CONFIGURATORS "source1_configurator") + +add_custom_rule(rule6 FROM_RULE rule4 CONFIGURATOR FOR_FILE_SET fileset2_configurator + FOR_SOURCE source2_configurator) +check_rule_property(rule6 FILE_SET_CONFIGURATORS "fileset2_configurator") +check_rule_property(rule6 SOURCE_CONFIGURATORS "source2_configurator") + +add_custom_rule(rule7 FROM_RULE rule4 CONFIGURATOR FOR_FILE_SET fileset2_configurator OVERRIDE + FOR_SOURCE source2_configurator OVERRIDE) +check_rule_property(rule7 FILE_SET_CONFIGURATORS "fileset2_configurator") +check_rule_property(rule7 SOURCE_CONFIGURATORS "source2_configurator") + +add_custom_rule(rule8 FROM_RULE rule4 CONFIGURATOR FOR_FILE_SET fileset2_configurator CHAIN + FOR_SOURCE source2_configurator CHAIN) +check_rule_property(rule8 FILE_SET_CONFIGURATORS "fileset1_configurator;fileset2_configurator") +check_rule_property(rule8 SOURCE_CONFIGURATORS "source1_configurator;source2_configurator") + +add_custom_rule(rule9 FROM_RULE rule8 CONFIGURATOR FOR_FILE_SET fileset3_configurator CHAIN + FOR_SOURCE source3_configurator CHAIN) +check_rule_property(rule9 FILE_SET_CONFIGURATORS "fileset1_configurator;fileset2_configurator;fileset3_configurator") +check_rule_property(rule9 SOURCE_CONFIGURATORS "source1_configurator;source2_configurator;source3_configurator") + +if(TEST_FAILED) + message(FATAL_ERROR "${TEST_FAILED}") +endif() diff --git a/Tests/RunCMake/add_custom_rule/Rule-DEPENDS.cmake b/Tests/RunCMake/add_custom_rule/Rule-DEPENDS.cmake new file mode 100644 index 0000000000..106422407a --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/Rule-DEPENDS.cmake @@ -0,0 +1,19 @@ +include(check_property.cmake) + +unset(TEST_FAILED) + +add_custom_rule(rule1 OUTPUT out1 COMMAND cmd arg1 arg2) +check_rule_property(rule1 DEPENDS "") + +add_custom_rule(rule2 OUTPUT out1 COMMAND cmd arg1 arg2 DEPENDS dep1 dep2) +check_rule_property(rule2 DEPENDS "dep1;dep2") + +add_custom_rule(rule3 OUTPUT out1 out2 DEPENDS dep1 dep2 COMMAND cmd arg1 arg2) +check_rule_property(rule3 DEPENDS "dep1;dep2") + +add_custom_rule(rule4 OUTPUT out1 out2 DEPENDS dep1 dep2 COMMAND cmd arg1 arg2 DEPENDS dep3 dep4) +check_rule_property(rule4 DEPENDS "dep1;dep2;dep3;dep4") + +if(TEST_FAILED) + message(FATAL_ERROR "${TEST_FAILED}") +endif() diff --git a/Tests/RunCMake/add_custom_rule/Rule-DEPFILE.cmake b/Tests/RunCMake/add_custom_rule/Rule-DEPFILE.cmake new file mode 100644 index 0000000000..b53e1b715e --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/Rule-DEPFILE.cmake @@ -0,0 +1,16 @@ +include(check_property.cmake) + +unset(TEST_FAILED) + +add_custom_rule(rule1 OUTPUT out1 COMMAND cmd arg1 arg2) +check_rule_property(rule1 DEPFILE "") + +add_custom_rule(rule2 OUTPUT out1 COMMAND cmd arg1 arg2 DEPFILE depfile) +check_rule_property(rule2 DEPFILE "depfile") + +add_custom_rule(rule3 OUTPUT out1 out2 DEPFILE depfile COMMAND cmd arg1 arg2) +check_rule_property(rule3 DEPFILE "depfile") + +if(TEST_FAILED) + message(FATAL_ERROR "${TEST_FAILED}") +endif() diff --git a/Tests/RunCMake/add_custom_rule/Rule-GLOBAL.cmake b/Tests/RunCMake/add_custom_rule/Rule-GLOBAL.cmake new file mode 100644 index 0000000000..b0ede24434 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/Rule-GLOBAL.cmake @@ -0,0 +1,27 @@ +include(check_property.cmake) + +unset(TEST_FAILED) + + +add_custom_rule(rule1 OUTPUT out1 COMMAND cmd arg1 arg2) +check_rule_property(rule1 GLOBAL "0") + +add_custom_rule(rule2 OUTPUT out1 COMMAND cmd arg1 arg2 GLOBAL) +check_rule_property(rule2 GLOBAL "1") + + +add_custom_rule(rule3 FROM_RULE rule1) +check_rule_property(rule3 GLOBAL "0") + +add_custom_rule(rule4 FROM_RULE rule1 GLOBAL) +check_rule_property(rule4 GLOBAL "1") + +add_custom_rule(rule5 FROM_RULE rule2) +check_rule_property(rule5 GLOBAL "0") + +add_custom_rule(rule6 FROM_RULE rule2 GLOBAL) +check_rule_property(rule6 GLOBAL "1") + +if(TEST_FAILED) + message(FATAL_ERROR "${TEST_FAILED}") +endif() diff --git a/Tests/RunCMake/add_custom_rule/Rule-OUTPUT.cmake b/Tests/RunCMake/add_custom_rule/Rule-OUTPUT.cmake new file mode 100644 index 0000000000..7a5f7f5b89 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/Rule-OUTPUT.cmake @@ -0,0 +1,16 @@ +include(check_property.cmake) + +unset(TEST_FAILED) + +add_custom_rule(rule1 OUTPUT out1 COMMAND cmd arg1 arg2) +check_rule_property(rule1 OUTPUT "out1") + +add_custom_rule(rule2 OUTPUT out1 out2 COMMAND cmd arg1 arg2) +check_rule_property(rule2 OUTPUT "out1;out2") + +add_custom_rule(rule3 OUTPUT out1 out2 COMMAND cmd arg1 arg2 OUTPUT out3 out4) +check_rule_property(rule3 OUTPUT "out1;out2;out3;out4") + +if(TEST_FAILED) + message(FATAL_ERROR "${TEST_FAILED}") +endif() diff --git a/Tests/RunCMake/add_custom_rule/RuleProperties.cmake b/Tests/RunCMake/add_custom_rule/RuleProperties.cmake new file mode 100644 index 0000000000..578752dd4f --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/RuleProperties.cmake @@ -0,0 +1,35 @@ +include(check_property.cmake) + +unset(TEST_FAILED) + + +add_custom_rule(rule1 OUTPUT out1 COMMAND cmd arg1 arg2) +check_rule_property(rule1 NAME "rule1") +check_rule_property(rule1 PARENT_RULE "") +check_rule_property(rule1 COMMAND_EXPAND_LISTS "1") +check_rule_property(rule1 VERBATIM "1") +check_rule_property(rule1 OUTPUT_FILE_SET "__cmake_rule____outputs;SOURCES") + +add_custom_rule(rule2 FROM_RULE rule1) +check_rule_property(rule2 NAME "rule2") +check_rule_property(rule2 PARENT_RULE "rule1") +check_rule_property(rule2 COMMAND_EXPAND_LISTS "1") +check_rule_property(rule2 VERBATIM "1") +check_rule_property(rule1 OUTPUT_FILE_SET "__cmake_rule____outputs;SOURCES") + +set_property(RULE rule1 PROPERTY COMMAND_EXPAND_LISTS "0") +set_property(RULE rule1 PROPERTY VERBATIM "0") +add_custom_rule(rule3 FROM_RULE rule1) +check_rule_property(rule3 NAME "rule3") +check_rule_property(rule3 PARENT_RULE "rule1") +check_rule_property(rule3 COMMAND_EXPAND_LISTS "0") +check_rule_property(rule3 VERBATIM "0") + +add_custom_rule(rule4 FROM_RULE rule2) +check_rule_property(rule4 NAME "rule4") +check_rule_property(rule4 PARENT_RULE "rule2") + + +if(TEST_FAILED) + message(FATAL_ERROR "${TEST_FAILED}") +endif() diff --git a/Tests/RunCMake/add_custom_rule/RunCMakeTest.cmake b/Tests/RunCMake/add_custom_rule/RunCMakeTest.cmake new file mode 100644 index 0000000000..57e9fc8617 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/RunCMakeTest.cmake @@ -0,0 +1,49 @@ +include(RunCMake) + +# check syntax: add_custom_rule(... COMMAND ...) +run_cmake(NoArguments) +run_cmake(NoRuleName) +run_cmake(InvalidRuleName) +run_cmake(MissingRequiredOption-COMMAND) +run_cmake(MissingRequiredOption-OUTPUT) +run_cmake(MissingRequiredValues1) +run_cmake(MissingRequiredValues2) +run_cmake(InvalidDuplicateOption-DEPFILE) +run_cmake(InvalidDuplicateOption-CONFIGURATOR) +run_cmake(InvalidDuplicateOption-FOR_FILE_SET) +run_cmake(InvalidDuplicateOption-FOR_SOURCE) +run_cmake(InvalidValue1-FOR_FILE_SET) +run_cmake(InvalidValue2-FOR_FILE_SET) +run_cmake(InvalidValue1-FOR_SOURCE) +run_cmake(InvalidValue2-FOR_SOURCE) + +# check syntax: add_custom_rule(... FROM_RULE ...) +run_cmake(FROM_RULE-NoRuleName) +run_cmake(FROM_RULE-MissingRequiredValue-FROM_RULE) +run_cmake(FROM_RULE-MissingRequiredValue-FOR_FILE_SET) +run_cmake(FROM_RULE-MissingRequiredValue-FOR_SOURCE) +run_cmake(FROM_RULE-InvalidOption-COMMAND) +run_cmake(FROM_RULE-InvalidOption-OUTPUT) +run_cmake(FROM_RULE-InvalidOption-DEPENDS) +run_cmake(FROM_RULE-InvalidOption-BYPRODUCTS) +run_cmake(FROM_RULE-InvalidOption-DEPFILE) +run_cmake(FROM_RULE-InvalidOption-FOR_FILE_SET) +run_cmake(FROM_RULE-InvalidOption-FOR_SOURCE) +run_cmake(FROM_RULE-InvalidDuplicateOption-CONFIGURATOR) +run_cmake(FROM_RULE-InvalidDuplicateOption-FOR_FILE_SET) +run_cmake(FROM_RULE-InvalidDuplicateOption-FOR_SOURCE) +run_cmake(FROM_RULE-InvalidValue-FROM_RULE) +run_cmake(FROM_RULE-InvalidValue1-FOR_FILE_SET) +run_cmake(FROM_RULE-InvalidValue2-FOR_FILE_SET) +run_cmake(FROM_RULE-InvalidValue1-FOR_SOURCE) +run_cmake(FROM_RULE-InvalidValue2-FOR_SOURCE) + +# check rule creation +run_cmake(Rule-COMMAND) +run_cmake(Rule-OUTPUT) +run_cmake(Rule-DEPENDS) +run_cmake(Rule-BYPRODUCTS) +run_cmake(Rule-DEPFILE) +run_cmake(Rule-CONFIGURATOR) +run_cmake(Rule-GLOBAL) +run_cmake(RuleProperties) diff --git a/Tests/RunCMake/add_custom_rule/check_property.cmake b/Tests/RunCMake/add_custom_rule/check_property.cmake new file mode 100644 index 0000000000..f9e5eac817 --- /dev/null +++ b/Tests/RunCMake/add_custom_rule/check_property.cmake @@ -0,0 +1,8 @@ + +function(check_rule_property rule property expected) + get_property(value RULE ${rule} PROPERTY ${property}) + if(NOT "${value}" STREQUAL "${expected}") + string (APPEND TEST_FAILED "RULE ${rule}, PROPERTY ${property}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n") + endif() + return(PROPAGATE TEST_FAILED) +endfunction() diff --git a/Tests/RunCMake/get_property/BadScope-stderr.txt b/Tests/RunCMake/get_property/BadScope-stderr.txt index 7e70b3aa5b..a6218729c7 100644 --- a/Tests/RunCMake/get_property/BadScope-stderr.txt +++ b/Tests/RunCMake/get_property/BadScope-stderr.txt @@ -1,5 +1,5 @@ ^CMake Error at BadScope\.cmake:1 \(get_property\): get_property given invalid scope FOO\. Valid scopes are GLOBAL, DIRECTORY, - TARGET, FILE_SET, SOURCE, TEST, VARIABLE, CACHE, INSTALL\. + RULE, TARGET, FILE_SET, SOURCE, TEST, VARIABLE, CACHE, INSTALL\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/get_property/directory_properties-stderr.txt b/Tests/RunCMake/get_property/directory_properties-stderr.txt index 33eb146cc0..4b4eae5277 100644 --- a/Tests/RunCMake/get_property/directory_properties-stderr.txt +++ b/Tests/RunCMake/get_property/directory_properties-stderr.txt @@ -22,6 +22,10 @@ get_directory_property: -->Imported1Top;Imported2Top<-- get_property: -->Imported1Top;Imported2Top<-- get_directory_property: -->Imported1Sub;Imported2Sub<-- get_property: -->Imported1Sub;Imported2Sub<-- +get_directory_property: -->Rule1Top;Rule2Top<-- +get_property: -->Rule1Top;Rule2Top<-- +get_directory_property: -->Rule1Sub;Rule2Sub<-- +get_property: -->Rule1Sub;Rule2Sub<-- get_directory_property: -->[^<;]*/Tests/RunCMake/get_property/directory_properties-build<-- get_property: -->[^<;]*/Tests/RunCMake/get_property/directory_properties-build<-- get_directory_property: -->[^<;]*/RunCMake/get_property<-- diff --git a/Tests/RunCMake/get_property/directory_properties.cmake b/Tests/RunCMake/get_property/directory_properties.cmake index 4dce52440e..b834650b5d 100644 --- a/Tests/RunCMake/get_property/directory_properties.cmake +++ b/Tests/RunCMake/get_property/directory_properties.cmake @@ -21,6 +21,9 @@ add_library(my::InterfaceTop ALIAS InterfaceTop) add_library(Imported1Top INTERFACE IMPORTED) add_library(Imported2Top INTERFACE IMPORTED) +add_custom_rule(Rule1Top OUTPUT out COMMAND cmd) +add_custom_rule(Rule2Top FROM_RULE Rule1Top) + add_subdirectory(directory_properties) check_directory_property("${CMAKE_CURRENT_SOURCE_DIR}" SUBDIRECTORIES) check_directory_property("${CMAKE_CURRENT_SOURCE_DIR}/directory_properties" SUBDIRECTORIES) @@ -28,6 +31,8 @@ check_directory_property("${CMAKE_CURRENT_SOURCE_DIR}" BUILDSYSTEM_TARGETS) check_directory_property("${CMAKE_CURRENT_SOURCE_DIR}/directory_properties" BUILDSYSTEM_TARGETS) check_directory_property("${CMAKE_CURRENT_SOURCE_DIR}" IMPORTED_TARGETS) check_directory_property("${CMAKE_CURRENT_SOURCE_DIR}/directory_properties" IMPORTED_TARGETS) +check_directory_property("${CMAKE_CURRENT_SOURCE_DIR}" RULES) +check_directory_property("${CMAKE_CURRENT_SOURCE_DIR}/directory_properties" RULES) check_directory_property("${CMAKE_CURRENT_SOURCE_DIR}" BINARY_DIR) check_directory_property("${CMAKE_CURRENT_SOURCE_DIR}" SOURCE_DIR) diff --git a/Tests/RunCMake/get_property/directory_properties/CMakeLists.txt b/Tests/RunCMake/get_property/directory_properties/CMakeLists.txt index 9fab799675..bc8f93834e 100644 --- a/Tests/RunCMake/get_property/directory_properties/CMakeLists.txt +++ b/Tests/RunCMake/get_property/directory_properties/CMakeLists.txt @@ -8,5 +8,8 @@ add_library(my::InterfaceSub ALIAS InterfaceSub) add_library(Imported1Sub INTERFACE IMPORTED) add_library(Imported2Sub INTERFACE IMPORTED) +add_custom_rule(Rule1Sub OUTPUT out COMMAND cmd) +add_custom_rule(Rule2Sub FROM_RULE Rule1Sub) + add_test(Sub/test1 COMMAND "${CMAKE_COMMAND}" -E echo "Sub/test1") add_test(Sub/test2 COMMAND "${CMAKE_COMMAND}" -E echo "Sub/test2") diff --git a/Tests/RunCMake/target_sources/FileSetBadName-stderr.txt b/Tests/RunCMake/target_sources/FileSetBadName-stderr.txt index a0b8054f87..233913bef3 100644 --- a/Tests/RunCMake/target_sources/FileSetBadName-stderr.txt +++ b/Tests/RunCMake/target_sources/FileSetBadName-stderr.txt @@ -1,6 +1,6 @@ ^CMake Error at FileSetBadName\.cmake:[0-9]+ \(target_sources\): target_sources Non-default file set name must contain only letters, numbers, and underscores, and must not start with a capital letter or - underscore + underscore\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/target_sources/FileSetChangeScope-stderr.txt b/Tests/RunCMake/target_sources/FileSetChangeScope-stderr.txt index 600d006956..7e1d96e3e3 100644 --- a/Tests/RunCMake/target_sources/FileSetChangeScope-stderr.txt +++ b/Tests/RunCMake/target_sources/FileSetChangeScope-stderr.txt @@ -1,5 +1,5 @@ ^CMake Error at FileSetChangeScope\.cmake:[0-9]+ \(target_sources\): target_sources Scope PUBLIC for file set "a" does not match original scope - INTERFACE + INTERFACE\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/target_sources/FileSetChangeType-stderr.txt b/Tests/RunCMake/target_sources/FileSetChangeType-stderr.txt index 3fd5a97188..742659b4f3 100644 --- a/Tests/RunCMake/target_sources/FileSetChangeType-stderr.txt +++ b/Tests/RunCMake/target_sources/FileSetChangeType-stderr.txt @@ -1,5 +1,5 @@ ^CMake Error at FileSetChangeType\.cmake:[0-9]+ \(target_sources\): target_sources Type "CXX_MODULES" for file set "a" does not match original - type "HEADERS" + type "HEADERS"\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/target_sources/FileSetCustomTarget-stderr.txt b/Tests/RunCMake/target_sources/FileSetCustomTarget-stderr.txt index 8ab3de7518..56444024ca 100644 --- a/Tests/RunCMake/target_sources/FileSetCustomTarget-stderr.txt +++ b/Tests/RunCMake/target_sources/FileSetCustomTarget-stderr.txt @@ -1,4 +1,5 @@ ^CMake Error at FileSetCustomTarget\.cmake:[0-9]+ \(target_sources\): - target_sources FILE_SETs may not be added to custom targets + target_sources FILE_SETs of type "HEADERS", "SOURCES", "CXX_MODULES" may + not be added to custom targets\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongType-stderr.txt b/Tests/RunCMake/target_sources/FileSetDefaultWrongType-stderr.txt index 85da541d64..85c5bedee5 100644 --- a/Tests/RunCMake/target_sources/FileSetDefaultWrongType-stderr.txt +++ b/Tests/RunCMake/target_sources/FileSetDefaultWrongType-stderr.txt @@ -1,5 +1,5 @@ ^CMake Error at FileSetDefaultWrongType\.cmake:[0-9]+ \(target_sources\): target_sources FILE_SET names starting with a capital letter are reserved - for built-in file sets and may only be "HEADERS", "SOURCES", "CXX_MODULES" + for built-in file sets and may only be "HEADERS", "SOURCES", "CXX_MODULES"\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/target_sources/FileSetFramework1-stderr.txt b/Tests/RunCMake/target_sources/FileSetFramework1-stderr.txt index 5a2e65a595..20e33191d0 100644 --- a/Tests/RunCMake/target_sources/FileSetFramework1-stderr.txt +++ b/Tests/RunCMake/target_sources/FileSetFramework1-stderr.txt @@ -1,5 +1,5 @@ CMake Error at FileSetFramework1\.cmake:[0-9]+ \(target_sources\): target_sources FILE_SETs, of type "HEADERS", may not be added to FRAMEWORK - targets + targets\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/target_sources/FileSetNoType-stderr.txt b/Tests/RunCMake/target_sources/FileSetNoType-stderr.txt index 5405fdb778..b9c459ff2f 100644 --- a/Tests/RunCMake/target_sources/FileSetNoType-stderr.txt +++ b/Tests/RunCMake/target_sources/FileSetNoType-stderr.txt @@ -1,4 +1,4 @@ ^CMake Error at FileSetNoType\.cmake:[0-9]+ \(target_sources\): - target_sources Must specify a TYPE when creating file set + target_sources Must specify a TYPE when creating file set\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/target_sources/FileSetWrongType-stderr.txt b/Tests/RunCMake/target_sources/FileSetWrongType-stderr.txt index f87e393c70..913a2f8998 100644 --- a/Tests/RunCMake/target_sources/FileSetWrongType-stderr.txt +++ b/Tests/RunCMake/target_sources/FileSetWrongType-stderr.txt @@ -1,5 +1,6 @@ ^CMake Error at FileSetWrongType\.cmake:[0-9]+ \(target_sources\): - target_sources File set TYPE may only be "HEADERS", "SOURCES", - "CXX_MODULES" + target_sources File set TYPE may only be one of the built-in types + "HEADERS", "SOURCES", "CXX_MODULES" or a RULE visible from the current + directory\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Utilities/Sphinx/cmake.py b/Utilities/Sphinx/cmake.py index dc53f033a4..c1667fb313 100644 --- a/Utilities/Sphinx/cmake.py +++ b/Utilities/Sphinx/cmake.py @@ -254,6 +254,7 @@ _cmake_index_objs = { 'prop_fs': _cmake_index_entry('file set property'), 'prop_gbl': _cmake_index_entry('global property'), 'prop_inst': _cmake_index_entry('installed file property'), + 'prop_rule': _cmake_index_entry('rule property'), 'prop_sf': _cmake_index_entry('source file property'), 'prop_test': _cmake_index_entry('test property'), 'prop_tgt': _cmake_index_entry('target property'), @@ -775,6 +776,7 @@ class CMakeDomain(Domain): 'prop_fs': ObjType('prop_fs', 'prop_fs'), 'prop_gbl': ObjType('prop_gbl', 'prop_gbl'), 'prop_inst': ObjType('prop_inst', 'prop_inst'), + 'prop_rule': ObjType('prop_rule', 'prop_rule'), 'prop_sf': ObjType('prop_sf', 'prop_sf'), 'prop_test': ObjType('prop_test', 'prop_test'), 'prop_tgt': ObjType('prop_tgt', 'prop_tgt'), @@ -809,6 +811,7 @@ class CMakeDomain(Domain): 'prop_fs': CMakeXRefRole(), 'prop_gbl': CMakeXRefRole(), 'prop_inst': CMakeXRefRole(), + 'prop_rule': CMakeXRefRole(), 'prop_sf': CMakeXRefRole(), 'prop_test': CMakeXRefRole(), 'prop_tgt': CMakeXRefRole(), diff --git a/bootstrap b/bootstrap index 1c11e76e75..4e90d967b0 100755 --- a/bootstrap +++ b/bootstrap @@ -262,6 +262,7 @@ CMAKE_UNUSED_SOURCES="\ CMAKE_CXX_SOURCES="\ cmAddCompileDefinitionsCommand \ cmAddCustomCommandCommand \ + cmAddCustomRuleCommand \ cmAddCustomTargetCommand \ cmAddDefinitionsCommand \ cmAddDependenciesCommand \ @@ -312,6 +313,7 @@ CMAKE_CXX_SOURCES="\ cmCustomCommand \ cmCustomCommandGenerator \ cmCustomCommandLines \ + cmCustomRule \ cmCxxModuleMapper \ cmCxxModuleUsageEffects \ cmDefinePropertyCommand \ @@ -373,6 +375,7 @@ CMAKE_CXX_SOURCES="\ cmGeneratorExpressionParser \ cmGeneratorFileSet \ cmGeneratorFileSets \ + cmGeneratorRule \ cmGeneratorTarget \ cmGeneratorTarget_CompatibleInterface \ cmGeneratorTarget_HeaderSetVerification \ @@ -459,6 +462,7 @@ CMAKE_CXX_SOURCES="\ cmPackageInfoReader \ cmPlaceholderExpander \ cmPlistParser \ + cmRule \ cmRulePlaceholderExpander \ cmRuntimeDependencyArchive \ cmScriptGenerator \ @@ -474,6 +478,7 @@ CMAKE_CXX_SOURCES="\ cmSourceFile \ cmSourceFileLocation \ cmSourceFilePropertyHelper \ + cmSpecializedRule \ cmStandardLevelResolver \ cmState \ cmStateDirectory \