Răsfoiți Sursa

cmTarget: add support for C++ module fileset types

C++ modules have two variants which are of importance to CMake:

  - `CXX_MODULES`: interface modules (those using `export module M;`,
    `export module M:part;`, or `module M:internal_part;`)
  - `CXX_MODULE_HEADER_UNITS`: importable header units

Creating C++ modules or partitions are *not* supported in any other
source listing. This is because the source files must be installed (so
their scope matters), but not part of usage requirements (what it means
for a module source to be injected into a consumer is not clear at this
moment). Due to the way `FILE_SET` works with scopes, they are a perfect
fit as long as `INTERFACE` is not allowed (which it is not).
Ben Boeckel 3 ani în urmă
părinte
comite
386465bf83
82 a modificat fișierele cu 1290 adăugiri și 74 ștergeri
  1. 62 15
      Help/command/target_sources.rst
  2. 0 5
      Help/dev/experimental.rst
  3. 12 0
      Help/manual/cmake-properties.7.rst
  4. 17 0
      Help/prop_tgt/CXX_MODULE_DIRS.rst
  5. 17 0
      Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst
  6. 17 0
      Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS.rst
  7. 19 0
      Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME.rst
  8. 18 0
      Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET.rst
  9. 18 0
      Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SETS.rst
  10. 19 0
      Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME.rst
  11. 18 0
      Help/prop_tgt/CXX_MODULE_SET.rst
  12. 16 0
      Help/prop_tgt/CXX_MODULE_SETS.rst
  13. 18 0
      Help/prop_tgt/CXX_MODULE_SET_NAME.rst
  14. 16 0
      Help/prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS.rst
  15. 16 0
      Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst
  16. 32 0
      Source/cmExportBuildFileGenerator.cxx
  17. 34 0
      Source/cmExportInstallFileGenerator.cxx
  18. 89 1
      Source/cmGeneratorTarget.cxx
  19. 30 0
      Source/cmGeneratorTarget.h
  20. 15 0
      Source/cmGlobalVisualStudio7Generator.cxx
  21. 2 0
      Source/cmGlobalVisualStudio7Generator.h
  22. 12 0
      Source/cmGlobalXCodeGenerator.cxx
  23. 65 0
      Source/cmMakefileTargetGenerator.cxx
  24. 45 47
      Source/cmNinjaTargetGenerator.cxx
  25. 79 0
      Source/cmTarget.cxx
  26. 4 0
      Source/cmTarget.h
  27. 26 4
      Source/cmTargetSourcesCommand.cxx
  28. 12 0
      Source/cmVisualStudio10TargetGenerator.cxx
  29. 3 0
      Tests/RunCMake/CMakeLists.txt
  30. 6 0
      Tests/RunCMake/CXXModules/CMakeLists.txt
  31. 1 0
      Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-result.txt
  32. 12 0
      Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-stderr.txt
  33. 8 0
      Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface.cmake
  34. 11 0
      Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate-stderr.txt
  35. 13 0
      Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate.cmake
  36. 11 0
      Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic-stderr.txt
  37. 13 0
      Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic.cmake
  38. 1 0
      Tests/RunCMake/CXXModules/FileSetModulesInterface-result.txt
  39. 12 0
      Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt
  40. 8 0
      Tests/RunCMake/CXXModules/FileSetModulesInterface.cmake
  41. 11 0
      Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt
  42. 12 0
      Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake
  43. 11 0
      Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt
  44. 12 0
      Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake
  45. 1 0
      Tests/RunCMake/CXXModules/NoCXX-result.txt
  46. 20 0
      Tests/RunCMake/CXXModules/NoCXX-stderr.txt
  47. 9 0
      Tests/RunCMake/CXXModules/NoCXX.cmake
  48. 1 0
      Tests/RunCMake/CXXModules/NoCXX20-result.txt
  49. 20 0
      Tests/RunCMake/CXXModules/NoCXX20-stderr.txt
  50. 10 0
      Tests/RunCMake/CXXModules/NoCXX20.cmake
  51. 1 0
      Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
  52. 20 0
      Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt
  53. 10 0
      Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake
  54. 1 0
      Tests/RunCMake/CXXModules/NoDyndepSupport-result.txt
  55. 30 0
      Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt
  56. 16 0
      Tests/RunCMake/CXXModules/NoDyndepSupport.cmake
  57. 1 0
      Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-result.txt
  58. 22 0
      Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-stderr.txt
  59. 15 0
      Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits.cmake
  60. 1 0
      Tests/RunCMake/CXXModules/NotCXXSourceModules-result.txt
  61. 17 0
      Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt
  62. 13 0
      Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake
  63. 67 0
      Tests/RunCMake/CXXModules/RunCMakeTest.cmake
  64. 25 0
      Tests/RunCMake/CXXModules/compiler_introspection.cmake
  65. 4 0
      Tests/RunCMake/CXXModules/sources/c-anchor.c
  66. 4 0
      Tests/RunCMake/CXXModules/sources/cxx-anchor.cxx
  67. 9 0
      Tests/RunCMake/CXXModules/sources/module-header.h
  68. 6 0
      Tests/RunCMake/CXXModules/sources/module-impl.cxx
  69. 11 0
      Tests/RunCMake/CXXModules/sources/module-internal-part-impl.cxx
  70. 3 0
      Tests/RunCMake/CXXModules/sources/module-internal-part.cxx
  71. 13 0
      Tests/RunCMake/CXXModules/sources/module-part-impl.cxx
  72. 3 0
      Tests/RunCMake/CXXModules/sources/module-part.cxx
  73. 6 0
      Tests/RunCMake/CXXModules/sources/module-use.cxx
  74. 5 0
      Tests/RunCMake/CXXModules/sources/module.cxx
  75. 1 0
      Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt
  76. 12 0
      Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt
  77. 6 0
      Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake
  78. 1 0
      Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt
  79. 12 0
      Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt
  80. 6 0
      Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake
  81. 2 0
      Tests/RunCMake/target_sources/RunCMakeTest.cmake
  82. 13 2
      Tests/RunCMake/try_compile/CxxStandard-stderr.txt

+ 62 - 15
Help/command/target_sources.rst

@@ -75,9 +75,33 @@ File Sets
 Adds a file set to a target, or adds files to an existing file set. Targets
 Adds a file set to a target, or adds files to an existing file set. Targets
 have zero or more named file sets. Each file set has a name, a type, a scope of
 have zero or more named file sets. Each file set has a name, a type, a scope of
 ``INTERFACE``, ``PUBLIC``, or ``PRIVATE``, one or more base directories, and
 ``INTERFACE``, ``PUBLIC``, or ``PRIVATE``, one or more base directories, and
-files within those directories. The only acceptable type is ``HEADERS``. The
-optional default file sets are named after their type. The target may not be a
-custom target or :prop_tgt:`FRAMEWORK` target.
+files within those directories. The acceptable types include:
+
+``HEADERS``
+
+  Sources intended to be used via a language's ``#include`` mechanism.
+
+``CXX_MODULES``
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+  Sources which contain C++ interface module or partition units (i.e., those
+  using the ``export`` keyword). This file set type may not have an
+  ``INTERFACE`` scope.
+
+``CXX_MODULE_HEADER_UNITS``
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+  C++ header sources which may be imported by other C++ source code. This file
+  set type may not have an ``INTERFACE`` scope.
+
+The optional default file sets are named after their type. The target may not
+be a custom target or :prop_tgt:`FRAMEWORK` target.
 
 
 Files in a ``PRIVATE`` or ``PUBLIC`` file set are marked as source files for
 Files in a ``PRIVATE`` or ``PUBLIC`` file set are marked as source files for
 the purposes of IDE integration. Additionally, files in ``HEADERS`` file sets
 the purposes of IDE integration. Additionally, files in ``HEADERS`` file sets
@@ -93,16 +117,17 @@ Each ``target_sources(FILE_SET)`` entry starts with ``INTERFACE``, ``PUBLIC``, o
 
 
   The name of the file set to create or add to. It must contain only letters,
   The name of the file set to create or add to. It must contain only letters,
   numbers and underscores. Names starting with a capital letter are reserved
   numbers and underscores. Names starting with a capital letter are reserved
-  for built-in file sets predefined by CMake. The only predefined set name is
-  ``HEADERS``. All other set names must not start with a capital letter or
+  for built-in file sets predefined by CMake. The only predefined set names
+  are those matching the acceptable types. All other set names must not start
+  with a capital letter or
   underscore.
   underscore.
 
 
 ``TYPE <type>``
 ``TYPE <type>``
 
 
-  Every file set is associated with a particular type of file. ``HEADERS``
-  is currently the only defined type and it is an error to specify anything
-  else. As a special case, if the name of the file set is ``HEADERS``, the
-  type does not need to be specified and the ``TYPE <type>`` arguments can be
+  Every file set is associated with a particular type of file. Only types
+  specified above may be used and it is an error to specify anything else. As
+  a special case, if the name of the file set is one of the types, the type
+  does not need to be specified and the ``TYPE <type>`` arguments can be
   omitted. For all other file set names, ``TYPE`` is required.
   omitted. For all other file set names, ``TYPE`` is required.
 
 
 ``BASE_DIRS <dirs>...``
 ``BASE_DIRS <dirs>...``
@@ -134,6 +159,8 @@ Each ``target_sources(FILE_SET)`` entry starts with ``INTERFACE``, ``PUBLIC``, o
 The following target properties are set by ``target_sources(FILE_SET)``,
 The following target properties are set by ``target_sources(FILE_SET)``,
 but they should not generally be manipulated directly:
 but they should not generally be manipulated directly:
 
 
+For file sets of type ``HEADERS``:
+
 * :prop_tgt:`HEADER_SETS`
 * :prop_tgt:`HEADER_SETS`
 * :prop_tgt:`INTERFACE_HEADER_SETS`
 * :prop_tgt:`INTERFACE_HEADER_SETS`
 * :prop_tgt:`HEADER_SET`
 * :prop_tgt:`HEADER_SET`
@@ -141,17 +168,37 @@ but they should not generally be manipulated directly:
 * :prop_tgt:`HEADER_DIRS`
 * :prop_tgt:`HEADER_DIRS`
 * :prop_tgt:`HEADER_DIRS_<NAME>`
 * :prop_tgt:`HEADER_DIRS_<NAME>`
 
 
+For file sets of type ``CXX_MODULES``:
+
+* :prop_tgt:`CXX_MODULE_SETS`
+* :prop_tgt:`INTERFACE_CXX_MODULE_SETS`
+* :prop_tgt:`CXX_MODULE_SET`
+* :prop_tgt:`CXX_MODULE_SET_<NAME>`
+* :prop_tgt:`CXX_MODULE_DIRS`
+* :prop_tgt:`CXX_MODULE_DIRS_<NAME>`
+
+For file sets of type ``CXX_MODULE_HEADER_UNITS``:
+
+* :prop_tgt:`CXX_MODULE_HEADER_UNIT_SETS`
+* :prop_tgt:`INTERFACE_CXX_MODULE_HEADER_UNIT_SETS`
+* :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET`
+* :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET_<NAME>`
+* :prop_tgt:`CXX_MODULE_HEADER_UNIT_DIRS`
+* :prop_tgt:`CXX_MODULE_HEADER_UNIT_DIRS_<NAME>`
+
 Target properties related to include directories are also modified by
 Target properties related to include directories are also modified by
 ``target_sources(FILE_SET)`` as follows:
 ``target_sources(FILE_SET)`` as follows:
 
 
 :prop_tgt:`INCLUDE_DIRECTORIES`
 :prop_tgt:`INCLUDE_DIRECTORIES`
 
 
-  If the ``TYPE`` is ``HEADERS``, and the scope of the file set is ``PRIVATE``
-  or ``PUBLIC``, all of the ``BASE_DIRS`` of the file set are wrapped in
-  :genex:`$<BUILD_INTERFACE>` and appended to this property.
+  If the ``TYPE`` is ``HEADERS`` or ``CXX_MODULE_HEADER_UNITS``, and the scope
+  of the file set is ``PRIVATE`` or ``PUBLIC``, all of the ``BASE_DIRS`` of
+  the file set are wrapped in :genex:`$<BUILD_INTERFACE>` and appended to this
+  property.
 
 
 :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES`
 :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES`
 
 
-  If the ``TYPE`` is ``HEADERS``, and the scope of the file set is
-  ``INTERFACE`` or ``PUBLIC``, all of the ``BASE_DIRS`` of the file set are
-  wrapped in :genex:`$<BUILD_INTERFACE>` and appended to this property.
+  If the ``TYPE`` is ``HEADERS`` or ``CXX_MODULE_HEADER_UNITS``, and the scope
+  of the file set is ``INTERFACE`` or ``PUBLIC``, all of the ``BASE_DIRS`` of
+  the file set are wrapped in :genex:`$<BUILD_INTERFACE>` and appended to this
+  property.

+ 0 - 5
Help/dev/experimental.rst

@@ -57,11 +57,6 @@ dependencies to the file specified by the ``<DYNDEP_FILE>`` placeholder. The
 ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_DEPFILE_FORMAT`` file may be set to ``msvc``
 ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_DEPFILE_FORMAT`` file may be set to ``msvc``
 for scandep rules which use ``msvc``-style dependency reporting.
 for scandep rules which use ``msvc``-style dependency reporting.
 
 
-For tools which need to know the file set the source belongs to, the
-``CMAKE_EXPERIMENTAL_CXX_MODULE_SOURCE_TYPE_FLAG_<FILE_SET_TYPE>`` flag may
-be provided so that different source types can be distinguished prior to
-scanning.
-
 The module dependencies should be written in the format described
 The module dependencies should be written in the format described
 by the `P1689r4`_ paper.
 by the `P1689r4`_ paper.
 
 

+ 12 - 0
Help/manual/cmake-properties.7.rst

@@ -184,6 +184,16 @@ Properties on Targets
    /prop_tgt/CUDA_STANDARD
    /prop_tgt/CUDA_STANDARD
    /prop_tgt/CUDA_STANDARD_REQUIRED
    /prop_tgt/CUDA_STANDARD_REQUIRED
    /prop_tgt/CXX_EXTENSIONS
    /prop_tgt/CXX_EXTENSIONS
+   /prop_tgt/CXX_MODULE_DIRS
+   /prop_tgt/CXX_MODULE_DIRS_NAME
+   /prop_tgt/CXX_MODULE_SET
+   /prop_tgt/CXX_MODULE_SET_NAME
+   /prop_tgt/CXX_MODULE_SETS
+   /prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS
+   /prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME
+   /prop_tgt/CXX_MODULE_HEADER_UNIT_SET
+   /prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME
+   /prop_tgt/CXX_MODULE_HEADER_UNIT_SETS
    /prop_tgt/CXX_STANDARD
    /prop_tgt/CXX_STANDARD
    /prop_tgt/CXX_STANDARD_REQUIRED
    /prop_tgt/CXX_STANDARD_REQUIRED
    /prop_tgt/DEBUG_POSTFIX
    /prop_tgt/DEBUG_POSTFIX
@@ -262,6 +272,8 @@ Properties on Targets
    /prop_tgt/INTERFACE_COMPILE_DEFINITIONS
    /prop_tgt/INTERFACE_COMPILE_DEFINITIONS
    /prop_tgt/INTERFACE_COMPILE_FEATURES
    /prop_tgt/INTERFACE_COMPILE_FEATURES
    /prop_tgt/INTERFACE_COMPILE_OPTIONS
    /prop_tgt/INTERFACE_COMPILE_OPTIONS
+   /prop_tgt/INTERFACE_CXX_MODULE_SETS
+   /prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS
    /prop_tgt/INTERFACE_HEADER_SETS
    /prop_tgt/INTERFACE_HEADER_SETS
    /prop_tgt/INTERFACE_HEADER_SETS_TO_VERIFY
    /prop_tgt/INTERFACE_HEADER_SETS_TO_VERIFY
    /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES
    /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES

+ 17 - 0
Help/prop_tgt/CXX_MODULE_DIRS.rst

@@ -0,0 +1,17 @@
+CXX_MODULE_DIRS
+---------------
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of base directories of the target's default
+C++ module set (i.e. the file set with name and type ``CXX_MODULES``). The
+property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_DIRS_<NAME>` for the list of base directories in
+other C++ module sets.

+ 17 - 0
Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst

@@ -0,0 +1,17 @@
+CXX_MODULE_DIRS_<NAME>
+----------------------
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of base directories of the target's ``<NAME>`` C++
+module set, which has the set type ``CXX_MODULES``. The property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_DIRS` for the list of base directories in the
+default C++ module set. See :prop_tgt:`CXX_MODULE_SETS` for the file set names
+of all C++ module sets.

+ 17 - 0
Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS.rst

@@ -0,0 +1,17 @@
+CXX_MODULE_HEADER_UNIT_DIRS
+---------------------------
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of base directories of the target's default C++
+module header set (i.e. the file set with name and type
+``CXX_MODULE_HEADER_UNITS``). The property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_HEADER_UNIT_DIRS_<NAME>` for the list of base directories
+in other C++ module header sets.

+ 19 - 0
Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME.rst

@@ -0,0 +1,19 @@
+CXX_MODULE_HEADER_UNIT_DIRS_<NAME>
+----------------------------------
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of base directories of the target's ``<NAME>`` C++
+module header set, which has the set type ``CXX_MODULE_HEADER_UNITS``. The
+property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_HEADER_UNIT_DIRS` for the list of base directories
+in the default C++ module header set. See
+:prop_tgt:`CXX_MODULE_HEADER_UNIT_SETS` for the file set names of all C++
+module header sets.

+ 18 - 0
Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET.rst

@@ -0,0 +1,18 @@
+CXX_MODULE_HEADER_UNIT_SET
+--------------------------
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of files in the target's default C++ module header
+set, (i.e. the file set with name and type ``CXX_MODULE_HEADER_UNITS``). If
+any of the paths are relative, they are computed relative to the target's
+source directory. The property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET_<NAME>` for the list of files in
+other C++ module header sets.

+ 18 - 0
Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SETS.rst

@@ -0,0 +1,18 @@
+CXX_MODULE_HEADER_UNIT_SETS
+---------------------------
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Read-only list of the target's ``PRIVATE`` and ``PUBLIC`` C++ module header
+sets (i.e. all file sets with the type ``CXX_MODULE_HEADER_UNITS``). Files
+listed in these file sets are treated as source files for the purpose of IDE
+integration.
+
+C++ module header sets may be defined using the :command:`target_sources`
+command ``FILE_SET`` option with type ``CXX_MODULE_HEADER_UNITS``.
+
+See also :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET_<NAME>`,
+:prop_tgt:`CXX_MODULE_HEADER_UNIT_SET` and
+:prop_tgt:`INTERFACE_CXX_MODULE_HEADER_UNIT_SETS`.

+ 19 - 0
Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME.rst

@@ -0,0 +1,19 @@
+CXX_MODULE_HEADER_UNIT_SET_<NAME>
+---------------------------------
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of files in the target's ``<NAME>`` C++ module header
+set, which has the set type ``CXX_MODULE_HEADER_UNITS``. If any of the paths
+are relative, they are computed relative to the target's source directory. The
+property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET` for the list of files in the
+default C++ module header set. See :prop_tgt:`CXX_MODULE_HEADER_UNIT_SETS` for
+the file set names of all C++ module header sets.

+ 18 - 0
Help/prop_tgt/CXX_MODULE_SET.rst

@@ -0,0 +1,18 @@
+CXX_MODULE_SET
+--------------
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of files in the target's default C++ module set,
+(i.e. the file set with name and type ``CXX_MODULES``). If any of the paths
+are relative, they are computed relative to the target's source directory. The
+property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_SET_<NAME>` for the list of files in other C++
+module sets.

+ 16 - 0
Help/prop_tgt/CXX_MODULE_SETS.rst

@@ -0,0 +1,16 @@
+CXX_MODULE_SETS
+---------------
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Read-only list of the target's ``PRIVATE`` and ``PUBLIC`` C++ module sets (i.e.
+all file sets with the type ``CXX_MODULES``). Files listed in these file sets are
+treated as source files for the purpose of IDE integration.
+
+C++ module sets may be defined using the :command:`target_sources` command
+``FILE_SET`` option with type ``CXX_MODULES``.
+
+See also :prop_tgt:`CXX_MODULE_SET_<NAME>`, :prop_tgt:`CXX_MODULE_SET` and
+:prop_tgt:`INTERFACE_CXX_MODULE_SETS`.

+ 18 - 0
Help/prop_tgt/CXX_MODULE_SET_NAME.rst

@@ -0,0 +1,18 @@
+CXX_MODULE_SET_<NAME>
+---------------------
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of files in the target's ``<NAME>`` C++ module set,
+which has the set type ``CXX_MODULES``. If any of the paths are relative, they
+are computed relative to the target's source directory. The property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_SET` for the list of files in the default C++ module
+set. See :prop_tgt:`CXX_MODULE_SETS` for the file set names of all C++ module
+sets.

+ 16 - 0
Help/prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS.rst

@@ -0,0 +1,16 @@
+INTERFACE_CXX_MODULE_HEADER_UNIT_SETS
+-------------------------------------
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Read-only list of the target's ``PUBLIC`` C++ module header sets (i.e. all
+file sets with the type ``CXX_MODULE_HEADER_UNITS``). Files listed in these
+C++ module header sets can be installed with :command:`install(TARGETS)` and
+exported with :command:`install(EXPORT)` and :command:`export`.
+
+C++ module header sets may be defined using the :command:`target_sources`
+command ``FILE_SET`` option with type ``CXX_MODULE_HEADER_UNITS``.
+
+See also :prop_tgt:`CXX_MODULE_HEADER_UNIT_SETS`.

+ 16 - 0
Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst

@@ -0,0 +1,16 @@
+INTERFACE_CXX_MODULE_SETS
+-------------------------
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Read-only list of the target's ``PUBLIC`` C++ module sets (i.e. all file sets
+with the type ``CXX_MODULES``). Files listed in these C++ module sets can be
+installed with :command:`install(TARGETS)` and exported with
+:command:`install(EXPORT)` and :command:`export`.
+
+C++ module sets may be defined using the :command:`target_sources` command
+``FILE_SET`` option with type ``CXX_MODULES``.
+
+See also :prop_tgt:`CXX_MODULE_SETS`.

+ 32 - 0
Source/cmExportBuildFileGenerator.cxx

@@ -9,7 +9,9 @@
 #include <sstream>
 #include <sstream>
 #include <utility>
 #include <utility>
 
 
+#include <cm/string_view>
 #include <cmext/algorithm>
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 
 #include "cmExportSet.h"
 #include "cmExportSet.h"
 #include "cmFileSet.h"
 #include "cmFileSet.h"
@@ -382,6 +384,21 @@ std::string cmExportBuildFileGenerator::GetFileSetDirectories(
       std::any_of(directoryEntries.begin(), directoryEntries.end(),
       std::any_of(directoryEntries.begin(), directoryEntries.end(),
                   EntryIsContextSensitive);
                   EntryIsContextSensitive);
 
 
+    auto const& type = fileSet->GetType();
+    // C++ modules do not support interface file sets which are dependent upon
+    // the configuration.
+    if (contextSensitive &&
+        (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) {
+      auto* mf = this->LG->GetMakefile();
+      std::ostringstream e;
+      e << "The \"" << gte->GetName() << "\" target's interface file set \""
+        << fileSet->GetName() << "\" of type \"" << type
+        << "\" contains context-sensitive base directory entries which is not "
+           "supported.";
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return std::string{};
+    }
+
     for (auto const& directory : directories) {
     for (auto const& directory : directories) {
       auto dest = cmOutputConverter::EscapeForCMake(
       auto dest = cmOutputConverter::EscapeForCMake(
         directory, cmOutputConverter::WrapQuotes::NoWrap);
         directory, cmOutputConverter::WrapQuotes::NoWrap);
@@ -427,6 +444,21 @@ std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte,
       std::any_of(fileEntries.begin(), fileEntries.end(),
       std::any_of(fileEntries.begin(), fileEntries.end(),
                   EntryIsContextSensitive);
                   EntryIsContextSensitive);
 
 
+    auto const& type = fileSet->GetType();
+    // C++ modules do not support interface file sets which are dependent upon
+    // the configuration.
+    if (contextSensitive &&
+        (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) {
+      auto* mf = this->LG->GetMakefile();
+      std::ostringstream e;
+      e << "The \"" << gte->GetName() << "\" target's interface file set \""
+        << fileSet->GetName() << "\" of type \"" << type
+        << "\" contains context-sensitive file entries which is not "
+           "supported.";
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return std::string{};
+    }
+
     for (auto const& it : files) {
     for (auto const& it : files) {
       for (auto const& filename : it.second) {
       for (auto const& filename : it.second) {
         auto escapedFile = cmOutputConverter::EscapeForCMake(
         auto escapedFile = cmOutputConverter::EscapeForCMake(

+ 34 - 0
Source/cmExportInstallFileGenerator.cxx

@@ -7,6 +7,9 @@
 #include <sstream>
 #include <sstream>
 #include <utility>
 #include <utility>
 
 
+#include <cm/string_view>
+#include <cmext/string_view>
+
 #include "cmExportSet.h"
 #include "cmExportSet.h"
 #include "cmFileSet.h"
 #include "cmFileSet.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratedFileStream.h"
@@ -18,6 +21,7 @@
 #include "cmInstallTargetGenerator.h"
 #include "cmInstallTargetGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMakefile.h"
+#include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
 #include "cmStateTypes.h"
@@ -562,6 +566,21 @@ std::string cmExportInstallFileGenerator::GetFileSetDirectories(
                            cge->Evaluate(gte->LocalGenerator, config, gte),
                            cge->Evaluate(gte->LocalGenerator, config, gte),
                            cmOutputConverter::WrapQuotes::NoWrap));
                            cmOutputConverter::WrapQuotes::NoWrap));
 
 
+    auto const& type = fileSet->GetType();
+    // C++ modules do not support interface file sets which are dependent upon
+    // the configuration.
+    if (cge->GetHadContextSensitiveCondition() &&
+        (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) {
+      auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
+      std::ostringstream e;
+      e << "The \"" << gte->GetName() << "\" target's interface file set \""
+        << fileSet->GetName() << "\" of type \"" << type
+        << "\" contains context-sensitive base file entries which is not "
+           "supported.";
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return std::string{};
+    }
+
     if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) {
     if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) {
       resultVector.push_back(
       resultVector.push_back(
         cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
         cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
@@ -610,6 +629,21 @@ std::string cmExportInstallFileGenerator::GetFileSetFiles(
       std::any_of(fileEntries.begin(), fileEntries.end(),
       std::any_of(fileEntries.begin(), fileEntries.end(),
                   EntryIsContextSensitive);
                   EntryIsContextSensitive);
 
 
+    auto const& type = fileSet->GetType();
+    // C++ modules do not support interface file sets which are dependent upon
+    // the configuration.
+    if (contextSensitive &&
+        (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) {
+      auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
+      std::ostringstream e;
+      e << "The \"" << gte->GetName() << "\" target's interface file set \""
+        << fileSet->GetName() << "\" of type \"" << type
+        << "\" contains context-sensitive base file entries which is not "
+           "supported.";
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return std::string{};
+    }
+
     for (auto const& it : files) {
     for (auto const& it : files) {
       auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/');
       auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/');
       for (auto const& filename : it.second) {
       for (auto const& filename : it.second) {

+ 89 - 1
Source/cmGeneratorTarget.cxx

@@ -1707,7 +1707,8 @@ void addFileSetEntry(cmGeneratorTarget const* headTarget,
         }
         }
       }
       }
       if (!found) {
       if (!found) {
-        if (fileSet->GetType() == "HEADERS"_s) {
+        if (fileSet->GetType() == "HEADERS"_s ||
+            fileSet->GetType() == "CXX_MODULE_HEADER_UNITS"_s) {
           headTarget->Makefile->GetOrCreateSourceGroup("Header Files")
           headTarget->Makefile->GetOrCreateSourceGroup("Header Files")
             ->AddGroupFile(path);
             ->AddGroupFile(path);
         }
         }
@@ -1728,6 +1729,20 @@ void AddFileSetEntries(cmGeneratorTarget const* headTarget,
       addFileSetEntry(headTarget, config, dagChecker, headerSet, entries);
       addFileSetEntry(headTarget, config, dagChecker, headerSet, entries);
     }
     }
   }
   }
+  for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) {
+    for (auto const& name : cmExpandedList(entry.Value)) {
+      auto const* cxxModuleSet = headTarget->Target->GetFileSet(name);
+      addFileSetEntry(headTarget, config, dagChecker, cxxModuleSet, entries);
+    }
+  }
+  for (auto const& entry :
+       headTarget->Target->GetCxxModuleHeaderSetsEntries()) {
+    for (auto const& name : cmExpandedList(entry.Value)) {
+      auto const* cxxModuleHeaderSet = headTarget->Target->GetFileSet(name);
+      addFileSetEntry(headTarget, config, dagChecker, cxxModuleHeaderSet,
+                      entries);
+    }
+  }
 }
 }
 
 
 bool processSources(cmGeneratorTarget const* tgt,
 bool processSources(cmGeneratorTarget const* tgt,
@@ -8700,3 +8715,76 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
 
 
   return filename;
   return filename;
 }
 }
+
+bool cmGeneratorTarget::HaveCxx20ModuleSources() const
+{
+  auto const& fs_names = this->Target->GetAllFileSetNames();
+  return std::any_of(fs_names.begin(), fs_names.end(),
+                     [this](std::string const& name) -> bool {
+                       auto const* file_set = this->Target->GetFileSet(name);
+                       if (!file_set) {
+                         this->Makefile->IssueMessage(
+                           MessageType::INTERNAL_ERROR,
+                           cmStrCat("Target \"", this->Target->GetName(),
+                                    "\" is tracked to have file set \"", name,
+                                    "\", but it was not found."));
+                         return false;
+                       }
+
+                       auto const& fs_type = file_set->GetType();
+                       return fs_type == "CXX_MODULES"_s ||
+                         fs_type == "CXX_MODULE_HEADER_UNITS"_s;
+                     });
+}
+
+cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport(
+  std::string const& config) const
+{
+  auto const* state = this->Makefile->GetState();
+  if (!state->GetLanguageEnabled("CXX")) {
+    return Cxx20SupportLevel::MissingCxx;
+  }
+  cmStandardLevelResolver standardResolver(this->Makefile);
+  if (!standardResolver.HaveStandardAvailable(this, "CXX", config,
+                                              "cxx_std_20")) {
+    return Cxx20SupportLevel::NoCxx20;
+  }
+  if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) {
+    return Cxx20SupportLevel::MissingExperimentalFlag;
+  }
+  return Cxx20SupportLevel::Supported;
+}
+
+void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const
+{
+  // Check for `CXX_MODULE*` file sets and a lack of support.
+  if (this->HaveCxx20ModuleSources()) {
+    switch (this->HaveCxxModuleSupport(config)) {
+      case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx:
+        this->Makefile->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("The \"", this->GetName(),
+                   "\" target has C++ module sources but the \"CXX\" language "
+                   "has not been enabled"));
+        break;
+      case cmGeneratorTarget::Cxx20SupportLevel::MissingExperimentalFlag:
+        this->Makefile->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("The \"", this->GetName(),
+                   "\" target has C++ module sources but its experimental "
+                   "support has not been requested"));
+        break;
+      case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20:
+        this->Makefile->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat(
+            "The \"", this->GetName(),
+            "\" target has C++ module sources but is not using at least "
+            "\"cxx_std_20\""));
+        break;
+      case cmGeneratorTarget::Cxx20SupportLevel::Supported:
+        // All is well.
+        break;
+    }
+  }
+}

+ 30 - 0
Source/cmGeneratorTarget.h

@@ -1196,4 +1196,34 @@ public:
     bool operator()(cmGeneratorTarget const* t1,
     bool operator()(cmGeneratorTarget const* t1,
                     cmGeneratorTarget const* t2) const;
                     cmGeneratorTarget const* t2) const;
   };
   };
+
+  // C++20 module support queries.
+
+  /**
+   * Query whether the target expects C++20 module support.
+   *
+   * This will inspect the target itself to see if C++20 module
+   * support is expected to work based on its sources.
+   */
+  bool HaveCxx20ModuleSources() const;
+
+  enum class Cxx20SupportLevel
+  {
+    // C++ is not available.
+    MissingCxx,
+    // The experimental feature is not available.
+    MissingExperimentalFlag,
+    // The target does not require at least C++20.
+    NoCxx20,
+    // C++20 modules are available and working.
+    Supported,
+  };
+  /**
+   * Query whether the target has C++20 module support available (regardless of
+   * whether it is required or not).
+   */
+  Cxx20SupportLevel HaveCxxModuleSupport(std::string const& config) const;
+
+  // Check C++ module status for the target.
+  void CheckCxxModuleStatus(std::string const& config) const;
 };
 };

+ 15 - 0
Source/cmGlobalVisualStudio7Generator.cxx

@@ -395,12 +395,27 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution(
 {
 {
   VisualStudioFolders.clear();
   VisualStudioFolders.clear();
 
 
+  std::vector<std::string> configs =
+    root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
+
   for (cmGeneratorTarget const* target : projectTargets) {
   for (cmGeneratorTarget const* target : projectTargets) {
     if (!this->IsInSolution(target)) {
     if (!this->IsInSolution(target)) {
       continue;
       continue;
     }
     }
     bool written = false;
     bool written = false;
 
 
+    for (auto const& c : configs) {
+      target->CheckCxxModuleStatus(c);
+    }
+
+    if (target->HaveCxx20ModuleSources() && !this->SupportsCxxModuleDyndep()) {
+      root->GetMakefile()->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("The \"", target->GetName(),
+                 "\" target contains C++ module sources which are not "
+                 "supported by the generator"));
+    }
+
     // handle external vc project files
     // handle external vc project files
     cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
     cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
     if (expath) {
     if (expath) {

+ 2 - 0
Source/cmGlobalVisualStudio7Generator.h

@@ -157,6 +157,8 @@ protected:
     cmValue typeGuid,
     cmValue typeGuid,
     const std::set<BT<std::pair<std::string, bool>>>& dependencies) = 0;
     const std::set<BT<std::pair<std::string, bool>>>& dependencies) = 0;
 
 
+  virtual bool SupportsCxxModuleDyndep() const { return false; }
+
   std::string ConvertToSolutionPath(const std::string& path);
   std::string ConvertToSolutionPath(const std::string& path);
 
 
   std::set<std::string> IsPartOfDefaultBuild(
   std::set<std::string> IsPartOfDefaultBuild(

+ 12 - 0
Source/cmGlobalXCodeGenerator.cxx

@@ -1376,6 +1376,18 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget(
     return true;
     return true;
   }
   }
 
 
+  for (std::string const& configName : this->CurrentConfigurationTypes) {
+    gtgt->CheckCxxModuleStatus(configName);
+  }
+
+  if (gtgt->HaveCxx20ModuleSources()) {
+    gtgt->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("The \"", gtgt->GetName(),
+               "\" target contains C++ module sources which are not "
+               "supported by the generator"));
+  }
+
   auto& gtgt_visited = this->CommandsVisited[gtgt];
   auto& gtgt_visited = this->CommandsVisited[gtgt];
   auto& deps = this->GetTargetDirectDepends(gtgt);
   auto& deps = this->GetTargetDirectDepends(gtgt);
   for (auto& d : deps) {
   for (auto& d : deps) {

+ 65 - 0
Source/cmMakefileTargetGenerator.cxx

@@ -21,6 +21,7 @@
 #include "cmComputeLinkInformation.h"
 #include "cmComputeLinkInformation.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
 #include "cmCustomCommandGenerator.h"
+#include "cmFileSet.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGeneratorTarget.h"
@@ -46,6 +47,7 @@
 #include "cmStateTypes.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
+#include "cmTarget.h"
 #include "cmValue.h"
 #include "cmValue.h"
 #include "cmake.h"
 #include "cmake.h"
 
 
@@ -190,6 +192,16 @@ void cmMakefileTargetGenerator::CreateRuleFile()
 
 
 void cmMakefileTargetGenerator::WriteTargetBuildRules()
 void cmMakefileTargetGenerator::WriteTargetBuildRules()
 {
 {
+  this->GeneratorTarget->CheckCxxModuleStatus(this->GetConfigName());
+
+  if (this->GeneratorTarget->HaveCxx20ModuleSources()) {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("The \"", this->GeneratorTarget->GetName(),
+               "\" target contains C++ module sources which are not supported "
+               "by the generator"));
+  }
+
   // -- Write the custom commands for this target
   // -- Write the custom commands for this target
 
 
   // Evaluates generator expressions and expands prop_value
   // Evaluates generator expressions and expands prop_value
@@ -302,6 +314,40 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
     }
     }
   }
   }
 
 
+  std::map<std::string, std::string> file_set_map;
+
+  auto const* tgt = this->GeneratorTarget->Target;
+  for (auto const& name : tgt->GetAllFileSetNames()) {
+    auto const* file_set = tgt->GetFileSet(name);
+    if (!file_set) {
+      this->Makefile->IssueMessage(
+        MessageType::INTERNAL_ERROR,
+        cmStrCat("Target \"", tgt->GetName(),
+                 "\" is tracked to have file set \"", name,
+                 "\", but it was not found."));
+      continue;
+    }
+
+    auto fileEntries = file_set->CompileFileEntries();
+    auto directoryEntries = file_set->CompileDirectoryEntries();
+    auto directories = file_set->EvaluateDirectoryEntries(
+      directoryEntries, this->LocalGenerator, this->GetConfigName(),
+      this->GeneratorTarget);
+
+    std::map<std::string, std::vector<std::string>> files;
+    for (auto const& entry : fileEntries) {
+      file_set->EvaluateFileEntry(directories, files, entry,
+                                  this->LocalGenerator, this->GetConfigName(),
+                                  this->GeneratorTarget);
+    }
+
+    for (auto const& it : files) {
+      for (auto const& filename : it.second) {
+        file_set_map[filename] = file_set->GetType();
+      }
+    }
+  }
+
   std::vector<cmSourceFile const*> objectSources;
   std::vector<cmSourceFile const*> objectSources;
   this->GeneratorTarget->GetObjectSources(objectSources,
   this->GeneratorTarget->GetObjectSources(objectSources,
                                           this->GetConfigName());
                                           this->GetConfigName());
@@ -314,6 +360,25 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
       this->WriteObjectRuleFiles(*sf);
       this->WriteObjectRuleFiles(*sf);
     }
     }
   }
   }
+
+  for (cmSourceFile const* sf : objectSources) {
+    auto const& path = sf->GetFullPath();
+    auto const it = file_set_map.find(path);
+    if (it != file_set_map.end()) {
+      auto const& file_set_type = it->second;
+      if (file_set_type == "CXX_MODULES"_s ||
+          file_set_type == "CXX_MODULE_HEADER_UNITS"_s) {
+        if (sf->GetLanguage() != "CXX"_s) {
+          this->Makefile->IssueMessage(
+            MessageType::FATAL_ERROR,
+            cmStrCat(
+              "Target \"", tgt->GetName(), "\" contains the source\n  ", path,
+              "\nin a file set of type \"", file_set_type,
+              R"(" but the source is not classified as a "CXX" source.)"));
+        }
+      }
+    }
+  }
 }
 }
 
 
 void cmMakefileTargetGenerator::WriteCommonCodeRules()
 void cmMakefileTargetGenerator::WriteCommonCodeRules()

+ 45 - 47
Source/cmNinjaTargetGenerator.cxx

@@ -36,7 +36,6 @@
 #include "cmRange.h"
 #include "cmRange.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
 #include "cmSourceFile.h"
-#include "cmStandardLevelResolver.h"
 #include "cmState.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
@@ -153,17 +152,12 @@ std::string cmNinjaTargetGenerator::LanguageDyndepRule(
 bool cmNinjaTargetGenerator::NeedCxxModuleSupport(
 bool cmNinjaTargetGenerator::NeedCxxModuleSupport(
   std::string const& lang, std::string const& config) const
   std::string const& lang, std::string const& config) const
 {
 {
-  if (lang != "CXX") {
+  if (lang != "CXX"_s) {
     return false;
     return false;
   }
   }
-  if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) {
-    return false;
-  }
-  cmGeneratorTarget const* tgt = this->GetGeneratorTarget();
-  cmStandardLevelResolver standardResolver(this->Makefile);
-  bool const uses_cxx20 =
-    standardResolver.HaveStandardAvailable(tgt, "CXX", config, "cxx_std_20");
-  return uses_cxx20 && this->GetGlobalGenerator()->CheckCxxModuleSupport();
+  return this->GetGeneratorTarget()->HaveCxxModuleSupport(config) ==
+    cmGeneratorTarget::Cxx20SupportLevel::Supported &&
+    this->GetGlobalGenerator()->CheckCxxModuleSupport();
 }
 }
 
 
 bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang,
 bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang,
@@ -255,51 +249,53 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
       flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS));
       flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS));
   }
   }
 
 
-  if (this->NeedCxxModuleSupport(language, config)) {
-    auto const& path = source->GetFullPath();
-    auto const* tgt = this->GeneratorTarget->Target;
+  auto const& path = source->GetFullPath();
+  auto const* tgt = this->GeneratorTarget->Target;
 
 
-    std::string file_set_type;
+  std::string file_set_type;
 
 
-    for (auto const& name : tgt->GetAllFileSetNames()) {
-      auto const* file_set = tgt->GetFileSet(name);
-      if (!file_set) {
-        this->GetMakefile()->IssueMessage(
-          MessageType::INTERNAL_ERROR,
-          cmStrCat("Target `", tgt->GetName(),
-                   "` is tracked to have file set `", name,
-                   "`, but it was not found."));
-        continue;
-      }
+  for (auto const& name : tgt->GetAllFileSetNames()) {
+    auto const* file_set = tgt->GetFileSet(name);
+    if (!file_set) {
+      this->GetMakefile()->IssueMessage(
+        MessageType::INTERNAL_ERROR,
+        cmStrCat("Target \"", tgt->GetName(),
+                 "\" is tracked to have file set \"", name,
+                 "\", but it was not found."));
+      continue;
+    }
 
 
-      auto fileEntries = file_set->CompileFileEntries();
-      auto directoryEntries = file_set->CompileDirectoryEntries();
-      auto directories = file_set->EvaluateDirectoryEntries(
-        directoryEntries, this->LocalGenerator, config, this->GeneratorTarget);
+    auto fileEntries = file_set->CompileFileEntries();
+    auto directoryEntries = file_set->CompileDirectoryEntries();
+    auto directories = file_set->EvaluateDirectoryEntries(
+      directoryEntries, this->LocalGenerator, config, this->GeneratorTarget);
 
 
-      std::map<std::string, std::vector<std::string>> files;
-      for (auto const& entry : fileEntries) {
-        file_set->EvaluateFileEntry(directories, files, entry,
-                                    this->LocalGenerator, config,
-                                    this->GeneratorTarget);
-      }
+    std::map<std::string, std::vector<std::string>> files;
+    for (auto const& entry : fileEntries) {
+      file_set->EvaluateFileEntry(directories, files, entry,
+                                  this->LocalGenerator, config,
+                                  this->GeneratorTarget);
+    }
 
 
-      for (auto const& it : files) {
-        for (auto const& filename : it.second) {
-          if (filename == path) {
-            file_set_type = file_set->GetType();
-            break;
-          }
+    for (auto const& it : files) {
+      for (auto const& filename : it.second) {
+        if (filename == path) {
+          file_set_type = file_set->GetType();
+          break;
         }
         }
       }
       }
+    }
 
 
-      if (!file_set_type.empty()) {
-        std::string source_type_var = cmStrCat(
-          "CMAKE_EXPERIMENTAL_CXX_MODULE_SOURCE_TYPE_FLAG_", file_set_type);
-        cmMakefile* mf = this->GetMakefile();
-        if (cmValue source_type_flag = mf->GetDefinition(source_type_var)) {
-          this->LocalGenerator->AppendFlags(flags, *source_type_flag);
-        }
+    if (file_set_type == "CXX_MODULES"_s ||
+        file_set_type == "CXX_MODULE_HEADER_UNITS"_s) {
+      if (source->GetLanguage() != "CXX"_s) {
+        this->GetMakefile()->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat(
+            "Target \"", tgt->GetName(), "\" contains the source\n  ", path,
+            "\nin a file set of type \"", file_set_type,
+            R"(" but the source is not classified as a "CXX" source.)"));
+        continue;
       }
       }
     }
     }
   }
   }
@@ -1038,6 +1034,8 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements(
   const std::string& config, const std::string& fileConfig,
   const std::string& config, const std::string& fileConfig,
   bool firstForConfig)
   bool firstForConfig)
 {
 {
+  this->GeneratorTarget->CheckCxxModuleStatus(config);
+
   // Write comments.
   // Write comments.
   cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig));
   cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig));
   this->GetImplFileStream(fileConfig)
   this->GetImplFileStream(fileConfig)

+ 79 - 0
Source/cmTarget.cxx

@@ -272,6 +272,8 @@ public:
   cmListFileBacktrace Backtrace;
   cmListFileBacktrace Backtrace;
 
 
   FileSetType HeadersFileSets;
   FileSetType HeadersFileSets;
+  FileSetType CxxModulesFileSets;
+  FileSetType CxxModuleHeadersFileSets;
 
 
   cmTargetInternals();
   cmTargetInternals();
 
 
@@ -301,6 +303,19 @@ cmTargetInternals::cmTargetInternals()
                     "The default header set"_s, "Header set"_s,
                     "The default header set"_s, "Header set"_s,
                     FileSetEntries("HEADER_SETS"_s),
                     FileSetEntries("HEADER_SETS"_s),
                     FileSetEntries("INTERFACE_HEADER_SETS"_s))
                     FileSetEntries("INTERFACE_HEADER_SETS"_s))
+  , CxxModulesFileSets("CXX_MODULES"_s, "CXX_MODULE_DIRS"_s,
+                       "CXX_MODULE_SET"_s, "CXX_MODULE_DIRS_"_s,
+                       "CXX_MODULE_SET_"_s, "C++ module"_s,
+                       "The default C++ module set"_s, "C++ module set"_s,
+                       FileSetEntries("CXX_MODULE_SETS"_s),
+                       FileSetEntries("INTERFACE_CXX_MODULE_SETS"_s))
+  , CxxModuleHeadersFileSets(
+      "CXX_MODULE_HEADER_UNITS"_s, "CXX_MODULE_HEADER_UNIT_DIRS"_s,
+      "CXX_MODULE_HEADER_UNIT_SET"_s, "CXX_MODULE_HEADER_UNIT_DIRS_"_s,
+      "CXX_MODULE_HEADER_UNIT_SET_"_s, "C++ module header"_s,
+      "The default C++ module header set"_s, "C++ module header set"_s,
+      FileSetEntries("CXX_MODULE_HEADER_UNIT_SETS"_s),
+      FileSetEntries("INTERFACE_CXX_MODULE_HEADER_UNIT_SETS"_s))
 {
 {
 }
 }
 
 
@@ -1366,11 +1381,32 @@ cmBTStringRange cmTarget::GetHeaderSetsEntries() const
   return cmMakeRange(this->impl->HeadersFileSets.SelfEntries.Entries);
   return cmMakeRange(this->impl->HeadersFileSets.SelfEntries.Entries);
 }
 }
 
 
+cmBTStringRange cmTarget::GetCxxModuleSetsEntries() const
+{
+  return cmMakeRange(this->impl->CxxModulesFileSets.SelfEntries.Entries);
+}
+
+cmBTStringRange cmTarget::GetCxxModuleHeaderSetsEntries() const
+{
+  return cmMakeRange(this->impl->CxxModuleHeadersFileSets.SelfEntries.Entries);
+}
+
 cmBTStringRange cmTarget::GetInterfaceHeaderSetsEntries() const
 cmBTStringRange cmTarget::GetInterfaceHeaderSetsEntries() const
 {
 {
   return cmMakeRange(this->impl->HeadersFileSets.InterfaceEntries.Entries);
   return cmMakeRange(this->impl->HeadersFileSets.InterfaceEntries.Entries);
 }
 }
 
 
+cmBTStringRange cmTarget::GetInterfaceCxxModuleSetsEntries() const
+{
+  return cmMakeRange(this->impl->CxxModulesFileSets.InterfaceEntries.Entries);
+}
+
+cmBTStringRange cmTarget::GetInterfaceCxxModuleHeaderSetsEntries() const
+{
+  return cmMakeRange(
+    this->impl->CxxModuleHeadersFileSets.InterfaceEntries.Entries);
+}
+
 namespace {
 namespace {
 #define MAKE_PROP(PROP) const std::string prop##PROP = #PROP
 #define MAKE_PROP(PROP) const std::string prop##PROP = #PROP
 MAKE_PROP(C_STANDARD);
 MAKE_PROP(C_STANDARD);
@@ -1630,6 +1666,12 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value)
   } else if (this->impl->HeadersFileSets.WriteProperties(
   } else if (this->impl->HeadersFileSets.WriteProperties(
                this, this->impl.get(), prop, value, true)) {
                this, this->impl.get(), prop, value, true)) {
     /* Handled in the `if` condition. */
     /* Handled in the `if` condition. */
+  } else if (this->impl->CxxModulesFileSets.WriteProperties(
+               this, this->impl.get(), prop, value, true)) {
+    /* Handled in the `if` condition. */
+  } else if (this->impl->CxxModuleHeadersFileSets.WriteProperties(
+               this, this->impl.get(), prop, value, true)) {
+    /* Handled in the `if` condition. */
   } else {
   } else {
     this->impl->Properties.SetProperty(prop, value);
     this->impl->Properties.SetProperty(prop, value);
   }
   }
@@ -1741,6 +1783,13 @@ void cmTarget::AppendProperty(const std::string& prop,
     this->impl->Makefile->IssueMessage(
     this->impl->Makefile->IssueMessage(
       MessageType::FATAL_ERROR, prop + " property may not be appended.");
       MessageType::FATAL_ERROR, prop + " property may not be appended.");
   } else if (this->impl->HeadersFileSets.WriteProperties(
   } else if (this->impl->HeadersFileSets.WriteProperties(
+               this, this->impl.get(), prop, value,
+               false)) { // NOLINT(bugprone-branch-clone)
+    /* Handled in the `if` condition. */
+  } else if (this->impl->CxxModulesFileSets.WriteProperties(
+               this, this->impl.get(), prop, value, false)) {
+    /* Handled in the `if` condition. */
+  } else if (this->impl->CxxModuleHeadersFileSets.WriteProperties(
                this, this->impl.get(), prop, value, false)) {
                this, this->impl.get(), prop, value, false)) {
     /* Handled in the `if` condition. */
     /* Handled in the `if` condition. */
   } else {
   } else {
@@ -2249,6 +2298,17 @@ cmValue cmTarget::GetProperty(const std::string& prop) const
     if (headers.first) {
     if (headers.first) {
       return headers.second;
       return headers.second;
     }
     }
+    auto cxx_modules = this->impl->CxxModulesFileSets.ReadProperties(
+      this, this->impl.get(), prop);
+    if (cxx_modules.first) {
+      return cxx_modules.second;
+    }
+    auto cxx_module_headers =
+      this->impl->CxxModuleHeadersFileSets.ReadProperties(
+        this, this->impl.get(), prop);
+    if (cxx_module_headers.first) {
+      return cxx_module_headers.second;
+    }
   }
   }
 
 
   cmValue retVal = this->impl->Properties.GetPropertyValue(prop);
   cmValue retVal = this->impl->Properties.GetPropertyValue(prop);
@@ -2526,6 +2586,11 @@ std::pair<cmFileSet*, bool> cmTarget::GetOrCreateFileSet(
     auto bt = this->impl->Makefile->GetBacktrace();
     auto bt = this->impl->Makefile->GetBacktrace();
     if (type == this->impl->HeadersFileSets.TypeName) {
     if (type == this->impl->HeadersFileSets.TypeName) {
       this->impl->HeadersFileSets.AddFileSet(name, vis, std::move(bt));
       this->impl->HeadersFileSets.AddFileSet(name, vis, std::move(bt));
+    } else if (type == this->impl->CxxModulesFileSets.TypeName) {
+      this->impl->CxxModulesFileSets.AddFileSet(name, vis, std::move(bt));
+    } else if (type == this->impl->CxxModuleHeadersFileSets.TypeName) {
+      this->impl->CxxModuleHeadersFileSets.AddFileSet(name, vis,
+                                                      std::move(bt));
     }
     }
   }
   }
   return std::make_pair(&result.first->second, result.second);
   return std::make_pair(&result.first->second, result.second);
@@ -2536,6 +2601,12 @@ std::string cmTarget::GetFileSetsPropertyName(const std::string& type)
   if (type == "HEADERS") {
   if (type == "HEADERS") {
     return "HEADER_SETS";
     return "HEADER_SETS";
   }
   }
+  if (type == "CXX_MODULES") {
+    return "CXX_MODULE_SETS";
+  }
+  if (type == "CXX_MODULE_HEADER_UNITS") {
+    return "CXX_MODULE_HEADER_UNIT_SETS";
+  }
   return "";
   return "";
 }
 }
 
 
@@ -2544,6 +2615,12 @@ std::string cmTarget::GetInterfaceFileSetsPropertyName(const std::string& type)
   if (type == "HEADERS") {
   if (type == "HEADERS") {
     return "INTERFACE_HEADER_SETS";
     return "INTERFACE_HEADER_SETS";
   }
   }
+  if (type == "CXX_MODULES") {
+    return "INTERFACE_CXX_MODULE_SETS";
+  }
+  if (type == "CXX_MODULE_HEADER_UNITS") {
+    return "INTERFACE_CXX_MODULE_HEADER_UNIT_SETS";
+  }
   return "";
   return "";
 }
 }
 
 
@@ -2571,6 +2648,8 @@ std::vector<std::string> cmTarget::GetAllInterfaceFileSets() const
   };
   };
 
 
   appendEntries(this->impl->HeadersFileSets.InterfaceEntries.Entries);
   appendEntries(this->impl->HeadersFileSets.InterfaceEntries.Entries);
+  appendEntries(this->impl->CxxModulesFileSets.InterfaceEntries.Entries);
+  appendEntries(this->impl->CxxModuleHeadersFileSets.InterfaceEntries.Entries);
 
 
   return result;
   return result;
 }
 }

+ 4 - 0
Source/cmTarget.h

@@ -275,8 +275,12 @@ public:
   cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const;
   cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const;
 
 
   cmBTStringRange GetHeaderSetsEntries() const;
   cmBTStringRange GetHeaderSetsEntries() const;
+  cmBTStringRange GetCxxModuleSetsEntries() const;
+  cmBTStringRange GetCxxModuleHeaderSetsEntries() const;
 
 
   cmBTStringRange GetInterfaceHeaderSetsEntries() const;
   cmBTStringRange GetInterfaceHeaderSetsEntries() const;
+  cmBTStringRange GetInterfaceCxxModuleSetsEntries() const;
+  cmBTStringRange GetInterfaceCxxModuleHeaderSetsEntries() const;
 
 
   std::string ImportedGetFullPath(const std::string& config,
   std::string ImportedGetFullPath(const std::string& config,
                                   cmStateEnums::ArtifactType artifact) const;
                                   cmStateEnums::ArtifactType artifact) const;

+ 26 - 4
Source/cmTargetSourcesCommand.cxx

@@ -9,6 +9,7 @@
 #include <cmext/string_view>
 #include <cmext/string_view>
 
 
 #include "cmArgumentParser.h"
 #include "cmArgumentParser.h"
+#include "cmExperimental.h"
 #include "cmFileSet.h"
 #include "cmFileSet.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpression.h"
 #include "cmListFileCache.h"
 #include "cmListFileCache.h"
@@ -256,9 +257,30 @@ bool TargetSourcesImpl::HandleOneFileSet(
       this->SetError("Must specify a TYPE when creating file set");
       this->SetError("Must specify a TYPE when creating file set");
       return false;
       return false;
     }
     }
-    if (type != "HEADERS"_s) {
-      this->SetError("File set TYPE may only be \"HEADERS\"");
-      return false;
+    bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled(
+      *this->Makefile, cmExperimental::Feature::CxxModuleCMakeApi);
+
+    if (supportCxx20FileSetTypes) {
+      if (type != "HEADERS"_s && type != "CXX_MODULES"_s &&
+          type != "CXX_MODULE_HEADER_UNITS"_s) {
+        this->SetError(
+          R"(File set TYPE may only be "HEADERS", "CXX_MODULES", or "CXX_MODULE_HEADER_UNITS")");
+        return false;
+      }
+
+      if (cmFileSetVisibilityIsForInterface(visibility) &&
+          !cmFileSetVisibilityIsForSelf(visibility)) {
+        if (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s) {
+          this->SetError(
+            R"(File set TYPEs "CXX_MODULES" and "CXX_MODULE_HEADER_UNITS" may not have "INTERFACE" visibility)");
+          return false;
+        }
+      }
+    } else {
+      if (type != "HEADERS"_s) {
+        this->SetError("File set TYPE may only be \"HEADERS\"");
+        return false;
+      }
     }
     }
 
 
     if (args.BaseDirs.empty()) {
     if (args.BaseDirs.empty()) {
@@ -294,7 +316,7 @@ bool TargetSourcesImpl::HandleOneFileSet(
   if (!baseDirectories.empty()) {
   if (!baseDirectories.empty()) {
     fileSet.first->AddDirectoryEntry(
     fileSet.first->AddDirectoryEntry(
       BT<std::string>(baseDirectories, this->Makefile->GetBacktrace()));
       BT<std::string>(baseDirectories, this->Makefile->GetBacktrace()));
-    if (type == "HEADERS"_s) {
+    if (type == "HEADERS"_s || type == "CXX_MODULE_HEADER_UNITS"_s) {
       for (auto const& dir : cmExpandedList(baseDirectories)) {
       for (auto const& dir : cmExpandedList(baseDirectories)) {
         auto interfaceDirectoriesGenex =
         auto interfaceDirectoriesGenex =
           cmStrCat("$<BUILD_INTERFACE:", dir, ">");
           cmStrCat("$<BUILD_INTERFACE:", dir, ">");

+ 12 - 0
Source/cmVisualStudio10TargetGenerator.cxx

@@ -347,6 +347,18 @@ std::ostream& cmVisualStudio10TargetGenerator::Elem::WriteString(
 
 
 void cmVisualStudio10TargetGenerator::Generate()
 void cmVisualStudio10TargetGenerator::Generate()
 {
 {
+  for (std::string const& config : this->Configurations) {
+    this->GeneratorTarget->CheckCxxModuleStatus(config);
+  }
+
+  if (this->GeneratorTarget->HaveCxx20ModuleSources()) {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("The \"", this->GeneratorTarget->GetName(),
+               "\" target contains C++ module sources which are not supported "
+               "by the generator"));
+  }
+
   this->ProjectType = this->ComputeProjectType(this->GeneratorTarget);
   this->ProjectType = this->ComputeProjectType(this->GeneratorTarget);
   this->Managed = this->ProjectType == VsProjectType::csproj;
   this->Managed = this->ProjectType == VsProjectType::csproj;
   const std::string ProjectFileExtension =
   const std::string ProjectFileExtension =

+ 3 - 0
Tests/RunCMake/CMakeLists.txt

@@ -564,6 +564,9 @@ if(CMake_TEST_CUDA)
 endif()
 endif()
 add_RunCMake_test(DependencyGraph -DCMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER})
 add_RunCMake_test(DependencyGraph -DCMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER})
 
 
+# Add C++ Module tests.
+add_RunCMake_test(CXXModules)
+
 # ctresalloc links against CMakeLib and CTestLib, which means it can't be built
 # ctresalloc links against CMakeLib and CTestLib, which means it can't be built
 # if CMake_TEST_EXTERNAL_CMAKE is activated (the compiler might be different.)
 # if CMake_TEST_EXTERNAL_CMAKE is activated (the compiler might be different.)
 # So, it has to be provided in the original build tree.
 # So, it has to be provided in the original build tree.

+ 6 - 0
Tests/RunCMake/CXXModules/CMakeLists.txt

@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.23)
+project(${RunCMake_TEST} NONE)
+
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "17be90bd-a850-44e0-be50-448de847d652")
+
+include(${RunCMake_TEST}.cmake)

+ 1 - 0
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-result.txt

@@ -0,0 +1 @@
+1

+ 12 - 0
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-stderr.txt

@@ -0,0 +1,12 @@
+CMake Warning \(dev\) at FileSetModuleHeaderUnitsInterface.cmake:2 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error at FileSetModuleHeaderUnitsInterface.cmake:2 \(target_sources\):
+  target_sources File set TYPEs "CXX_MODULES" and "CXX_MODULE_HEADER_UNITS"
+  may not have "INTERFACE" visibility
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)

+ 8 - 0
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface.cmake

@@ -0,0 +1,8 @@
+add_library(module-header)
+target_sources(module-header
+  INTERFACE
+    FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES
+      sources/module-header.h)
+target_compile_features(module-header
+  PRIVATE
+    cxx_std_20)

+ 11 - 0
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate-stderr.txt

@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at FileSetModuleHeaderUnitsPrivate.cmake:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.

+ 13 - 0
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate.cmake

@@ -0,0 +1,13 @@
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(module-header
+  sources/cxx-anchor.cxx)
+target_sources(module-header
+  PRIVATE
+    FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES
+      sources/module-header.h)
+target_compile_features(module-header
+  PRIVATE
+    cxx_std_20)

+ 11 - 0
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic-stderr.txt

@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at FileSetModuleHeaderUnitsPublic.cmake:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.

+ 13 - 0
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic.cmake

@@ -0,0 +1,13 @@
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(module-header
+  sources/cxx-anchor.cxx)
+target_sources(module-header
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES
+      sources/module-header.h)
+target_compile_features(module-header
+  PRIVATE
+    cxx_std_20)

+ 1 - 0
Tests/RunCMake/CXXModules/FileSetModulesInterface-result.txt

@@ -0,0 +1 @@
+1

+ 12 - 0
Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt

@@ -0,0 +1,12 @@
+CMake Warning \(dev\) at FileSetModulesInterface.cmake:2 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error at FileSetModulesInterface.cmake:2 \(target_sources\):
+  target_sources File set TYPEs "CXX_MODULES" and "CXX_MODULE_HEADER_UNITS"
+  may not have "INTERFACE" visibility
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)

+ 8 - 0
Tests/RunCMake/CXXModules/FileSetModulesInterface.cmake

@@ -0,0 +1,8 @@
+add_library(module)
+target_sources(module
+  INTERFACE
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(module
+  PRIVATE
+    cxx_std_20)

+ 11 - 0
Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt

@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at FileSetModulesPrivate.cmake:6 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.

+ 12 - 0
Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake

@@ -0,0 +1,12 @@
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(module)
+target_sources(module
+  PRIVATE
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(module
+  PRIVATE
+    cxx_std_20)

+ 11 - 0
Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt

@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at FileSetModulesPublic.cmake:6 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.

+ 12 - 0
Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake

@@ -0,0 +1,12 @@
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(module)
+target_sources(module
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(module
+  PRIVATE
+    cxx_std_20)

+ 1 - 0
Tests/RunCMake/CXXModules/NoCXX-result.txt

@@ -0,0 +1 @@
+1

+ 20 - 0
Tests/RunCMake/CXXModules/NoCXX-stderr.txt

@@ -0,0 +1,20 @@
+CMake Warning \(dev\) at NoCXX.cmake:4 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error in CMakeLists.txt:
+  The "nocxx" target has C\+\+ module sources but the "CXX" language has not
+  been enabled
+
+(
+CMake Error in CMakeLists.txt:
+(  The "nocxx" target has C\+\+ module sources but the "CXX" language has not
+  been enabled
+|  The "nocxx" target contains C\+\+ module sources which are not supported by
+  the generator
+)
+)*
+CMake Generate step failed.  Build files cannot be regenerated correctly.

+ 9 - 0
Tests/RunCMake/CXXModules/NoCXX.cmake

@@ -0,0 +1,9 @@
+enable_language(C)
+
+add_library(nocxx)
+target_sources(nocxx
+  PRIVATE
+    sources/c-anchor.c
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)

+ 1 - 0
Tests/RunCMake/CXXModules/NoCXX20-result.txt

@@ -0,0 +1 @@
+1

+ 20 - 0
Tests/RunCMake/CXXModules/NoCXX20-stderr.txt

@@ -0,0 +1,20 @@
+CMake Warning \(dev\) at NoCXX20.cmake:4 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error in CMakeLists.txt:
+  The "nocxx20" target has C\+\+ module sources but is not using at least
+  "cxx_std_20"
+
+(
+CMake Error in CMakeLists.txt:
+(  The "nocxx20" target has C\+\+ module sources but is not using at least
+  "cxx_std_20"
+|  The "nocxx20" target contains C\+\+ module sources which are not supported by
+  the generator
+)
+)*
+CMake Generate step failed.  Build files cannot be regenerated correctly.

+ 10 - 0
Tests/RunCMake/CXXModules/NoCXX20.cmake

@@ -0,0 +1,10 @@
+enable_language(CXX)
+
+add_library(nocxx20)
+target_sources(nocxx20
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(nocxx20
+  PRIVATE
+    cxx_std_17)

+ 1 - 0
Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt

@@ -0,0 +1 @@
+1

+ 20 - 0
Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt

@@ -0,0 +1,20 @@
+CMake Warning \(dev\) at NoCXX20ModuleFlag.cmake:4 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error in CMakeLists.txt:
+  The "noexperimentalflag" target has C\+\+ module sources but its experimental
+  support has not been requested
+
+(
+CMake Error in CMakeLists.txt:
+(  The "noexperimentalflag" target has C\+\+ module sources but its experimental
+  support has not been requested
+|  The "noexperimentalflag" target contains C\+\+ module sources which are not
+  supported by the generator
+)
+)*
+CMake Generate step failed.  Build files cannot be regenerated correctly.

+ 10 - 0
Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake

@@ -0,0 +1,10 @@
+enable_language(CXX)
+
+add_library(noexperimentalflag)
+target_sources(noexperimentalflag
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(noexperimentalflag
+  PRIVATE
+    cxx_std_20)

+ 1 - 0
Tests/RunCMake/CXXModules/NoDyndepSupport-result.txt

@@ -0,0 +1 @@
+1

+ 30 - 0
Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt

@@ -0,0 +1,30 @@
+CMake Warning \(dev\) at NoDyndepSupport.cmake:10 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+(CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error:
+  The Ninja generator does not support C\+\+20 modules using Ninja version
+
+    .*
+
+  due to lack of required features.  Ninja 1.10 or higher is required.
+
+|CMake Error in CMakeLists.txt:
+  The "nodyndep" target contains C\+\+ module sources which are not supported
+  by the generator
+
+(
+CMake Error in CMakeLists.txt:
+  The "nodyndep" target contains C\+\+ module sources which are not supported
+  by the generator
+
+)*)
+CMake Generate step failed.  Build files cannot be regenerated correctly.

+ 16 - 0
Tests/RunCMake/CXXModules/NoDyndepSupport.cmake

@@ -0,0 +1,16 @@
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+if (NOT CMAKE_CXX_STANDARD_DEFAULT)
+  set(CMAKE_CXX_STANDARD_DEFAULT "11")
+endif ()
+
+add_library(nodyndep)
+target_sources(nodyndep
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(nodyndep
+  PRIVATE
+    cxx_std_20)

+ 1 - 0
Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-result.txt

@@ -0,0 +1 @@
+1

+ 22 - 0
Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-stderr.txt

@@ -0,0 +1,22 @@
+CMake Warning \(dev\) at NotCXXSourceModuleHeaderUnits.cmake:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error in CMakeLists.txt:
+  Target "not-cxx-source" contains the source
+
+    .*/Tests/RunCMake/CXXModules/sources/c-anchor.c
+
+  in a file set of type "CXX_MODULE_HEADER_UNITS" but the source is not
+  classified as a "CXX" source.
+
+
+CMake Generate step failed.  Build files cannot be regenerated correctly.

+ 15 - 0
Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits.cmake

@@ -0,0 +1,15 @@
+enable_language(C)
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(not-cxx-source)
+target_sources(not-cxx-source
+  PRIVATE
+    sources/cxx-anchor.cxx
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES
+      sources/c-anchor.c)
+target_compile_features(not-cxx-source
+  PRIVATE
+    cxx_std_20)

+ 1 - 0
Tests/RunCMake/CXXModules/NotCXXSourceModules-result.txt

@@ -0,0 +1 @@
+1

+ 17 - 0
Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt

@@ -0,0 +1,17 @@
+CMake Warning \(dev\) at NotCXXSourceModules.cmake:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error in CMakeLists.txt:
+  Target "not-cxx-source" contains the source
+
+    .*/Tests/RunCMake/CXXModules/sources/c-anchor.c
+
+  in a file set of type "CXX_MODULES" but the source is not classified as a
+  "CXX" source.
+
+
+CMake Generate step failed.  Build files cannot be regenerated correctly.

+ 13 - 0
Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake

@@ -0,0 +1,13 @@
+enable_language(C)
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(not-cxx-source)
+target_sources(not-cxx-source
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/c-anchor.c)
+target_compile_features(not-cxx-source
+  PRIVATE
+    cxx_std_20)

+ 67 - 0
Tests/RunCMake/CXXModules/RunCMakeTest.cmake

@@ -0,0 +1,67 @@
+include(RunCMake)
+
+# For `if (IN_LIST)`
+cmake_policy(SET CMP0057 NEW)
+
+run_cmake(compiler_introspection)
+include("${RunCMake_BINARY_DIR}/compiler_introspection-build/info.cmake")
+
+# Test negative cases where C++20 modules do not work.
+run_cmake(NoCXX)
+if ("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
+  # This test requires that the compiler be told to compile in an older-than-20
+  # standard. If the compiler forces a standard to be used, skip it.
+  if (NOT forced_cxx_standard)
+    run_cmake(NoCXX20)
+  endif ()
+
+  # This test uses C++20, but another prerequisite is missing, so forced
+  # standards don't matter.
+  run_cmake(NoCXX20ModuleFlag)
+endif ()
+
+if (RunCMake_GENERATOR MATCHES "Ninja")
+  execute_process(
+    COMMAND "${CMAKE_MAKE_PROGRAM}" --version
+    RESULT_VARIABLE res
+    OUTPUT_VARIABLE ninja_version
+    ERROR_VARIABLE err
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_STRIP_TRAILING_WHITESPACE)
+
+  if (res)
+    message(WARNING
+      "Failed to determine `ninja` version: ${err}")
+    set(ninja_version "0")
+  endif ()
+endif ()
+
+# Test behavior when the generator does not support C++20 modules.
+if (NOT RunCMake_GENERATOR MATCHES "Ninja" OR
+    ninja_version VERSION_LESS "1.10" OR
+    NOT "cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
+  if ("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
+    run_cmake(NoDyndepSupport)
+  endif ()
+
+  # Bail; the remaining tests require the generator to successfully generate
+  # with C++20 modules in the source list.
+  return ()
+endif ()
+
+set(fileset_types
+  Modules
+  ModuleHeaderUnits)
+set(scopes
+  Interface
+  Private
+  Public)
+foreach (fileset_type IN LISTS fileset_types)
+  foreach (scope IN LISTS scopes)
+    run_cmake("FileSet${fileset_type}${scope}")
+  endforeach ()
+
+  # Test the error message when a non-C++ source file is found in the source
+  # list.
+  run_cmake("NotCXXSource${fileset_type}")
+endforeach ()

+ 25 - 0
Tests/RunCMake/CXXModules/compiler_introspection.cmake

@@ -0,0 +1,25 @@
+enable_language(CXX)
+
+set(info "")
+
+# See `Modules/Compiler/MSVC-CXX.cmake` for this. If there is explicitly no
+# default, the feature list is populated to be everything.
+if (DEFINED CMAKE_CXX_STANDARD_DEFAULT AND
+    CMAKE_CXX_STANDARD_DEFAULT STREQUAL "")
+  set(CMAKE_CXX_COMPILE_FEATURES "")
+endif ()
+
+# Detect if the environment forces a C++ standard, let the test selection know.
+set(forced_cxx_standard 0)
+if (CMAKE_CXX_FLAGS MATCHES "-std=")
+  set(forced_cxx_standard 1)
+endif ()
+
+# Forward information about the C++ compile features.
+string(APPEND info "\
+set(CMAKE_CXX_COMPILE_FEATURES \"${CMAKE_CXX_COMPILE_FEATURES}\")
+set(CMAKE_MAKE_PROGRAM \"${CMAKE_MAKE_PROGRAM}\")
+set(forced_cxx_standard \"${forced_cxx_standard}\")
+")
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}")

+ 4 - 0
Tests/RunCMake/CXXModules/sources/c-anchor.c

@@ -0,0 +1,4 @@
+int c_anchor()
+{
+  return 0;
+}

+ 4 - 0
Tests/RunCMake/CXXModules/sources/cxx-anchor.cxx

@@ -0,0 +1,4 @@
+int cxx_anchor()
+{
+  return 0;
+}

+ 9 - 0
Tests/RunCMake/CXXModules/sources/module-header.h

@@ -0,0 +1,9 @@
+#ifndef module_header_h
+#define module_header_h
+
+inline int h()
+{
+  return 0;
+}
+
+#endif

+ 6 - 0
Tests/RunCMake/CXXModules/sources/module-impl.cxx

@@ -0,0 +1,6 @@
+module M;
+
+int f()
+{
+  return 0;
+}

+ 11 - 0
Tests/RunCMake/CXXModules/sources/module-internal-part-impl.cxx

@@ -0,0 +1,11 @@
+#ifdef _MSC_VER
+// Only MSVC supports this pattern.
+module M : internal_part;
+#else
+module M;
+#endif
+
+int i()
+{
+  return 0;
+}

+ 3 - 0
Tests/RunCMake/CXXModules/sources/module-internal-part.cxx

@@ -0,0 +1,3 @@
+module M : internal_part;
+
+int i();

+ 13 - 0
Tests/RunCMake/CXXModules/sources/module-part-impl.cxx

@@ -0,0 +1,13 @@
+#ifdef _MSC_VER
+// Only MSVC supports this pattern.
+module M : part;
+#else
+module M;
+#endif
+
+import M : internal_part;
+
+int p()
+{
+  return i();
+}

+ 3 - 0
Tests/RunCMake/CXXModules/sources/module-part.cxx

@@ -0,0 +1,3 @@
+export module M : part;
+
+int p();

+ 6 - 0
Tests/RunCMake/CXXModules/sources/module-use.cxx

@@ -0,0 +1,6 @@
+import M;
+
+int main(int argc, char* argv[])
+{
+  return f() + p();
+}

+ 5 - 0
Tests/RunCMake/CXXModules/sources/module.cxx

@@ -0,0 +1,5 @@
+export module M;
+export import M : part;
+import M : internal_part;
+
+int f();

+ 1 - 0
Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt

@@ -0,0 +1 @@
+1

+ 12 - 0
Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt

@@ -0,0 +1,12 @@
+^CMake Warning \(dev\) at FileSetDefaultWrongTypeExperimental.cmake:6 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error at FileSetDefaultWrongTypeExperimental\.cmake:[0-9]+ \(target_sources\):
+  target_sources File set TYPE may only be "HEADERS", "CXX_MODULES", or
+  "CXX_MODULE_HEADER_UNITS"
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$

+ 6 - 0
Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake

@@ -0,0 +1,6 @@
+enable_language(C)
+
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "17be90bd-a850-44e0-be50-448de847d652")
+
+add_library(lib1 STATIC empty.c)
+target_sources(lib1 PRIVATE FILE_SET UNKNOWN)

+ 1 - 0
Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt

@@ -0,0 +1 @@
+1

+ 12 - 0
Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt

@@ -0,0 +1,12 @@
+^CMake Warning \(dev\) at FileSetWrongTypeExperimental.cmake:6 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error at FileSetWrongTypeExperimental\.cmake:[0-9]+ \(target_sources\):
+  target_sources File set TYPE may only be "HEADERS", "CXX_MODULES", or
+  "CXX_MODULE_HEADER_UNITS"
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$

+ 6 - 0
Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake

@@ -0,0 +1,6 @@
+enable_language(C)
+
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "17be90bd-a850-44e0-be50-448de847d652")
+
+add_library(lib1 STATIC empty.c)
+target_sources(lib1 PRIVATE FILE_SET a TYPE UNKNOWN)

+ 2 - 0
Tests/RunCMake/target_sources/RunCMakeTest.cmake

@@ -26,6 +26,8 @@ run_cmake(FileSetProperties)
 run_cmake(FileSetNoType)
 run_cmake(FileSetNoType)
 run_cmake(FileSetWrongType)
 run_cmake(FileSetWrongType)
 run_cmake(FileSetDefaultWrongType)
 run_cmake(FileSetDefaultWrongType)
+run_cmake(FileSetWrongTypeExperimental)
+run_cmake(FileSetDefaultWrongTypeExperimental)
 run_cmake(FileSetChangeScope)
 run_cmake(FileSetChangeScope)
 run_cmake(FileSetChangeType)
 run_cmake(FileSetChangeType)
 run_cmake(FileSetWrongBaseDirs)
 run_cmake(FileSetWrongBaseDirs)

+ 13 - 2
Tests/RunCMake/try_compile/CxxStandard-stderr.txt

@@ -1,6 +1,17 @@
-^CMake Error at .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:[0-9]+ \(add_executable\):
+^(CMake Error in .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:
+  The CXX_STANDARD property on target "cmTC_[0-9a-f]*" contained an invalid
+  value: "3".
+
+
+)?CMake Error at .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:[0-9]+ \(add_executable\):
   CXX_STANDARD is set to invalid value '3'
   CXX_STANDARD is set to invalid value '3'
-+
+
+(
+CMake Error in .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:
+  The CXX_STANDARD property on target "cmTC_[0-9a-f]*" contained an invalid
+  value: "3".
+
+)?
 CMake Error at CxxStandard.cmake:[0-9]+ \(try_compile\):
 CMake Error at CxxStandard.cmake:[0-9]+ \(try_compile\):
   Failed to generate test project build system.
   Failed to generate test project build system.
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):