Merge topic 'custom-rule-support'

386d1d874c Add support of custom rules

Acked-by: Kitware Robot <kwrobot@kitware.com>
Tested-by: buildbot <buildbot@kitware.com>
Merge-request: !12361
This commit is contained in:
Brad King
2026-09-21 11:40:22 -04:00
committed by Kitware Robot
254 changed files with 4454 additions and 126 deletions
+1
View File
@@ -723,3 +723,4 @@ See Also
^^^^^^^^
* :command:`add_custom_target`
* :command:`add_custom_rule`
+402
View File
@@ -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(<name> `OUTPUT`_ <output1> [<output2> ...]
COMMAND <command1> [<args1>...]
[...])
`Derived Rule`_
add_custom_rule(<name> `FROM_RULE`_ <rule>
[...])
Generating Files
^^^^^^^^^^^^^^^^
.. signature::
add_custom_rule(<name> OUTPUT <output1> [<output2> ...]
COMMAND <command1> [<args1>...]
[...])
:target:
OUTPUT
Add a custom template rule to produce an output:
.. code-block:: cmake
add_custom_rule(<name> OUTPUT <output1> [<output2> ...]
COMMAND <command1> [<args1>...]
[COMMAND <command2> [<args2>...]] ...
[DEPENDS <depends>...]
[BYPRODUCTS <files>...]
[DEPFILE <depfile>]
[CONFIGURATOR [FOR_FILE_SET <configurator>]
[FOR_SOURCE <configurator>]]
[GLOBAL])
This defines a template rule ``<name>`` 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 <File Sets>` of type ``<name>``. For each file of the file
set, a :command:`custom command <add_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 <Rule 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.
``<RULE>``
Name of the rule used as template.
``<TARGET>``
Name of the target to which the file set of sources is attached.
``<FILE_SET>``
Name of the file set used for the rule instantiation.
``<SOURCE_DIR>``
The value of the :variable:`CMAKE_SOURCE_DIR` variable.
``<BINARY_DIR>``
The value of the :variable:`CMAKE_BINARY_DIR` variable.
``<CURRENT_SOURCE_DIR>``
The path to the source directory of the file set creation.
``<CURRENT_BINARY_DIR>``
The path to the binary directory of the file set creation.
``<SOURCE>``
The full path of the current source file being processed.
``<INPUT_DIR>``
The directory of the current source file being processed.
``<FILE_NAME>``
The file name of the current source file being processed.
``<BASE_NAME>``
The stem name (i.e. without directory and extension) of the source file
being processed.
``<INCLUDE_DIRECTORIES>``
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
<cmake-generator-expressions(7)>`, 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
$<LIST:TRANSFORM,<INCLUDE_DIRECTORIES>,PREPEND,-inc:>
``<COMPILE_DEFINITIONS>``
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
<cmake-generator-expressions(7)>`, 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
$<LIST:TRANSFORM,<COMPILE_DEFINITIONS>,PREPEND,-def:>
``<COMPILE_OPTIONS>``
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 <CMake Language Lists>` 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 <CMake Language Lists>` 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:`<RULE>_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 <OUTFILE_DIR> pattern
set(${patterns} "OUTFILE_DIR=<CURRENT_BINARY_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 "$<IF:$<BOOL:$<SOURCE_PROPERTY:<SOURCE>,TARGET_DIRECTORY:<TARGET>,CPLUSPLUS>>,.cxx,.c>")
set(SWIG_LANGUAGE "-$<STRING:TOLOWER,$<FILE_SET_PROPERTY:<FILE_SET>,TARGET:<TARGET>,LANGUAGE>>")
add_custom_rule(swig
OUTPUT "<OUTFILE_DIR>/<BASE_NAME>${OUTFILE_EXT}"
COMMAND ${SWIG_EXECUTABLE} "<SOURCE>"
"<OUTFILE_DIR>/<BASE_NAME>${OUTFILE_EXT}"
${SWIG_LANGUAGE}
<COMPILE_OPTIONS>
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(<name> FROM_RULE <rule>
[...])
:target:
FROM_RULE
Create a new template rule ``<name>`` inheriting a snapshot of all the
characteristics of the ``<rule>``, 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(<name> FROM_RULE <rule>
[CONFIGURATOR [FOR_FILE_SET <configurator> [CHAIN|OVERRIDE]]
[FOR_SOURCE <configurator> [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 ``<configurator>`` will be added to the already specified
configurators of inherited rules. Configurators will be called in order
of their rules' definition.
``OVERRIDE``
The specified ``<configurator>`` 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 ``<OUTFILE_DIR>`` 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`
+7
View File
@@ -8,6 +8,7 @@ Get a property.
get_property(<variable>
<GLOBAL |
DIRECTORY [<dir>] |
RULE <rule> |
TARGET <target> |
FILE_SET <file_set> TARGET <target> |
SOURCE <source>
@@ -39,6 +40,12 @@ It must be one of the following:
.. versionadded:: 3.19
``<dir>`` 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.
+7
View File
@@ -7,6 +7,7 @@ Set a named property in a given scope.
set_property({GLOBAL |
DIRECTORY [<dir>] |
RULE <rule>... |
TARGET <target>... |
FILE_SET <file_set>... TARGET <target> |
SOURCE <source>...
@@ -36,6 +37,12 @@ It must be one of the following:
.. versionadded:: 3.19
``<dir>`` 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.
+12 -2
View File
@@ -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
``<rule>``
.. 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 ``<rule>`` 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
+1
View File
@@ -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
@@ -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:: $<RULE_PROPERTY:rule,prop>
.. 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 ``<RULE>``. Any other value will raise an error.
Toolchain And Language Expressions
----------------------------------
+36
View File
@@ -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
+12
View File
@@ -0,0 +1,12 @@
RULES
-----
.. versionadded:: 4.5
This read-only directory property contains a
:ref:`semicolon-separated list <CMake Language Lists>` 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.
+5 -6
View File
@@ -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 <CMake Language Lists>` 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
+5 -5
View File
@@ -5,11 +5,11 @@ INCLUDE_DIRECTORIES
List of preprocessor include file search directories.
This property holds a :ref:`semicolon-separated list <CMake Language Lists>` 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 <CMake Language Lists>` 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.
+1
View File
@@ -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
+27
View File
@@ -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 <CMake Language Lists>` 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=<OUTPUT_DIR>/my_file``, when the pattern
``<OUTPUT_FILE>`` is expanded, the pattern ``<OUTPUT_DIR>`` is already known.
Related properties:
* :prop_sf:`<RULE>_PATTERNS` to specify patterns for a specific file.
Related commands:
* :command:`add_custom_rule` for custom rule specification.
+9
View File
@@ -0,0 +1,9 @@
BYPRODUCTS
----------
.. versionadded:: 4.5
Read-only property giving a
:ref:`semicolon-separated list <CMake Language Lists>` of the produced
artifacts, if any, by the rule as specified by the ``BYPRODUCTS`` option of the
:command:`add_custom_rule` command.
+12
View File
@@ -0,0 +1,12 @@
COMMAND
-------
.. versionadded:: 4.5
Read-only property giving a
:ref:`semicolon-separated list <CMake Language Lists>` 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_<INDEX>` rule property with the
index ``0``.
+10
View File
@@ -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 ``<INDEX>``th ``COMMAND``, use the :prop_rule:`COMMAND_<INDEX>`
rule property.
+11
View File
@@ -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 <cmake-generator-expressions(7)>`.
By default, ``COMMAND_EXPAND_LISTS`` is true.
+16
View File
@@ -0,0 +1,16 @@
COMMAND_<INDEX>
---------------
.. versionadded:: 4.5
Read-only property giving a
:ref:`semicolon-separated list <CMake Language Lists>` of the arguments of the
rule as specified by the ``<INDEX>``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.
+8
View File
@@ -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 <cmake-generator-expressions(7)>`.
+24
View File
@@ -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 <CMake Language Lists>` 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
<cmake-generator-expressions(7)>` with the syntax ``$<...>``. See the
:manual:`cmake-buildsystem(7)` manual for more on defining buildsystem
properties.
+15
View File
@@ -0,0 +1,15 @@
COMPILE_OPTIONS
---------------
List of options to pass to the compiler.
This property holds a :ref:`semicolon-separated list <CMake Language Lists>`
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
<cmake-generator-expressions(7)>` with the syntax ``$<...>``. See the
:manual:`cmake-buildsystem(7)` manual for more on defining buildsystem
properties.
.. include:: ../command/include/OPTIONS_SHELL.rst
+9
View File
@@ -0,0 +1,9 @@
DEPENDS
-------
.. versionadded:: 4.5
Read-only property giving a
:ref:`semicolon-separated list <CMake Language Lists>` of the dependencies, if
any, of the rule as specified by the ``DEPENDS`` option of the
:command:`add_custom_rule` command.
+11
View File
@@ -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.
+7
View File
@@ -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.
@@ -0,0 +1,9 @@
FILE_SET_CONFIGURATORS
----------------------
.. versionadded:: 4.5
Read-only property giving a
:ref:`semicolon-separated list <CMake Language Lists>` of the file set
configurators, as specified by ``CONFIGURATOR FOR_FILE_SET`` option, given in
the order of their evaluation.
+20
View File
@@ -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.
+20
View File
@@ -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 <CMake Language Lists>` 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
<cmake-generator-expressions(7)>` with the syntax ``$<...>``. See the
:manual:`cmake-buildsystem(7)` manual for more on defining buildsystem
properties.
+17
View File
@@ -0,0 +1,17 @@
JOB_POOL_COMPILE
----------------
.. versionadded:: 4.5
:ref:`Ninja only <Ninja Generators>`: 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
+15
View File
@@ -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
+6
View File
@@ -0,0 +1,6 @@
NAME
----
.. versionadded:: 4.5
Read-only property giving the name of the rule.
+9
View File
@@ -0,0 +1,9 @@
OUTPUT
------
.. versionadded:: 4.5
Read-only property giving a
:ref:`semicolon-separated list <CMake Language Lists>` of outputs of the rule
as specified by the ``OUTPUT`` option of the :command:`add_custom_rule`
command.
+23
View File
@@ -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 <CMake Language Lists>` 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:
* ``<RULE>``: name of the rule
* ``<TARGET>``: name of the target
* ``<FILE_SET>``: name of the file set
The type must be one of the :ref:`predefined types <File Sets>`.
This property gets the following default value:
``__cmake_rule_<RULE>_<TARGET>_<FILE_SET>_outputs;SOURCES``.
+8
View File
@@ -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.
+9
View File
@@ -0,0 +1,9 @@
SOURCE_CONFIGURATORS
--------------------
.. versionadded:: 4.5
Read-only property giving a
:ref:`semicolon-separated list <CMake Language Lists>` of the source
configurators, as specified by ``CONFIGURATOR FOR_SOURCE`` option, given in
the order of their evaluation.
+9
View File
@@ -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 <JOB_POOLS>`.
+15
View File
@@ -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.
+13
View File
@@ -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 <cmake-generator-expressions(7)>`.
+1
View File
@@ -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
+27
View File
@@ -0,0 +1,27 @@
<RULE>_PATTERNS
---------------
.. versionadded:: 4.5
Patterns specification for instantiating the rule ``<RULE>``.
The ``<RULE>_PATTERNS`` property may be set to a
:ref:`semicolon-separated list <CMake Language Lists>` 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=<OUTPUT_DIR>/my_file``, when the pattern
``<OUTPUT_FILE>`` is expanded, the pattern ``<OUTPUT_DIR>`` 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.
+1
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
add_custom_rule
---------------
* The :command:`add_custom_rule` command was added to allow the definition of
pattern rules.
+5
View File
@@ -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.
+8
View File
@@ -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
+445
View File
@@ -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 <algorithm>
#include <utility>
#include <cm/memory>
#include <cm/optional>
#include <cm/string_view>
#include <cmext/algorithm>
#include <cmext/string_view>
#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 <typename Result>
class FromRuleArgumentParser : public cmArgumentParser<Result>
{
public:
FromRuleArgumentParser()
: cmArgumentParser<Result>()
{
this->Bind("CHAIN"_s, &Result::Chain)
.Bind("OVERRIDE"_s, &Result::Override)
.BindParsedKeywords(&Result::ParsedKeywords);
}
};
}
bool cmAddCustomRuleCommand(std::vector<std::string> 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<ArgumentParser::NonEmpty<std::vector<std::string>>>
Configurators;
bool Global = false;
std::vector<cm::string_view> 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<std::string> unexpectedArgs;
auto parser = cmArgumentParser<Arguments>{}
.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: <configurator> (CHAIN|OVERRIDE)
struct ConfiguratorArguments : public ArgumentParser::ParseResult
{
std::string Configurator;
bool Chain = false;
bool Override = false;
std::vector<cm::string_view> ParsedKeywords;
};
// configurators syntax: FOR_FILE_SET <configurator> <options>
// FOR_SOURCE <configurator> <options>
struct ConfiguratorsArguments : public ArgumentParser::ParseResult
{
cm::optional<ConfiguratorArguments> ForFileSet;
cm::optional<ConfiguratorArguments> ForSource;
} parsedConfigurators;
if (parsedArgs.Configurators) {
auto fileSetConfiguratorParser =
FromRuleArgumentParser<ConfiguratorArguments>{}.Bind(
FOR_FILE_SET, &ConfiguratorArguments::Configurator);
auto sourceConfiguratorParser =
FromRuleArgumentParser<ConfiguratorArguments>{}.Bind(
FOR_SOURCE, &ConfiguratorArguments::Configurator);
auto configuratorsParser =
cmArgumentParser<ConfiguratorsArguments>{}
.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<ConfiguratorArguments>& configurator,
cm::string_view type) -> bool {
if (!configurator) {
return true;
}
ConfiguratorArguments& ca = configurator.value();
cm::optional<cmStateEnums::CommandType> 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<cmSpecializedRule>(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<std::vector<std::string>> Output;
cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>>
Byproducts;
ArgumentParser::NonEmpty<std::vector<std::vector<std::string>>> Commands;
cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Depends;
cm::optional<std::string> Depfile;
};
std::vector<std::string> unexpectedArgs;
auto parser = cmArgumentParser<Arguments>{}
.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<std::string> ForFileSet;
cm::optional<std::string> ForSource;
std::vector<cm::string_view> ParsedKeywords;
} parsedConfigurators;
if (parsedArgs.Configurators) {
// parse the arguments of CONFIGURATOR option
// CONFIGURATOR syntax: FOR_FILE_SET <configurator>
// FOR_SOURCE <configurator>
auto configuratorsParser =
cmArgumentParser<ConfiguratorsArguments>{}
.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<std::string> 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<cmCustomRule>(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;
}
+13
View File
@@ -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 <string>
#include <vector>
class cmExecutionStatus;
bool cmAddCustomRuleCommand(std::vector<std::string> const& args,
cmExecutionStatus& status);
+2
View File
@@ -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);
+1 -1
View File
@@ -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);
+55
View File
@@ -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 <string>
#include <utility>
#include <vector>
#include "cmRule.h"
class cmMakefile;
cmCustomRule::cmCustomRule(cmMakefile& makefile, std::string name,
CustomCommands commands,
std::vector<std::string> 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<std::string> const& cmCustomRule::GetOutputs() const
{
return this->Outputs;
}
void cmCustomRule::SetByproducts(std::vector<std::string> byproducts)
{
this->Byproducts = std::move(byproducts);
}
std::vector<std::string> const& cmCustomRule::GetByproducts() const
{
return this->Byproducts;
}
void cmCustomRule::SetDepends(std::vector<std::string> depends)
{
this->Depends = std::move(depends);
}
std::vector<std::string> 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;
}
+12
View File
@@ -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<FileSetDomain, 2>;
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)
+85
View File
@@ -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<std::string> 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(),
"$<RULE_PROPERTY:rule,prop> expression requires a "
"non-empty rule name and property name.");
return std::string{};
}
if (ruleName.empty()) {
reportError(eval, content->GetOriginalExpression(),
"$<RULE_PROPERTY:rule,prop> expression requires a "
"non-empty rule name.");
return std::string{};
}
if (propertyName.empty()) {
reportError(eval, content->GetOriginalExpression(),
"$<RULE_PROPERTY:src,prop> 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", &quoteNode },
{ "RULE_PROPERTY", &rulePropertyNode },
{ "SOURCE_EXISTS", &sourceExistsNode },
{ "SOURCE_PROPERTY", &sourcePropertyNode },
{ "FILE_SET_EXISTS", &fileSetExistsNode },
+4 -2
View File
@@ -174,10 +174,12 @@ std::vector<BT<std::string>> 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();
+5 -1
View File
@@ -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<BT<std::string>> GetIncludeDirectories(
@@ -130,6 +133,7 @@ public:
private:
cmGeneratorTarget const* Target;
cmFileSet const* FileSet;
cm::FileSetMetadata::FileSetDomain Domain;
mutable std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>
CompiledDirectoryEntries;
mutable std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>
+32 -27
View File
@@ -12,9 +12,9 @@
#include <cm/memory>
#include <cm/optional>
#include <cmext/algorithm>
#include <cmext/enum_set>
#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<cmGeneratorFileSet>(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<cmGeneratorFileSet>(target, fileSet));
genFileSet = entry.first->second.get();
name, cm::make_unique<cmGeneratorFileSet>(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<cmGeneratorFileSet>(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<std::unique_ptr<cm::TargetPropertyEntry>>
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<std::unique_ptr<cm::TargetPropertyEntry>>
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);
}
+5
View File
@@ -14,6 +14,7 @@
#include <cm/string_view>
#include "cmFileSetMetadata.h"
#include "cmTargetPropertyEntry.h"
namespace cm {
@@ -67,6 +68,8 @@ public:
std::vector<std::unique_ptr<TargetPropertyEntry>> GetSources(
cm::GenEx::Context const& context, cmGeneratorTarget const* target,
cm::FileSetMetadata::FileSetDomainSet
domains = { cm::FileSetMetadata::FileSetDomain::NATIVE },
cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const;
std::vector<std::unique_ptr<TargetPropertyEntry>> GetSources(
std::string type, cm::GenEx::Context const& context,
@@ -75,6 +78,8 @@ public:
std::vector<std::unique_ptr<TargetPropertyEntry>> GetInterfaceSources(
cm::GenEx::Context const& context, cmGeneratorTarget const* target,
cm::FileSetMetadata::FileSetDomainSet
domains = { cm::FileSetMetadata::FileSetDomain::NATIVE },
cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const;
std::vector<std::unique_ptr<TargetPropertyEntry>> GetInterfaceSources(
std::string type, cm::GenEx::Context const& context,
+293
View File
@@ -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 <algorithm>
#include <iterator>
#include <utility>
#include <vector>
#include <cm/memory>
#include <cm/optional>
#include <cm/string_view>
#include <cmext/algorithm>
#include <cmext/string_view>
#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<cm::string_view> 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",
"$<LIST:FILTER,$<FILE_SET_PROPERTY:<FILE_SET>,TARGET:<TARGET>,INCLUDE_"
"DIRECTORIES>;$<SOURCE_PROPERTY:<SOURCE>,TARGET_DIRECTORY:<TARGET>,"
"INCLUDE_DIRECTORIES>;$<RULE_PROPERTY:<RULE>,INCLUDE_DIRECTORIES>,"
"EXCLUDE,^$>" },
{ "COMPILE_OPTIONS",
"$<LIST:FILTER,$<RULE_PROPERTY:<RULE>,COMPILE_OPTIONS>;$<SOURCE_"
"PROPERTY:<SOURCE>,"
"TARGET_DIRECTORY:<TARGET>,COMPILE_OPTIONS>;$<FILE_SET_PROPERTY:<FILE_"
"SET>,TARGET:<TARGET>,COMPILE_OPTIONS>,EXCLUDE,^$>" },
{ "COMPILE_DEFINITIONS",
"$<LIST:FILTER,$<RULE_PROPERTY:<RULE>,COMPILE_DEFINITIONS>;$<SOURCE_"
"PROPERTY:<"
"SOURCE>,"
"TARGET_DIRECTORY:<TARGET>,COMPILE_DEFINITIONS>;$<FILE_SET_PROPERTY:<"
"FILE_SET>,TARGET:<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<std::string>{}(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<cmCustomCommand> cmGeneratorRule::CreateCustomCommand() const
{
auto expandVariables = [this](std::string const& item) -> std::string {
return this->RuleExpander.ExpandVariables(item);
};
auto expandVector =
[&expandVariables](
std::vector<std::string> const& data) -> std::vector<std::string> {
std::vector<std::string> 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<std::string> const& data) -> std::vector<std::string> {
std::vector<std::string> 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<cmCustomCommand>();
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<std::string> 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<cmCustomCommand> 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<std::string>{ output });
// get the directory of the generated file
outFileSet->AddDirectoryEntry(
BT<std::string>{ cmStrCat("$<PATH:GET_PARENT_PATH,", output, '>') });
}
return cc;
}
+78
View File
@@ -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 <memory>
#include <string>
#include <unordered_map>
#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<cmCustomCommand> Generate(cmFileSet* outFileSet) const;
private:
void UpdateRuleExpander(cmRule::PatternSet const& patterns);
std::unique_ptr<cmCustomCommand> 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<std::string, std::string>;
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;
};
+5 -1
View File
@@ -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);
}
+30 -5
View File
@@ -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<std::string> 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<std::string> 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<std::string> 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)
+94
View File
@@ -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<cmMSVC60LinkLineComputer>(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<cmGeneratorRule>(
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<std::string> 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
{
+20
View File
@@ -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<std::string, cmTarget*>;
using GeneratorTargetMap =
std::unordered_map<std::string, cmGeneratorTarget*>;
using RuleMap = std::unordered_map<std::string, cmRule*>;
using GeneratorRuleMap = std::unordered_map<std::string, cmGeneratorRule*>;
using MakefileMap = std::unordered_map<std::string, cmMakefile*>;
using LocalGeneratorMap = std::unordered_map<std::string, cmLocalGenerator*>;
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).
+21
View File
@@ -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<cmGeneratorRule> 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.
+9
View File
@@ -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<cmGeneratorRule> gr);
cmGeneratorRule* FindGeneratorRuleToUse(std::string const& name) const;
/**
* Process a list of include directories
*/
@@ -684,6 +688,11 @@ protected:
GeneratorTargetVector OwnedImportedGeneratorTargets;
std::map<std::string, std::string> AliasTargets;
using GeneratorRuleMap = std::unordered_map<std::string, cmGeneratorRule*>;
GeneratorRuleMap GeneratorRuleSearchIndex;
using GeneratorRuleVector = std::vector<std::unique_ptr<cmGeneratorRule>>;
GeneratorRuleVector GeneratorRules;
std::map<std::string, std::string> Compilers;
std::map<std::string, std::string> VariableMappings;
std::string CompilerSysroot;
+27
View File
@@ -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<cmRule> 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<cmCustomCommand> 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;
+23
View File
@@ -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<cmRule> rule);
// -- List of custom rules
std::vector<std::unique_ptr<cmRule>> const& GetOwnedRules() const
{
return this->RulesOwned;
}
using cmRuleMap = std::unordered_map<std::string, cmRule*>;
/** 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<std::unique_ptr<cmRule>> RulesOwned;
cmRuleMap Rules;
};
+6 -3
View File
@@ -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
+2 -1
View File
@@ -18,6 +18,7 @@ public:
VARIABLE,
CACHED_VARIABLE,
INSTALL,
FILE_SET
FILE_SET,
RULE
};
};
+604
View File
@@ -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 <algorithm>
#include <iterator>
#include <memory>
#include <unordered_map>
#include <utility>
#include <cm/string_view>
#include <cmext/algorithm>
#include <cmext/string_view>
#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<cm::string_view, ReadOnlyProperty> 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<std::string, std::string>;
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_<RULE>_<TARGET>_<FILE_SET>_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<std::string> 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<cmRule> 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<void>(varScope);
static_cast<void>(polScope);
std::vector<cmListFileArgument> 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 <RULE>_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;
}
+242
View File
@@ -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 <cstddef>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#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 <typename E>
struct enum_hash
{
typename std::enable_if<std::is_enum<E>::value, std::size_t>::type
operator()(E const key) const
{
return static_cast<std::size_t>(key);
}
};
}
//
// cmRule: base class for various rule types
//
class cmRule
{
public:
using CustomCommand = std::vector<std::string>;
using CustomCommands = std::vector<CustomCommand>;
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<std::string> const& GetOutputs() const = 0;
/** Get the extra files produced by the command. */
virtual std::vector<std::string> const& GetByproducts() const = 0;
/** Get the vector that holds the list of dependencies. */
virtual std::vector<std::string> 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<std::pair<std::string const*, std::string const*>>;
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<Pattern>;
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<ConfiguratorType, std::string,
cm::enum_hash<ConfiguratorType>>
Configurators;
std::unordered_map<ConfiguratorType, ConfiguratorSet,
cm::enum_hash<ConfiguratorType>>
ConfiguratorsChain;
cmPropertyMap Properties;
std::vector<BT<std::string>> IncludeDirectories;
std::vector<BT<std::string>> CompileOptions;
std::vector<BT<std::string>> 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<std::string> 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<std::string> const& GetOutputs() const override;
/** Set/Get the extra files produced by the command. */
void SetByproducts(std::vector<std::string> byproducts);
std::vector<std::string> const& GetByproducts() const override;
/** Set/Get the vector that holds the list of dependencies. */
void SetDepends(std::vector<std::string> depends);
std::vector<std::string> 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<std::string> Outputs;
std::vector<std::string> Byproducts;
std::vector<std::string> 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<std::string> const& GetOutputs() const override;
/** Get the extra files produced by the command. */
std::vector<std::string> const& GetByproducts() const override;
/** Get the vector that holds the list of dependencies. */
std::vector<std::string> const& GetDepends() const override;
/** Get the file that holds the list of dependencies. */
std::string const& GetDepfile() const override;
private:
cmRule const& ParentRule;
};
+45 -3
View File
@@ -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<std::string> const& names,
std::string const& propertyName,
std::string const& propertyValue, bool appendAsString,
bool appendMode, bool remove);
bool HandleTargetMode(cmExecutionStatus& status,
std::set<std::string> const& names,
std::string const& propertyName,
@@ -453,6 +459,8 @@ bool cmSetPropertyCommand(std::vector<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> const& names,
std::string const& propertyName,
+45
View File
@@ -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 <string>
#include <utility>
#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<std::string> const& cmSpecializedRule::GetOutputs() const
{
return this->ParentRule.GetOutputs();
}
std::vector<std::string> const& cmSpecializedRule::GetByproducts() const
{
return this->ParentRule.GetByproducts();
}
std::vector<std::string> const& cmSpecializedRule::GetDepends() const
{
return this->ParentRule.GetDepends();
}
std::string const& cmSpecializedRule::GetDepfile() const
{
return this->ParentRule.GetDepfile();
}
+9
View File
@@ -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<std::string> 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);
}
+2
View File
@@ -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<cmStateDetail::BuildsystemDirectoryStateType>::iterator
DirectoryState;
+2
View File
@@ -105,6 +105,8 @@ struct cmStateDetail::BuildsystemDirectoryStateType
std::vector<std::string> NormalTargetNames;
std::vector<std::string> ImportedTargetNames;
std::vector<std::string> RuleNames;
std::set<std::string> Projects;
std::string ProjectName;
+110 -23
View File
@@ -171,6 +171,15 @@ struct FileSetType
cmTargetInternals const* impl) const;
};
struct FileSetRule
{
std::vector<BT<std::string>> SelfEntries;
std::vector<BT<std::string>> 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<cm::string_view, FileSetType> FileSetTypes;
std::unordered_map<std::string, FileSetRule> 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 <typename ValueType>
bool UsageRequirementProperty::Write(
cmTargetInternals const* impl, cm::optional<cmListFileBacktrace> const& bt,
@@ -3331,14 +3353,16 @@ cmFileSet* cmTarget::GetFileSet(std::string const& name)
std::pair<cmFileSet*, bool> 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<std::string> cmTarget::GetAllFileSetNames() const
std::vector<std::string> cmTarget::GetAllFileSetNames(
cm::FileSetMetadata::FileSetDomainSet domains) const
{
std::vector<std::string> 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<std::string> RetrieveFileSetNames(
void RetrieveFileSetNames(
std::unordered_map<cm::string_view, FileSetType> const& fileSetTypes,
std::function<
std::vector<BT<std::string>> const&(FileSetType const& fileSetType)>
GetFileSets)
GetFileSets,
std::vector<std::string>& result)
{
std::vector<std::string> result;
auto inserter = std::back_inserter(result);
auto appendEntries = [=](std::vector<BT<std::string>> const& entries) {
@@ -3396,27 +3431,79 @@ std::vector<std::string> RetrieveFileSetNames(
for (auto const& fileSetType : fileSetTypes) {
appendEntries(GetFileSets(fileSetType.second));
}
}
void RetrieveFileSetNames(
std::unordered_map<std::string, FileSetRule> const& fileSetRules,
std::function<
std::vector<BT<std::string>> const&(FileSetRule const& fileSetRule)>
GetFileSets,
std::vector<std::string>& result)
{
auto appendEntries = [&result](std::vector<BT<std::string>> const& entries) {
for (auto const& entry : entries) {
result.push_back(entry.Value);
}
};
for (auto const& fileSetRule : fileSetRules) {
appendEntries(GetFileSets(fileSetRule.second));
}
}
}
std::vector<std::string> cmTarget::GetAllPrivateFileSets(
cm::FileSetMetadata::FileSetDomainSet domains) const
{
std::vector<std::string> result;
if (domains.contains(cm::FileSetMetadata::FileSetDomain::NATIVE)) {
RetrieveFileSetNames(
this->impl->FileSetTypes,
[](FileSetType const& fileSetType)
-> std::vector<BT<std::string>> const& {
return fileSetType.SelfEntries.Entries;
},
result);
}
if (domains.contains(cm::FileSetMetadata::FileSetDomain::RULE)) {
RetrieveFileSetNames(
this->impl->FileSetRules,
[](FileSetRule const& fileSetRule)
-> std::vector<BT<std::string>> const& {
return fileSetRule.SelfEntries;
},
result);
}
return result;
}
}
std::vector<std::string> cmTarget::GetAllPrivateFileSets() const
std::vector<std::string> cmTarget::GetAllInterfaceFileSets(
cm::FileSetMetadata::FileSetDomainSet domains) const
{
return RetrieveFileSetNames(
this->impl->FileSetTypes,
[](FileSetType const& fileSetType) -> std::vector<BT<std::string>> const& {
return fileSetType.SelfEntries.Entries;
});
}
std::vector<std::string> result;
std::vector<std::string> cmTarget::GetAllInterfaceFileSets() const
{
return RetrieveFileSetNames(
this->impl->FileSetTypes,
[](FileSetType const& fileSetType) -> std::vector<BT<std::string>> const& {
return fileSetType.InterfaceEntries.Entries;
});
if (domains.contains(cm::FileSetMetadata::FileSetDomain::NATIVE)) {
RetrieveFileSetNames(
this->impl->FileSetTypes,
[](FileSetType const& fileSetType)
-> std::vector<BT<std::string>> const& {
return fileSetType.InterfaceEntries.Entries;
},
result);
}
if (domains.contains(cm::FileSetMetadata::FileSetDomain::RULE)) {
RetrieveFileSetNames(
this->impl->FileSetRules,
[](FileSetRule const& fileSetRule)
-> std::vector<BT<std::string>> const& {
return fileSetRule.InterfaceEntries;
},
result);
}
return result;
}
bool cmTarget::HasFileSets() const
+17 -10
View File
@@ -17,6 +17,7 @@
#include <cm/string_view>
#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<cmFileSet*, bool> 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<cmFileSet*, bool> GetOrCreateFileSet(
std::string const& name, std::string const& type,
cm::FileSetMetadata::Visibility vis, cmMakefile* mf);
std::vector<std::string> GetAllFileSetNames() const;
std::vector<std::string> GetAllPrivateFileSets() const;
std::vector<std::string> GetAllInterfaceFileSets() const;
std::vector<std::string> GetAllFileSetNames(
cm::FileSetMetadata::FileSetDomainSet domains = {
cm::FileSetMetadata::FileSetDomain::NATIVE }) const;
std::vector<std::string> GetAllPrivateFileSets(
cm::FileSetMetadata::FileSetDomainSet domains = {
cm::FileSetMetadata::FileSetDomain::NATIVE }) const;
std::vector<std::string> 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;
+32 -24
View File
@@ -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;
}
}
+3
View File
@@ -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)
@@ -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\)
@@ -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\)
+3
View File
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 4.4)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.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 "<SOURCE>" "<INPUT>" 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=$<PATH:NATIVE_PATH,<SOURCE>>;OUTPUT_DIR=<CURRENT_BINARY_DIR>/${target};CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$<PATH:NATIVE_PATH,<OUTPUT_DIR>/<BASE_NAME>.c>;INCLUDES=;DEFINES=${CMAKE_C_DEFINE_FLAG}RULE_PATTERN=1;FLAGS=" PARENT_SCOPE)
endfunction()
add_custom_rule(preprocess1 OUTPUT <OUTPUT_DIR>/<BASE_NAME>.c
COMMAND "${CMAKE_COMMAND}" -E make_directory "<OUTPUT_DIR>"
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=$<PATH:NATIVE_PATH,<SOURCE>>;OUTPUT_DIR=<CURRENT_BINARY_DIR>/${target};CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$<PATH:NATIVE_PATH,<OUTPUT_DIR>/<BASE_NAME>.c>;INCLUDES=;DEFINES=${CMAKE_C_DEFINE_FLAG}RULE_PATTERN=1;FLAGS=" PARENT_SCOPE)
endfunction()
add_custom_rule(preprocess2 OUTPUT <OUTPUT_DIR>/<BASE_NAME>.c
COMMAND "${CMAKE_COMMAND}" -E make_directory "<OUTPUT_DIR>"
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=$<PATH:NATIVE_PATH,<SOURCE>>;OUTPUT_DIR=<CURRENT_BINARY_DIR>/${target};CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$<PATH:NATIVE_PATH,<OUTPUT_DIR>/<BASE_NAME>.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 <OUTPUT_DIR>/<BASE_NAME>.c
COMMAND "${CMAKE_COMMAND}" -E make_directory "<OUTPUT_DIR>"
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)
@@ -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()
@@ -0,0 +1,9 @@
enable_language(C)
add_custom_rule(simple OUTPUT <CURRENT_BINARY_DIR>/<FILE_NAME>
COMMAND "${CMAKE_COMMAND}" -E copy <SOURCE> <CURRENT_BINARY_DIR>)
add_custom_target(foo ALL)
target_sources(foo PRIVATE FILE_SET fs TYPE simple FILES file.i)
+104
View File
@@ -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 "<SOURCE>" "<INPUT>" create_preprocessed_source "${create_preprocessed_source}")
separate_arguments(create_preprocessed_source UNIX_COMMAND "${create_preprocessed_source}")
add_custom_rule(preprocess1 OUTPUT <OUTPUT_DIR>/<BASE_NAME>.c
COMMAND "${CMAKE_COMMAND}" -E make_directory "<OUTPUT_DIR>"
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=$<PATH:NATIVE_PATH,<SOURCE>>;OUTPUT_DIR=<CURRENT_BINARY_DIR>/<TARGET>;CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$<PATH:NATIVE_PATH,<OUTPUT_DIR>/<BASE_NAME>.c>;INCLUDES=;DEFINES=$<LIST:TRANSFORM,<COMPILE_DEFINITIONS>,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=$<PATH:NATIVE_PATH,<SOURCE>>;OUTPUT_DIR=<CURRENT_BINARY_DIR>/<TARGET>;CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$<PATH:NATIVE_PATH,<OUTPUT_DIR>/<BASE_NAME>.c>;INCLUDES=;DEFINES=$<LIST:TRANSFORM,<COMPILE_DEFINITIONS>,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 <OUTPUT_DIR>/<BASE_NAME>.c
COMMAND "${CMAKE_COMMAND}" -E make_directory "<OUTPUT_DIR>"
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=$<PATH:NATIVE_PATH,<SOURCE>>;OUTPUT_DIR=<CURRENT_BINARY_DIR>/<TARGET>;CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$<PATH:NATIVE_PATH,<OUTPUT_DIR>/<BASE_NAME>.c>;INCLUDES=;DEFINES=$<LIST:TRANSFORM,<COMPILE_DEFINITIONS>,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 <OUTPUT_DIR>/<BASE_NAME>.c
COMMAND "${CMAKE_COMMAND}" -E make_directory "<OUTPUT_DIR>"
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=$<PATH:NATIVE_PATH,<SOURCE>>;OUTPUT_DIR=<CURRENT_BINARY_DIR>/<TARGET>;CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$<PATH:NATIVE_PATH,<OUTPUT_DIR>/<BASE_NAME>.c>;INCLUDES=;DEFINES=$<LIST:TRANSFORM,<COMPILE_DEFINITIONS>,PREPEND,${CMAKE_C_DEFINE_FLAG}>;FLAGS=")
+8
View File
@@ -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)
+6
View File
@@ -0,0 +1,6 @@
enable_language(C)
add_library(foo STATIC)
add_subdirectory(subdir2)
@@ -0,0 +1 @@
1
@@ -0,0 +1,4 @@
CMake Error in CMakeLists\.txt:
Output File Set name, for rule "foo", did not expand correctly:
"bad_<PATTERN>_foo_bar_fs"
@@ -0,0 +1,8 @@
add_custom_rule(foo OUTPUT out1 COMMAND cmd arg1 arg2)
set_property(RULE foo PROPERTY OUTPUT_FILE_SET bad_<PATTERN>_<RULE>_<TARGET>_<FILE_SET> SOURCES)
add_library(bar)
target_sources(bar PRIVATE FILE_SET fs TYPE foo FILES file1.c)
@@ -0,0 +1 @@
1
@@ -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\)
@@ -0,0 +1,8 @@
add_custom_rule(foo OUTPUT out1 COMMAND cmd arg1 arg2)
set_property(RULE foo PROPERTY OUTPUT_FILE_SET <RULE>_<TARGET>_<FILE_SET> FOO)
add_library(bar)
target_sources(bar PRIVATE FILE_SET fs TYPE foo FILES file1.c)
@@ -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"\.
@@ -0,0 +1,10 @@
add_custom_rule(foo OUTPUT out1 COMMAND cmd arg1 arg2)
set_property(RULE foo PROPERTY OUTPUT_FILE_SET <RULE>_<TARGET>_<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)
+47
View File
@@ -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 "<SOURCE>" "<INPUT>" create_preprocessed_source "${create_preprocessed_source}")
separate_arguments(create_preprocessed_source UNIX_COMMAND "${create_preprocessed_source}")
add_custom_rule(preprocess OUTPUT <OUTPUT_DIR>/<BASE_NAME>.c
COMMAND "${CMAKE_COMMAND}" -E make_directory "<OUTPUT_DIR>"
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=$<PATH:NATIVE_PATH,<SOURCE>>;OUTPUT_DIR=<CURRENT_BINARY_DIR>/<TARGET>;CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$<PATH:NATIVE_PATH,<OUTPUT_DIR>/<BASE_NAME>.c>;INCLUDES=;DEFINES=${CMAKE_C_DEFINE_FLAG}RULE_PATTERN=1;FLAGS=")
# Use <RULE>_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=$<PATH:NATIVE_PATH,<SOURCE>>;OUTPUT_DIR=<CURRENT_BINARY_DIR>/<TARGET>;CMAKE_C_COMPILER=${CMAKE_C_COMPILER};PREPROCESSED_SOURCE=$<PATH:NATIVE_PATH,<OUTPUT_DIR>/<BASE_NAME>.c>;INCLUDES=;DEFINES=${CMAKE_C_DEFINE_FLAG}RULE_PATTERN=1;FLAGS=")
# <RULE>_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")
@@ -0,0 +1 @@
1
@@ -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\)
@@ -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)
@@ -0,0 +1 @@
1

Some files were not shown because too many files have changed in this diff Show More