mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
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:
@@ -723,3 +723,4 @@ See Also
|
||||
^^^^^^^^
|
||||
|
||||
* :command:`add_custom_target`
|
||||
* :command:`add_custom_rule`
|
||||
|
||||
@@ -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`
|
||||
@@ -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,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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
----------------------------------
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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,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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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``.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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)>`.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
NAME
|
||||
----
|
||||
|
||||
.. versionadded:: 4.5
|
||||
|
||||
Read-only property giving the name of the rule.
|
||||
@@ -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.
|
||||
@@ -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``.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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>`.
|
||||
@@ -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.
|
||||
@@ -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)>`.
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
add_custom_rule
|
||||
---------------
|
||||
|
||||
* The :command:`add_custom_rule` command was added to allow the definition of
|
||||
pattern rules.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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", "eNode },
|
||||
{ "RULE_PROPERTY", &rulePropertyNode },
|
||||
{ "SOURCE_EXISTS", &sourceExistsNode },
|
||||
{ "SOURCE_PROPERTY", &sourcePropertyNode },
|
||||
{ "FILE_SET_EXISTS", &fileSetExistsNode },
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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>>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
@@ -18,6 +18,7 @@ public:
|
||||
VARIABLE,
|
||||
CACHED_VARIABLE,
|
||||
INSTALL,
|
||||
FILE_SET
|
||||
FILE_SET,
|
||||
RULE
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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\)
|
||||
|
||||
@@ -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)
|
||||
@@ -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=")
|
||||
@@ -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)
|
||||
@@ -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 @@
|
||||
1
|
||||
@@ -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)
|
||||
@@ -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
Reference in New Issue
Block a user