summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2022-06-17 15:35:03 +0000
committerKitware Robot <kwrobot@kitware.com>2022-06-17 11:35:12 -0400
commitd94e09ec88c99434941029cde07b6c1e6bd0dde1 (patch)
tree6aa82ff8decf5ba566b5f9181c31d019dabfb23d
parent88a300d421c4cfec59197fcc7faac1ef526882c2 (diff)
parent07bc3b07ec8796f4a067ede7f4426ccbfa8ab858 (diff)
downloadcmake-d94e09ec88c99434941029cde07b6c1e6bd0dde1.tar.gz
Merge topic 'cpp-named-module-file-sets'
07bc3b07ec gitlab-ci: test C++ modules using GCC 1b2270aa4e ci: add a Docker image to test out C++ modules with GCC 8c5a53096a Tests/RunCMake/CXXModules: add module-using examples 4151547e2f cmGlobalNinjaGenerator: use `cmModuleMapper` implementation b43bdaff3c cmCxxModuleMapper: implement support for GCC's module map format 02d0f0e752 cmCxxModuleMapper: add source to handle module mapper contents a046a45aad cmGlobalNinjaGenerator: add a TODO for header units 386465bf83 cmTarget: add support for C++ module fileset types ... Acked-by: Kitware Robot <kwrobot@kitware.com> Tested-by: buildbot <buildbot@kitware.com> Merge-request: !7369
-rw-r--r--.gitlab-ci.yml20
-rw-r--r--.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake4
-rw-r--r--.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake4
-rw-r--r--.gitlab/ci/cxx_modules_rules_gcc.cmake10
-rw-r--r--.gitlab/ci/docker/gcc_cxx_modules/Dockerfile9
-rwxr-xr-x.gitlab/ci/docker/gcc_cxx_modules/install_deps.sh7
-rwxr-xr-x.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh26
-rw-r--r--.gitlab/os-linux.yml24
-rw-r--r--Help/command/target_sources.rst77
-rw-r--r--Help/dev/experimental.rst22
-rw-r--r--Help/manual/cmake-properties.7.rst12
-rw-r--r--Help/prop_tgt/CXX_MODULE_DIRS.rst17
-rw-r--r--Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst17
-rw-r--r--Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS.rst17
-rw-r--r--Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME.rst19
-rw-r--r--Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET.rst18
-rw-r--r--Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SETS.rst18
-rw-r--r--Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME.rst19
-rw-r--r--Help/prop_tgt/CXX_MODULE_SET.rst18
-rw-r--r--Help/prop_tgt/CXX_MODULE_SETS.rst16
-rw-r--r--Help/prop_tgt/CXX_MODULE_SET_NAME.rst18
-rw-r--r--Help/prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS.rst16
-rw-r--r--Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst16
-rw-r--r--Source/CMakeLists.txt4
-rw-r--r--Source/cmCommonTargetGenerator.cxx4
-rw-r--r--Source/cmCxxModuleMapper.cxx76
-rw-r--r--Source/cmCxxModuleMapper.h48
-rw-r--r--Source/cmExperimental.cxx63
-rw-r--r--Source/cmExperimental.h21
-rw-r--r--Source/cmExportBuildFileGenerator.cxx32
-rw-r--r--Source/cmExportInstallFileGenerator.cxx34
-rw-r--r--Source/cmGeneratorTarget.cxx90
-rw-r--r--Source/cmGeneratorTarget.h30
-rw-r--r--Source/cmGlobalNinjaGenerator.cxx74
-rw-r--r--Source/cmGlobalVisualStudio7Generator.cxx15
-rw-r--r--Source/cmGlobalVisualStudio7Generator.h2
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx16
-rw-r--r--Source/cmMakefileTargetGenerator.cxx65
-rw-r--r--Source/cmNinjaTargetGenerator.cxx97
-rw-r--r--Source/cmScriptGenerator.cxx2
-rw-r--r--Source/cmTarget.cxx79
-rw-r--r--Source/cmTarget.h4
-rw-r--r--Source/cmTargetSourcesCommand.cxx30
-rw-r--r--Source/cmVisualStudio10TargetGenerator.cxx12
-rw-r--r--Tests/RunCMake/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/CXXModules/CMakeLists.txt6
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-result.txt1
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-stderr.txt12
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface.cmake8
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate-stderr.txt11
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate.cmake13
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic-stderr.txt11
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic.cmake13
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModulesInterface-result.txt1
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt12
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModulesInterface.cmake8
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt11
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake12
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt11
-rw-r--r--Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake12
-rw-r--r--Tests/RunCMake/CXXModules/NoCXX-result.txt1
-rw-r--r--Tests/RunCMake/CXXModules/NoCXX-stderr.txt20
-rw-r--r--Tests/RunCMake/CXXModules/NoCXX.cmake9
-rw-r--r--Tests/RunCMake/CXXModules/NoCXX20-result.txt1
-rw-r--r--Tests/RunCMake/CXXModules/NoCXX20-stderr.txt20
-rw-r--r--Tests/RunCMake/CXXModules/NoCXX20.cmake10
-rw-r--r--Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt1
-rw-r--r--Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt20
-rw-r--r--Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake10
-rw-r--r--Tests/RunCMake/CXXModules/NoDyndepSupport-result.txt1
-rw-r--r--Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt30
-rw-r--r--Tests/RunCMake/CXXModules/NoDyndepSupport.cmake16
-rw-r--r--Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-result.txt1
-rw-r--r--Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-stderr.txt22
-rw-r--r--Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits.cmake15
-rw-r--r--Tests/RunCMake/CXXModules/NotCXXSourceModules-result.txt1
-rw-r--r--Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt17
-rw-r--r--Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake13
-rw-r--r--Tests/RunCMake/CXXModules/RunCMakeTest.cmake120
-rw-r--r--Tests/RunCMake/CXXModules/compiler_introspection.cmake25
-rw-r--r--Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake18
-rw-r--r--Tests/RunCMake/CXXModules/examples/generated-stderr.txt9
-rw-r--r--Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt23
-rw-r--r--Tests/RunCMake/CXXModules/examples/generated/importable.cxx.in5
-rw-r--r--Tests/RunCMake/CXXModules/examples/generated/main.cxx6
-rw-r--r--Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt9
-rw-r--r--Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt31
-rw-r--r--Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx9
-rw-r--r--Tests/RunCMake/CXXModules/examples/internal-partitions/main.cxx6
-rw-r--r--Tests/RunCMake/CXXModules/examples/internal-partitions/partition.cxx6
-rw-r--r--Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt9
-rw-r--r--Tests/RunCMake/CXXModules/examples/library-static-stderr.txt9
-rw-r--r--Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt30
-rw-r--r--Tests/RunCMake/CXXModules/examples/library/importable.cxx8
-rw-r--r--Tests/RunCMake/CXXModules/examples/library/main.cxx6
-rw-r--r--Tests/RunCMake/CXXModules/examples/partitions-stderr.txt9
-rw-r--r--Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt31
-rw-r--r--Tests/RunCMake/CXXModules/examples/partitions/importable.cxx9
-rw-r--r--Tests/RunCMake/CXXModules/examples/partitions/main.cxx6
-rw-r--r--Tests/RunCMake/CXXModules/examples/partitions/partition.cxx8
-rw-r--r--Tests/RunCMake/CXXModules/examples/simple-stderr.txt9
-rw-r--r--Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt18
-rw-r--r--Tests/RunCMake/CXXModules/examples/simple/importable.cxx6
-rw-r--r--Tests/RunCMake/CXXModules/examples/simple/main.cxx6
-rw-r--r--Tests/RunCMake/CXXModules/sources/c-anchor.c4
-rw-r--r--Tests/RunCMake/CXXModules/sources/cxx-anchor.cxx4
-rw-r--r--Tests/RunCMake/CXXModules/sources/module-header.h9
-rw-r--r--Tests/RunCMake/CXXModules/sources/module-impl.cxx6
-rw-r--r--Tests/RunCMake/CXXModules/sources/module-internal-part-impl.cxx11
-rw-r--r--Tests/RunCMake/CXXModules/sources/module-internal-part.cxx3
-rw-r--r--Tests/RunCMake/CXXModules/sources/module-part-impl.cxx13
-rw-r--r--Tests/RunCMake/CXXModules/sources/module-part.cxx3
-rw-r--r--Tests/RunCMake/CXXModules/sources/module-use.cxx6
-rw-r--r--Tests/RunCMake/CXXModules/sources/module.cxx5
-rw-r--r--Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt1
-rw-r--r--Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt12
-rw-r--r--Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake6
-rw-r--r--Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt1
-rw-r--r--Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt12
-rw-r--r--Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake6
-rw-r--r--Tests/RunCMake/target_sources/RunCMakeTest.cmake2
-rw-r--r--Tests/RunCMake/try_compile/CxxStandard-stderr.txt15
-rwxr-xr-xbootstrap2
123 files changed, 2010 insertions, 117 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8f265080a7..f321d33e4d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -275,6 +275,26 @@ t:hip4.2-radeon:
variables:
CMAKE_CI_NO_MR: "true"
+t:linux-gcc-cxx-modules-ninja:
+ extends:
+ - .gcc_cxx_modules_ninja
+ - .cmake_test_linux_release
+ - .linux_builder_tags
+ - .run_dependent
+ - .needs_centos6_x86_64
+ variables:
+ CMAKE_CI_JOB_NIGHTLY: "true"
+
+t:linux-gcc-cxx-modules-ninja-multi:
+ extends:
+ - .gcc_cxx_modules_ninja_multi
+ - .cmake_test_linux_release
+ - .linux_builder_tags
+ - .run_dependent
+ - .needs_centos6_x86_64
+ variables:
+ CMAKE_CI_JOB_NIGHTLY: "true"
+
b:fedora36-ninja:
extends:
- .fedora36_ninja
diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake
new file mode 100644
index 0000000000..bf990c8193
--- /dev/null
+++ b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake
@@ -0,0 +1,4 @@
+set(CMake_TEST_MODULE_COMPILATION "named,partitions,internal_partitions" CACHE STRING "")
+set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_gcc.cmake" CACHE STRING "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake
new file mode 100644
index 0000000000..bf990c8193
--- /dev/null
+++ b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake
@@ -0,0 +1,4 @@
+set(CMake_TEST_MODULE_COMPILATION "named,partitions,internal_partitions" CACHE STRING "")
+set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_gcc.cmake" CACHE STRING "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/cxx_modules_rules_gcc.cmake b/.gitlab/ci/cxx_modules_rules_gcc.cmake
new file mode 100644
index 0000000000..d800099935
--- /dev/null
+++ b/.gitlab/ci/cxx_modules_rules_gcc.cmake
@@ -0,0 +1,10 @@
+set(CMake_TEST_CXXModules_UUID "a246741c-d067-4019-a8fb-3d16b0c9d1d3")
+
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
+ "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E -x c++ <SOURCE>"
+ " -MT <DYNDEP_FILE> -MD -MF <DEP_FILE>"
+ " -fmodules-ts -fdep-file=<DYNDEP_FILE> -fdep-output=<OBJECT> -fdep-format=trtbd"
+ " -o <PREPROCESSED_SOURCE>")
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "gcc")
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "-fmodules-ts -fmodule-mapper=<MODULE_MAP_FILE> -fdep-format=trtbd -x c++")
diff --git a/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile b/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile
new file mode 100644
index 0000000000..e0af0b9f0f
--- /dev/null
+++ b/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile
@@ -0,0 +1,9 @@
+FROM fedora:36
+MAINTAINER Ben Boeckel <ben.boeckel@kitware.com>
+
+# Install build dependencies for packages.
+COPY install_deps.sh /root/install_deps.sh
+RUN sh /root/install_deps.sh
+
+COPY install_gcc.sh /root/install_gcc.sh
+RUN sh /root/install_gcc.sh
diff --git a/.gitlab/ci/docker/gcc_cxx_modules/install_deps.sh b/.gitlab/ci/docker/gcc_cxx_modules/install_deps.sh
new file mode 100755
index 0000000000..2556b54e24
--- /dev/null
+++ b/.gitlab/ci/docker/gcc_cxx_modules/install_deps.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -e
+
+dnf install -y --setopt=install_weak_deps=False \
+ gcc-c++ mpfr-devel libmpc-devel isl-devel flex bison file findutils diffutils
+dnf clean all
diff --git a/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh b/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh
new file mode 100755
index 0000000000..20ea35f6f7
--- /dev/null
+++ b/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+set -e
+
+readonly revision="p1689r5-cmake-ci-20220614" # 3075e510e3d29583f8886b95aff044c0474c84a5
+readonly tarball="https://github.com/mathstuf/gcc/archive/$revision.tar.gz"
+
+readonly workdir="$HOME/gcc"
+readonly srcdir="$workdir/gcc"
+readonly builddir="$workdir/build"
+readonly njobs="$( nproc )"
+
+mkdir -p "$workdir"
+cd "$workdir"
+curl -L "$tarball" > "gcc-$revision.tar.gz"
+tar xf "gcc-$revision.tar.gz"
+mv "gcc-$revision" "$srcdir"
+mkdir -p "$builddir"
+cd "$builddir"
+"$srcdir/configure" \
+ --disable-multilib \
+ --enable-languages=c,c++ \
+ --prefix="/opt/gcc-p1689"
+make "-j$njobs"
+make "-j$njobs" install-strip
+rm -rf "$workdir"
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml
index 0b2d382e81..6afd299fa4 100644
--- a/.gitlab/os-linux.yml
+++ b/.gitlab/os-linux.yml
@@ -299,6 +299,30 @@
CMAKE_CONFIGURATION: hip4.2_radeon
CMAKE_GENERATOR: "Ninja Multi-Config"
+### C++ modules
+
+.gcc_cxx_modules_x86_64:
+ image: "kitware/cmake:ci-gcc_cxx_modules-x86_64-2022-06-14"
+
+ variables:
+ GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
+ CMAKE_ARCH: x86_64
+ CC: "/opt/gcc-p1689/bin/gcc"
+ CXX: "/opt/gcc-p1689/bin/g++"
+
+.gcc_cxx_modules_ninja:
+ extends: .gcc_cxx_modules_x86_64
+
+ variables:
+ CMAKE_CONFIGURATION: linux_gcc_cxx_modules_ninja
+
+.gcc_cxx_modules_ninja_multi:
+ extends: .gcc_cxx_modules_x86_64
+
+ variables:
+ CMAKE_CONFIGURATION: linux_gcc_cxx_modules_ninja_multi
+ CMAKE_GENERATOR: "Ninja Multi-Config"
+
## Tags
.linux_builder_tags:
diff --git a/Help/command/target_sources.rst b/Help/command/target_sources.rst
index 1ad6c373e2..0c4323c98e 100644
--- a/Help/command/target_sources.rst
+++ b/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
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
-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
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,
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.
``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.
``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)``,
but they should not generally be manipulated directly:
+For file sets of type ``HEADERS``:
+
* :prop_tgt:`HEADER_SETS`
* :prop_tgt:`INTERFACE_HEADER_SETS`
* :prop_tgt:`HEADER_SET`
@@ -141,17 +168,37 @@ but they should not generally be manipulated directly:
* :prop_tgt:`HEADER_DIRS`
* :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_sources(FILE_SET)`` as follows:
: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`
- 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.
diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst
index 7638d2210d..14e6075022 100644
--- a/Help/dev/experimental.rst
+++ b/Help/dev/experimental.rst
@@ -7,6 +7,23 @@ See documentation on `CMake Development`_ for more information.
.. _`CMake Development`: README.rst
+Features are gated behind ``CMAKE_EXPERIMENTAL_`` variables which must be set
+to specific values in order to enable their gated behaviors. Note that the
+specific values will change over time to reinforce their experimental nature.
+When used, a warning will be generated to indicate that an experimental
+feature is in use and that the affected behavior in the project is not part of
+CMake's stability guarantees.
+
+C++20 Module APIs
+=================
+
+Variable: ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+Value: ``17be90bd-a850-44e0-be50-448de847d652``
+
+In order to support C++20 modules, there are a number of behaviors that have
+CMake APIs to provide the required features to build and export them from a
+project.
+
C++20 Module Dependencies
=========================
@@ -40,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``
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
by the `P1689r4`_ paper.
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 9bb8a714f0..cc054913c6 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -184,6 +184,16 @@ Properties on Targets
/prop_tgt/CUDA_STANDARD
/prop_tgt/CUDA_STANDARD_REQUIRED
/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_REQUIRED
/prop_tgt/DEBUG_POSTFIX
@@ -262,6 +272,8 @@ Properties on Targets
/prop_tgt/INTERFACE_COMPILE_DEFINITIONS
/prop_tgt/INTERFACE_COMPILE_FEATURES
/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_TO_VERIFY
/prop_tgt/INTERFACE_INCLUDE_DIRECTORIES
diff --git a/Help/prop_tgt/CXX_MODULE_DIRS.rst b/Help/prop_tgt/CXX_MODULE_DIRS.rst
new file mode 100644
index 0000000000..fdf383196c
--- /dev/null
+++ b/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.
diff --git a/Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst b/Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst
new file mode 100644
index 0000000000..8c27d4573e
--- /dev/null
+++ b/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.
diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS.rst
new file mode 100644
index 0000000000..17e5cf0ed7
--- /dev/null
+++ b/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.
diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME.rst
new file mode 100644
index 0000000000..ca30f23cd4
--- /dev/null
+++ b/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.
diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET.rst
new file mode 100644
index 0000000000..f67a84886d
--- /dev/null
+++ b/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.
diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SETS.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SETS.rst
new file mode 100644
index 0000000000..7b4bd3fb2a
--- /dev/null
+++ b/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`.
diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME.rst
new file mode 100644
index 0000000000..d328950324
--- /dev/null
+++ b/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.
diff --git a/Help/prop_tgt/CXX_MODULE_SET.rst b/Help/prop_tgt/CXX_MODULE_SET.rst
new file mode 100644
index 0000000000..ae9000e9e4
--- /dev/null
+++ b/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.
diff --git a/Help/prop_tgt/CXX_MODULE_SETS.rst b/Help/prop_tgt/CXX_MODULE_SETS.rst
new file mode 100644
index 0000000000..c03df391ac
--- /dev/null
+++ b/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`.
diff --git a/Help/prop_tgt/CXX_MODULE_SET_NAME.rst b/Help/prop_tgt/CXX_MODULE_SET_NAME.rst
new file mode 100644
index 0000000000..27c88f319b
--- /dev/null
+++ b/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.
diff --git a/Help/prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS.rst b/Help/prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS.rst
new file mode 100644
index 0000000000..eb3a9ff809
--- /dev/null
+++ b/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`.
diff --git a/Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst b/Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst
new file mode 100644
index 0000000000..cc30386220
--- /dev/null
+++ b/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`.
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 95b07cbc97..6312e93f50 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -197,6 +197,8 @@ set(SRCS
cmCustomCommandLines.cxx
cmCustomCommandLines.h
cmCustomCommandTypes.h
+ cmCxxModuleMapper.cxx
+ cmCxxModuleMapper.h
cmDefinitions.cxx
cmDefinitions.h
cmDependencyProvider.h
@@ -543,6 +545,8 @@ set(SRCS
cmExecuteProcessCommand.h
cmExpandedCommandArgument.cxx
cmExpandedCommandArgument.h
+ cmExperimental.cxx
+ cmExperimental.h
cmExportCommand.cxx
cmExportCommand.h
cmExportLibraryDependenciesCommand.cxx
diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx
index ba95168d68..5fe6756b13 100644
--- a/Source/cmCommonTargetGenerator.cxx
+++ b/Source/cmCommonTargetGenerator.cxx
@@ -9,6 +9,7 @@
#include "cmComputeLinkInformation.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalCommonGenerator.h"
+#include "cmGlobalGenerator.h"
#include "cmLocalCommonGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
@@ -175,6 +176,9 @@ std::vector<std::string> cmCommonTargetGenerator::GetLinkedTargetDirectories(
cmLocalGenerator* lg = linkee->GetLocalGenerator();
std::string di = cmStrCat(lg->GetCurrentBinaryDirectory(), '/',
lg->GetTargetDirectory(linkee));
+ if (lg->GetGlobalGenerator()->IsMultiConfig()) {
+ di = cmStrCat(di, '/', config);
+ }
dirs.push_back(std::move(di));
}
}
diff --git a/Source/cmCxxModuleMapper.cxx b/Source/cmCxxModuleMapper.cxx
new file mode 100644
index 0000000000..94ad721702
--- /dev/null
+++ b/Source/cmCxxModuleMapper.cxx
@@ -0,0 +1,76 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmCxxModuleMapper.h"
+
+#include <cassert>
+#include <sstream>
+#include <vector>
+
+#include "cmScanDepFormat.h"
+
+cm::optional<std::string> CxxModuleLocations::BmiGeneratorPathForModule(
+ std::string const& logical_name) const
+{
+ if (auto l = this->BmiLocationForModule(logical_name)) {
+ return this->PathForGenerator(*l);
+ }
+ return {};
+}
+
+namespace {
+
+std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc,
+ cmScanDepInfo const& obj)
+{
+ std::stringstream mm;
+
+ // Documented in GCC's documentation. The format is a series of
+ // lines with a module name and the associated filename separated
+ // by spaces. The first line may use `$root` as the module name
+ // to specify a "repository root". That is used to anchor any
+ // relative paths present in the file (CMake should never
+ // generate any).
+
+ // Write the root directory to use for module paths.
+ mm << "$root " << loc.RootDirectory << "\n";
+
+ for (auto const& p : obj.Provides) {
+ if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
+ mm << p.LogicalName << ' ' << *bmi_loc << '\n';
+ }
+ }
+ for (auto const& r : obj.Requires) {
+ if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) {
+ mm << r.LogicalName << ' ' << *bmi_loc << '\n';
+ }
+ }
+
+ return mm.str();
+}
+}
+
+cm::static_string_view CxxModuleMapExtension(
+ cm::optional<CxxModuleMapFormat> format)
+{
+ if (format) {
+ switch (*format) {
+ case CxxModuleMapFormat::Gcc:
+ return ".gcm"_s;
+ }
+ }
+
+ return ".bmi"_s;
+}
+
+std::string CxxModuleMapContent(CxxModuleMapFormat format,
+ CxxModuleLocations const& loc,
+ cmScanDepInfo const& obj)
+{
+ switch (format) {
+ case CxxModuleMapFormat::Gcc:
+ return CxxModuleMapContentGcc(loc, obj);
+ }
+
+ assert(false);
+ return {};
+}
diff --git a/Source/cmCxxModuleMapper.h b/Source/cmCxxModuleMapper.h
new file mode 100644
index 0000000000..99384c919b
--- /dev/null
+++ b/Source/cmCxxModuleMapper.h
@@ -0,0 +1,48 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <functional>
+#include <string>
+
+#include <cm/optional>
+#include <cmext/string_view>
+
+struct cmScanDepInfo;
+
+enum class CxxModuleMapFormat
+{
+ Gcc,
+};
+
+struct CxxModuleLocations
+{
+ // The path from which all relative paths should be computed. If
+ // this is relative, it is relative to the compiler's working
+ // directory.
+ std::string RootDirectory;
+
+ // A function to convert a full path to a path for the generator.
+ std::function<std::string(std::string const&)> PathForGenerator;
+
+ // Lookup the BMI location of a logical module name.
+ std::function<cm::optional<std::string>(std::string const&)>
+ BmiLocationForModule;
+
+ // Returns the generator path (if known) for the BMI given a
+ // logical module name.
+ cm::optional<std::string> BmiGeneratorPathForModule(
+ std::string const& logical_name) const;
+};
+
+// Return the extension to use for a given modulemap format.
+cm::static_string_view CxxModuleMapExtension(
+ cm::optional<CxxModuleMapFormat> format);
+
+// Return the contents of the module map in the given format for the
+// object file.
+std::string CxxModuleMapContent(CxxModuleMapFormat format,
+ CxxModuleLocations const& loc,
+ cmScanDepInfo const& obj);
diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx
new file mode 100644
index 0000000000..e815d0e675
--- /dev/null
+++ b/Source/cmExperimental.cxx
@@ -0,0 +1,63 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmExperimental.h"
+
+#include <cassert>
+#include <cstddef>
+#include <string>
+
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmValue.h"
+
+namespace {
+
+/*
+ * The `Uuid` fields of these objects should change periodically.
+ * Search for other instances to keep the documentation and test suite
+ * up-to-date.
+ */
+
+struct FeatureData
+{
+ std::string const Uuid;
+ std::string const Variable;
+ std::string const Description;
+ bool Warned;
+} LookupTable[] = {
+ // CxxModuleCMakeApi
+ { "17be90bd-a850-44e0-be50-448de847d652",
+ "CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API",
+ "CMake's C++ module support is experimental. It is meant only for "
+ "experimentation and feedback to CMake developers.",
+ false },
+};
+static_assert(sizeof(LookupTable) / sizeof(LookupTable[0]) ==
+ static_cast<size_t>(cmExperimental::Feature::Sentinel),
+ "Experimental feature lookup table mismatch");
+
+FeatureData& DataForFeature(cmExperimental::Feature f)
+{
+ assert(f != cmExperimental::Feature::Sentinel);
+ return LookupTable[static_cast<size_t>(f)];
+}
+}
+
+bool cmExperimental::HasSupportEnabled(cmMakefile const& mf, Feature f)
+{
+ bool enabled = false;
+ auto& data = DataForFeature(f);
+
+ auto value = mf.GetDefinition(data.Variable);
+ if (value == data.Uuid) {
+ enabled = true;
+ }
+
+ if (enabled && !data.Warned) {
+ mf.IssueMessage(MessageType::AUTHOR_WARNING, data.Description);
+ data.Warned = true;
+ }
+
+ return enabled;
+}
diff --git a/Source/cmExperimental.h b/Source/cmExperimental.h
new file mode 100644
index 0000000000..26e0d17dbe
--- /dev/null
+++ b/Source/cmExperimental.h
@@ -0,0 +1,21 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+class cmMakefile;
+
+class cmExperimental
+{
+public:
+ enum class Feature
+ {
+ CxxModuleCMakeApi,
+
+ Sentinel,
+ };
+
+ static bool HasSupportEnabled(cmMakefile const& mf, Feature f);
+};
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index 6ce0c9850d..af33ada39d 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -9,7 +9,9 @@
#include <sstream>
#include <utility>
+#include <cm/string_view>
#include <cmext/algorithm>
+#include <cmext/string_view>
#include "cmExportSet.h"
#include "cmFileSet.h"
@@ -382,6 +384,21 @@ std::string cmExportBuildFileGenerator::GetFileSetDirectories(
std::any_of(directoryEntries.begin(), directoryEntries.end(),
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) {
auto dest = cmOutputConverter::EscapeForCMake(
directory, cmOutputConverter::WrapQuotes::NoWrap);
@@ -427,6 +444,21 @@ std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte,
std::any_of(fileEntries.begin(), fileEntries.end(),
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& filename : it.second) {
auto escapedFile = cmOutputConverter::EscapeForCMake(
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx
index adccdfeece..3a06769f9a 100644
--- a/Source/cmExportInstallFileGenerator.cxx
+++ b/Source/cmExportInstallFileGenerator.cxx
@@ -7,6 +7,9 @@
#include <sstream>
#include <utility>
+#include <cm/string_view>
+#include <cmext/string_view>
+
#include "cmExportSet.h"
#include "cmFileSet.h"
#include "cmGeneratedFileStream.h"
@@ -18,6 +21,7 @@
#include "cmInstallTargetGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
+#include "cmMessageType.h"
#include "cmOutputConverter.h"
#include "cmPolicies.h"
#include "cmStateTypes.h"
@@ -562,6 +566,21 @@ std::string cmExportInstallFileGenerator::GetFileSetDirectories(
cge->Evaluate(gte->LocalGenerator, config, gte),
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) {
resultVector.push_back(
cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
@@ -610,6 +629,21 @@ std::string cmExportInstallFileGenerator::GetFileSetFiles(
std::any_of(fileEntries.begin(), fileEntries.end(),
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) {
auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/');
for (auto const& filename : it.second) {
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 9147f8a3f8..433c1d5409 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -1712,7 +1712,8 @@ void addFileSetEntry(cmGeneratorTarget const* headTarget,
}
}
if (!found) {
- if (fileSet->GetType() == "HEADERS"_s) {
+ if (fileSet->GetType() == "HEADERS"_s ||
+ fileSet->GetType() == "CXX_MODULE_HEADER_UNITS"_s) {
headTarget->Makefile->GetOrCreateSourceGroup("Header Files")
->AddGroupFile(path);
}
@@ -1733,6 +1734,20 @@ void AddFileSetEntries(cmGeneratorTarget const* headTarget,
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,
@@ -8705,3 +8720,76 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
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;
+ }
+ }
+}
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index 6bce7d2b2e..349afa7b77 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -1196,4 +1196,34 @@ public:
bool operator()(cmGeneratorTarget const* t1,
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;
};
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index acaed36728..7cbe80a20b 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -5,6 +5,7 @@
#include <algorithm>
#include <cctype>
#include <cstdio>
+#include <functional>
#include <sstream>
#include <utility>
@@ -21,6 +22,7 @@
#include "cmsys/FStream.hxx"
+#include "cmCxxModuleMapper.h"
#include "cmDocumentationEntry.h"
#include "cmFortranParser.h"
#include "cmGeneratedFileStream.h"
@@ -2517,7 +2519,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
// Populate the module map with those provided by linked targets first.
for (std::string const& linked_target_dir : linked_target_dirs) {
std::string const ltmn =
- cmStrCat(linked_target_dir, "/", arg_lang, "Modules.json");
+ cmStrCat(linked_target_dir, '/', arg_lang, "Modules.json");
Json::Value ltm;
cmsys::ifstream ltmf(ltmn.c_str(), std::ios::in | std::ios::binary);
Json::Reader reader;
@@ -2534,11 +2536,20 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
}
}
- const char* module_ext = "";
- if (arg_modmapfmt == "gcc") {
- module_ext = ".gcm";
+ cm::optional<CxxModuleMapFormat> modmap_fmt;
+ if (arg_modmapfmt.empty()) {
+ // nothing to do.
+ } else if (arg_modmapfmt == "gcc") {
+ modmap_fmt = CxxModuleMapFormat::Gcc;
+ } else {
+ cmSystemTools::Error(
+ cmStrCat("-E cmake_ninja_dyndep does not understand the ", arg_modmapfmt,
+ " module map format"));
+ return false;
}
+ auto module_ext = CxxModuleMapExtension(modmap_fmt);
+
// Extend the module map with those provided by this target.
// We do this after loading the modules provided by linked targets
// in case we have one of the same name that must be preferred.
@@ -2555,7 +2566,8 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
}
} else {
// Assume the module file path matches the logical module name.
- std::string safe_logical_name = p.LogicalName;
+ std::string safe_logical_name =
+ p.LogicalName; // TODO: needs fixing for header units
cmSystemTools::ReplaceString(safe_logical_name, ":", "-");
mod = cmStrCat(module_dir, safe_logical_name, module_ext);
}
@@ -2568,6 +2580,20 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
ddf << "ninja_dyndep_version = 1.0\n";
{
+ CxxModuleLocations locs;
+ locs.RootDirectory = ".";
+ locs.PathForGenerator = [this](std::string const& path) -> std::string {
+ return this->ConvertToNinjaPath(path);
+ };
+ locs.BmiLocationForModule =
+ [&mod_files](std::string const& logical) -> cm::optional<std::string> {
+ auto m = mod_files.find(logical);
+ if (m != mod_files.end()) {
+ return m->second;
+ }
+ return {};
+ };
+
cmNinjaBuild build("dyndep");
build.Outputs.emplace_back("");
for (cmScanDepInfo const& object : objects) {
@@ -2589,46 +2615,14 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
build.Variables.emplace("restat", "1");
}
- if (arg_modmapfmt.empty()) {
- // nothing to do.
- } else {
- std::stringstream mm;
- if (arg_modmapfmt == "gcc") {
- // Documented in GCC's documentation. The format is a series of lines
- // with a module name and the associated filename separated by
- // spaces. The first line may use `$root` as the module name to
- // specify a "repository root". That is used to anchor any relative
- // paths present in the file (CMake should never generate any).
-
- // Write the root directory to use for module paths.
- mm << "$root .\n";
-
- for (auto const& l : object.Provides) {
- auto m = mod_files.find(l.LogicalName);
- if (m != mod_files.end()) {
- mm << l.LogicalName << " " << this->ConvertToNinjaPath(m->second)
- << "\n";
- }
- }
- for (auto const& r : object.Requires) {
- auto m = mod_files.find(r.LogicalName);
- if (m != mod_files.end()) {
- mm << r.LogicalName << " " << this->ConvertToNinjaPath(m->second)
- << "\n";
- }
- }
- } else {
- cmSystemTools::Error(
- cmStrCat("-E cmake_ninja_dyndep does not understand the ",
- arg_modmapfmt, " module map format"));
- return false;
- }
+ if (modmap_fmt) {
+ auto mm = CxxModuleMapContent(*modmap_fmt, locs, object);
// XXX(modmap): If changing this path construction, change
// `cmNinjaTargetGenerator::WriteObjectBuildStatements` to generate the
// corresponding file path.
cmGeneratedFileStream mmf(cmStrCat(object.PrimaryOutput, ".modmap"));
- mmf << mm.str();
+ mmf << mm;
}
this->WriteBuild(ddf, build);
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index f7f7317cdb..e53ae8ea91 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -395,12 +395,27 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution(
{
VisualStudioFolders.clear();
+ std::vector<std::string> configs =
+ root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
+
for (cmGeneratorTarget const* target : projectTargets) {
if (!this->IsInSolution(target)) {
continue;
}
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
cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
if (expath) {
diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h
index a55cf45c8b..288069c771 100644
--- a/Source/cmGlobalVisualStudio7Generator.h
+++ b/Source/cmGlobalVisualStudio7Generator.h
@@ -157,6 +157,8 @@ protected:
cmValue typeGuid,
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::set<std::string> IsPartOfDefaultBuild(
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index d5783efc72..5738799ee1 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -468,6 +468,10 @@ bool cmGlobalXCodeGenerator::Open(const std::string& bindir,
}
CFRelease(cfStr);
}
+#else
+ (void)bindir;
+ (void)projectName;
+ (void)dryRun;
#endif
return ret;
@@ -1372,6 +1376,18 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget(
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& deps = this->GetTargetDirectDepends(gtgt);
for (auto& d : deps) {
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 103d3ac9b9..1e1df792b5 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -21,6 +21,7 @@
#include "cmComputeLinkInformation.h"
#include "cmCustomCommand.h"
#include "cmCustomCommandGenerator.h"
+#include "cmFileSet.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
@@ -46,6 +47,7 @@
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
+#include "cmTarget.h"
#include "cmValue.h"
#include "cmake.h"
@@ -190,6 +192,16 @@ void cmMakefileTargetGenerator::CreateRuleFile()
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
// 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;
this->GeneratorTarget->GetObjectSources(objectSources,
this->GetConfigName());
@@ -314,6 +360,25 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
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()
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 3fac7f5dcd..ee065c4620 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -36,7 +36,6 @@
#include "cmRange.h"
#include "cmRulePlaceholderExpander.h"
#include "cmSourceFile.h"
-#include "cmStandardLevelResolver.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
@@ -153,17 +152,12 @@ std::string cmNinjaTargetGenerator::LanguageDyndepRule(
bool cmNinjaTargetGenerator::NeedCxxModuleSupport(
std::string const& lang, std::string const& config) const
{
- if (lang != "CXX") {
+ if (lang != "CXX"_s) {
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,
@@ -255,51 +249,53 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
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,
bool firstForConfig)
{
+ this->GeneratorTarget->CheckCxxModuleStatus(config);
+
// Write comments.
cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig));
this->GetImplFileStream(fileConfig)
@@ -1616,8 +1614,9 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang,
mod_dir = this->GeneratorTarget->GetFortranModuleDirectory(
this->Makefile->GetHomeOutputDirectory());
} else if (lang == "CXX") {
- mod_dir =
- cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory);
+ mod_dir = this->GetGlobalGenerator()->ExpandCFGIntDir(
+ cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory),
+ config);
}
if (mod_dir.empty()) {
mod_dir = this->Makefile->GetCurrentBinaryDirectory();
diff --git a/Source/cmScriptGenerator.cxx b/Source/cmScriptGenerator.cxx
index 166ee569b3..32f9becbdb 100644
--- a/Source/cmScriptGenerator.cxx
+++ b/Source/cmScriptGenerator.cxx
@@ -133,7 +133,7 @@ void cmScriptGenerator::GenerateScriptActionsOnce(std::ostream& os,
std::string config_test = this->CreateConfigTest(this->Configurations);
os << indent << "if(" << config_test << ")\n";
this->GenerateScriptActions(os, indent.Next());
- os << indent << "endif(" << config_test << ")\n";
+ os << indent << "endif()\n";
}
}
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 0e7c86077e..f62f35706e 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -272,6 +272,8 @@ public:
cmListFileBacktrace Backtrace;
FileSetType HeadersFileSets;
+ FileSetType CxxModulesFileSets;
+ FileSetType CxxModuleHeadersFileSets;
cmTargetInternals();
@@ -301,6 +303,19 @@ cmTargetInternals::cmTargetInternals()
"The default header set"_s, "Header set"_s,
FileSetEntries("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))
{
}
@@ -1371,11 +1386,32 @@ cmBTStringRange cmTarget::GetHeaderSetsEntries() const
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
{
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 {
#define MAKE_PROP(PROP) const std::string prop##PROP = #PROP
MAKE_PROP(C_STANDARD);
@@ -1635,6 +1671,12 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value)
} else if (this->impl->HeadersFileSets.WriteProperties(
this, this->impl.get(), prop, value, true)) {
/* 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 {
this->impl->Properties.SetProperty(prop, value);
}
@@ -1746,6 +1788,13 @@ void cmTarget::AppendProperty(const std::string& prop,
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR, prop + " property may not be appended.");
} 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)) {
/* Handled in the `if` condition. */
} else {
@@ -2254,6 +2303,17 @@ cmValue cmTarget::GetProperty(const std::string& prop) const
if (headers.first) {
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);
@@ -2531,6 +2591,11 @@ std::pair<cmFileSet*, bool> cmTarget::GetOrCreateFileSet(
auto bt = this->impl->Makefile->GetBacktrace();
if (type == this->impl->HeadersFileSets.TypeName) {
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);
@@ -2541,6 +2606,12 @@ std::string cmTarget::GetFileSetsPropertyName(const std::string& type)
if (type == "HEADERS") {
return "HEADER_SETS";
}
+ if (type == "CXX_MODULES") {
+ return "CXX_MODULE_SETS";
+ }
+ if (type == "CXX_MODULE_HEADER_UNITS") {
+ return "CXX_MODULE_HEADER_UNIT_SETS";
+ }
return "";
}
@@ -2549,6 +2620,12 @@ std::string cmTarget::GetInterfaceFileSetsPropertyName(const std::string& type)
if (type == "HEADERS") {
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 "";
}
@@ -2576,6 +2653,8 @@ std::vector<std::string> cmTarget::GetAllInterfaceFileSets() const
};
appendEntries(this->impl->HeadersFileSets.InterfaceEntries.Entries);
+ appendEntries(this->impl->CxxModulesFileSets.InterfaceEntries.Entries);
+ appendEntries(this->impl->CxxModuleHeadersFileSets.InterfaceEntries.Entries);
return result;
}
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 5ed018e83f..94d66885a7 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -275,8 +275,12 @@ public:
cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const;
cmBTStringRange GetHeaderSetsEntries() const;
+ cmBTStringRange GetCxxModuleSetsEntries() const;
+ cmBTStringRange GetCxxModuleHeaderSetsEntries() const;
cmBTStringRange GetInterfaceHeaderSetsEntries() const;
+ cmBTStringRange GetInterfaceCxxModuleSetsEntries() const;
+ cmBTStringRange GetInterfaceCxxModuleHeaderSetsEntries() const;
std::string ImportedGetFullPath(const std::string& config,
cmStateEnums::ArtifactType artifact) const;
diff --git a/Source/cmTargetSourcesCommand.cxx b/Source/cmTargetSourcesCommand.cxx
index b1367e1848..e4244a6857 100644
--- a/Source/cmTargetSourcesCommand.cxx
+++ b/Source/cmTargetSourcesCommand.cxx
@@ -9,6 +9,7 @@
#include <cmext/string_view>
#include "cmArgumentParser.h"
+#include "cmExperimental.h"
#include "cmFileSet.h"
#include "cmGeneratorExpression.h"
#include "cmListFileCache.h"
@@ -256,9 +257,30 @@ bool TargetSourcesImpl::HandleOneFileSet(
this->SetError("Must specify a TYPE when creating file set");
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()) {
@@ -294,7 +316,7 @@ bool TargetSourcesImpl::HandleOneFileSet(
if (!baseDirectories.empty()) {
fileSet.first->AddDirectoryEntry(
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)) {
auto interfaceDirectoriesGenex =
cmStrCat("$<BUILD_INTERFACE:", dir, ">");
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 6826ea9691..a7460e8fc4 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -347,6 +347,18 @@ std::ostream& cmVisualStudio10TargetGenerator::Elem::WriteString(
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->Managed = this->ProjectType == VsProjectType::csproj;
const std::string ProjectFileExtension =
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 5325a3a45d..bbf8ddc1fb 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -564,6 +564,9 @@ if(CMake_TEST_CUDA)
endif()
add_RunCMake_test(DependencyGraph -DCMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER})
+# Add C++ Module tests.
+add_RunCMake_test(CXXModules -DCMake_TEST_MODULE_COMPILATION=${CMake_TEST_MODULE_COMPILATION} -DCMake_TEST_MODULE_COMPILATION_RULES=${CMake_TEST_MODULE_COMPILATION_RULES})
+
# 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.)
# So, it has to be provided in the original build tree.
diff --git a/Tests/RunCMake/CXXModules/CMakeLists.txt b/Tests/RunCMake/CXXModules/CMakeLists.txt
new file mode 100644
index 0000000000..338a412e66
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-result.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-stderr.txt
new file mode 100644
index 0000000000..d573a02f61
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface.cmake b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface.cmake
new file mode 100644
index 0000000000..03ca17e238
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate-stderr.txt
new file mode 100644
index 0000000000..a7ac88ecee
--- /dev/null
+++ b/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.
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate.cmake b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate.cmake
new file mode 100644
index 0000000000..ebf9853b22
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic-stderr.txt
new file mode 100644
index 0000000000..a5b4edee78
--- /dev/null
+++ b/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.
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic.cmake b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic.cmake
new file mode 100644
index 0000000000..3dfccbb555
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterface-result.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterface-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/FileSetModulesInterface-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt
new file mode 100644
index 0000000000..81a35e8ef5
--- /dev/null
+++ b/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\)
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterface.cmake b/Tests/RunCMake/CXXModules/FileSetModulesInterface.cmake
new file mode 100644
index 0000000000..24cec3ec10
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt
new file mode 100644
index 0000000000..03e06cc99e
--- /dev/null
+++ b/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.
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake b/Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake
new file mode 100644
index 0000000000..ca1898237f
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt
new file mode 100644
index 0000000000..0c110c3a3b
--- /dev/null
+++ b/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.
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake b/Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake
new file mode 100644
index 0000000000..58de174252
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/NoCXX-result.txt b/Tests/RunCMake/CXXModules/NoCXX-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/NoCXX-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/NoCXX-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX-stderr.txt
new file mode 100644
index 0000000000..aa7f406fcd
--- /dev/null
+++ b/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.
diff --git a/Tests/RunCMake/CXXModules/NoCXX.cmake b/Tests/RunCMake/CXXModules/NoCXX.cmake
new file mode 100644
index 0000000000..3c46f9dc08
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20-result.txt b/Tests/RunCMake/CXXModules/NoCXX20-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/NoCXX20-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt
new file mode 100644
index 0000000000..95d73b1adc
--- /dev/null
+++ b/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.
diff --git a/Tests/RunCMake/CXXModules/NoCXX20.cmake b/Tests/RunCMake/CXXModules/NoCXX20.cmake
new file mode 100644
index 0000000000..d502f7cad9
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt
new file mode 100644
index 0000000000..5f90ec87c4
--- /dev/null
+++ b/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.
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake
new file mode 100644
index 0000000000..5f896f90d6
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/NoDyndepSupport-result.txt b/Tests/RunCMake/CXXModules/NoDyndepSupport-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/NoDyndepSupport-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt b/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt
new file mode 100644
index 0000000000..52f781fa3b
--- /dev/null
+++ b/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.
diff --git a/Tests/RunCMake/CXXModules/NoDyndepSupport.cmake b/Tests/RunCMake/CXXModules/NoDyndepSupport.cmake
new file mode 100644
index 0000000000..09544007f5
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-result.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-stderr.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-stderr.txt
new file mode 100644
index 0000000000..a93eb400ef
--- /dev/null
+++ b/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.
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits.cmake b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits.cmake
new file mode 100644
index 0000000000..af4ddacfab
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModules-result.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModules-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/NotCXXSourceModules-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt
new file mode 100644
index 0000000000..d341c1f33a
--- /dev/null
+++ b/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.
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake b/Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake
new file mode 100644
index 0000000000..f7a6060df9
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
new file mode 100644
index 0000000000..68fdcae37c
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
@@ -0,0 +1,120 @@
+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 ()
+
+# Actual compilation tests.
+if (NOT CMake_TEST_MODULE_COMPILATION)
+ return ()
+endif ()
+
+function (run_cxx_module_test directory)
+ set(test_name "${directory}")
+ if (NOT ARGN STREQUAL "")
+ list(POP_FRONT ARGN test_name)
+ endif ()
+
+ set(RunCMake_TEST_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/examples/${directory}")
+ set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/examples/${test_name}-build")
+
+ if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
+ set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug)
+ else ()
+ set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+ endif ()
+
+ set(RunCMake_TEST_OPTIONS
+ "-DCMake_TEST_MODULE_COMPILATION_RULES=${CMake_TEST_MODULE_COMPILATION_RULES}"
+ ${ARGN})
+ run_cmake("examples/${test_name}")
+ set(RunCMake_TEST_NO_CLEAN 1)
+ run_cmake_command("${test_name}-build" "${CMAKE_COMMAND}" --build . --config Debug)
+ run_cmake_command("${test_name}-test" "${CMAKE_CTEST_COMMAND}" -C Debug)
+endfunction ()
+
+string(REPLACE "," ";" CMake_TEST_MODULE_COMPILATION "${CMake_TEST_MODULE_COMPILATION}")
+
+# Tests which use named modules.
+if ("named" IN_LIST CMake_TEST_MODULE_COMPILATION)
+ run_cxx_module_test(simple)
+ run_cxx_module_test(library library-static -DBUILD_SHARED_LIBS=OFF)
+ run_cxx_module_test(generated)
+endif ()
+
+# Tests which use named modules in shared libraries.
+if ("shared" IN_LIST CMake_TEST_MODULE_COMPILATION)
+ run_cxx_module_test(library library-shared -DBUILD_SHARED_LIBS=ON)
+endif ()
+
+# Tests which use partitions.
+if ("partitions" IN_LIST CMake_TEST_MODULE_COMPILATION)
+ run_cxx_module_test(partitions)
+endif ()
+
+# Tests which use internal partitions.
+if ("internal_partitions" IN_LIST CMake_TEST_MODULE_COMPILATION)
+ run_cxx_module_test(internal-partitions)
+endif ()
diff --git a/Tests/RunCMake/CXXModules/compiler_introspection.cmake b/Tests/RunCMake/CXXModules/compiler_introspection.cmake
new file mode 100644
index 0000000000..7a2df3d35e
--- /dev/null
+++ b/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}")
diff --git a/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake b/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake
new file mode 100644
index 0000000000..91ad2082b7
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake
@@ -0,0 +1,18 @@
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "17be90bd-a850-44e0-be50-448de847d652")
+
+if (NOT EXISTS "${CMake_TEST_MODULE_COMPILATION_RULES}")
+ message(FATAL_ERROR
+ "The `CMake_TEST_MODULE_COMPILATION_RULES` file must be specified "
+ "for these tests to operate.")
+endif ()
+
+include("${CMake_TEST_MODULE_COMPILATION_RULES}")
+
+if (NOT CMake_TEST_CXXModules_UUID STREQUAL "a246741c-d067-4019-a8fb-3d16b0c9d1d3")
+ message(FATAL_ERROR
+ "The compilation rule file needs updated for changes in the test "
+ "suite. Please see the history for what needs to be updated.")
+endif ()
+
+include(CTest)
+enable_testing()
diff --git a/Tests/RunCMake/CXXModules/examples/generated-stderr.txt b/Tests/RunCMake/CXXModules/examples/generated-stderr.txt
new file mode 100644
index 0000000000..b9bbf34f28
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/generated-stderr.txt
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:12 \(target_sources\):
+ CMake's C\+\+ module support is experimental. It is meant only for
+ experimentation and feedback to CMake developers.
+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.
diff --git a/Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt
new file mode 100644
index 0000000000..73f7ff7760
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_generated CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/importable.cxx.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/importable.cxx"
+ COPYONLY)
+
+add_executable(generated)
+target_sources(generated
+ PRIVATE
+ main.cxx
+ PRIVATE
+ FILE_SET CXX_MODULES
+ BASE_DIRS
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/importable.cxx")
+target_compile_features(generated PUBLIC cxx_std_20)
+
+add_test(NAME generated COMMAND generated)
diff --git a/Tests/RunCMake/CXXModules/examples/generated/importable.cxx.in b/Tests/RunCMake/CXXModules/examples/generated/importable.cxx.in
new file mode 100644
index 0000000000..a9287d7fa5
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/generated/importable.cxx.in
@@ -0,0 +1,5 @@
+export module importable;
+
+export int from_import() {
+ return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/generated/main.cxx b/Tests/RunCMake/CXXModules/examples/generated/main.cxx
new file mode 100644
index 0000000000..feb38d22e5
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/generated/main.cxx
@@ -0,0 +1,6 @@
+import importable;
+
+int main(int argc, char* argv[])
+{
+ return from_import();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt b/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt
new file mode 100644
index 0000000000..4652aeced6
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\):
+ CMake's C\+\+ module support is experimental. It is meant only for
+ experimentation and feedback to CMake developers.
+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.
diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt
new file mode 100644
index 0000000000..f5e9d942d6
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt
@@ -0,0 +1,31 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_internal_partitions CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+include(GenerateExportHeader)
+
+add_library(internal-partitions)
+generate_export_header(internal-partitions)
+target_sources(internal-partitions
+ PUBLIC
+ FILE_SET HEADERS
+ BASE_DIRS
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/internal-partitions_export.h"
+ FILE_SET CXX_MODULES
+ BASE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ FILES
+ importable.cxx
+ partition.cxx)
+target_compile_features(internal-partitions PUBLIC cxx_std_20)
+
+add_executable(exe)
+target_link_libraries(exe PRIVATE internal-partitions)
+target_sources(exe
+ PRIVATE
+ main.cxx)
+
+add_test(NAME exe COMMAND exe)
diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx b/Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx
new file mode 100644
index 0000000000..8be521b183
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx
@@ -0,0 +1,9 @@
+export module importable;
+import importable : internal_partition;
+
+#include "internal-partitions_export.h"
+
+export INTERNAL_PARTITIONS_EXPORT int from_import()
+{
+ return from_partition();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/main.cxx b/Tests/RunCMake/CXXModules/examples/internal-partitions/main.cxx
new file mode 100644
index 0000000000..feb38d22e5
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/internal-partitions/main.cxx
@@ -0,0 +1,6 @@
+import importable;
+
+int main(int argc, char* argv[])
+{
+ return from_import();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/partition.cxx b/Tests/RunCMake/CXXModules/examples/internal-partitions/partition.cxx
new file mode 100644
index 0000000000..b15f53c5ae
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/internal-partitions/partition.cxx
@@ -0,0 +1,6 @@
+module importable : internal_partition;
+
+int from_partition()
+{
+ return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt b/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt
new file mode 100644
index 0000000000..4652aeced6
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\):
+ CMake's C\+\+ module support is experimental. It is meant only for
+ experimentation and feedback to CMake developers.
+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.
diff --git a/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt b/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt
new file mode 100644
index 0000000000..4652aeced6
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\):
+ CMake's C\+\+ module support is experimental. It is meant only for
+ experimentation and feedback to CMake developers.
+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.
diff --git a/Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt
new file mode 100644
index 0000000000..27fd94f3fa
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_library CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+include(GenerateExportHeader)
+
+add_library(library)
+generate_export_header(library)
+target_sources(library
+ PUBLIC
+ FILE_SET HEADERS
+ BASE_DIRS
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/library_export.h"
+ FILE_SET CXX_MODULES
+ BASE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ FILES
+ importable.cxx)
+target_compile_features(library PUBLIC cxx_std_20)
+
+add_executable(exe)
+target_link_libraries(exe PRIVATE library)
+target_sources(exe
+ PRIVATE
+ main.cxx)
+
+add_test(NAME exe COMMAND exe)
diff --git a/Tests/RunCMake/CXXModules/examples/library/importable.cxx b/Tests/RunCMake/CXXModules/examples/library/importable.cxx
new file mode 100644
index 0000000000..72ed0dfd32
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/library/importable.cxx
@@ -0,0 +1,8 @@
+export module importable;
+
+#include "library_export.h"
+
+export LIBRARY_EXPORT int from_import()
+{
+ return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/library/main.cxx b/Tests/RunCMake/CXXModules/examples/library/main.cxx
new file mode 100644
index 0000000000..feb38d22e5
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/library/main.cxx
@@ -0,0 +1,6 @@
+import importable;
+
+int main(int argc, char* argv[])
+{
+ return from_import();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt b/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt
new file mode 100644
index 0000000000..4652aeced6
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\):
+ CMake's C\+\+ module support is experimental. It is meant only for
+ experimentation and feedback to CMake developers.
+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.
diff --git a/Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt
new file mode 100644
index 0000000000..3a7b0d4a3b
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt
@@ -0,0 +1,31 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_partitions CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+include(GenerateExportHeader)
+
+add_library(partitions)
+generate_export_header(partitions)
+target_sources(partitions
+ PUBLIC
+ FILE_SET HEADERS
+ BASE_DIRS
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/partitions_export.h"
+ FILE_SET CXX_MODULES
+ BASE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ FILES
+ importable.cxx
+ partition.cxx)
+target_compile_features(partitions PUBLIC cxx_std_20)
+
+add_executable(exe)
+target_link_libraries(exe PRIVATE partitions)
+target_sources(exe
+ PRIVATE
+ main.cxx)
+
+add_test(NAME exe COMMAND exe)
diff --git a/Tests/RunCMake/CXXModules/examples/partitions/importable.cxx b/Tests/RunCMake/CXXModules/examples/partitions/importable.cxx
new file mode 100644
index 0000000000..1e81aafa1b
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/partitions/importable.cxx
@@ -0,0 +1,9 @@
+export module importable;
+export import importable : partition;
+
+#include "partitions_export.h"
+
+export PARTITIONS_EXPORT int from_import()
+{
+ return from_partition();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/partitions/main.cxx b/Tests/RunCMake/CXXModules/examples/partitions/main.cxx
new file mode 100644
index 0000000000..c5b78c9c52
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/partitions/main.cxx
@@ -0,0 +1,6 @@
+import importable;
+
+int main(int argc, char* argv[])
+{
+ return from_import() + from_partition();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/partitions/partition.cxx b/Tests/RunCMake/CXXModules/examples/partitions/partition.cxx
new file mode 100644
index 0000000000..a47a4fdfde
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/partitions/partition.cxx
@@ -0,0 +1,8 @@
+export module importable : partition;
+
+#include "partitions_export.h"
+
+export PARTITIONS_EXPORT int from_partition()
+{
+ return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/simple-stderr.txt b/Tests/RunCMake/CXXModules/examples/simple-stderr.txt
new file mode 100644
index 0000000000..5e4392abc9
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/simple-stderr.txt
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
+ CMake's C\+\+ module support is experimental. It is meant only for
+ experimentation and feedback to CMake developers.
+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.
diff --git a/Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt
new file mode 100644
index 0000000000..442e4256bc
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_simple CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_executable(simple)
+target_sources(simple
+ PRIVATE
+ main.cxx
+ PRIVATE
+ FILE_SET CXX_MODULES
+ BASE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ FILES
+ importable.cxx)
+target_compile_features(simple PUBLIC cxx_std_20)
+
+add_test(NAME simple COMMAND simple)
diff --git a/Tests/RunCMake/CXXModules/examples/simple/importable.cxx b/Tests/RunCMake/CXXModules/examples/simple/importable.cxx
new file mode 100644
index 0000000000..607680a07c
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/simple/importable.cxx
@@ -0,0 +1,6 @@
+export module importable;
+
+export int from_import()
+{
+ return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/simple/main.cxx b/Tests/RunCMake/CXXModules/examples/simple/main.cxx
new file mode 100644
index 0000000000..feb38d22e5
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/simple/main.cxx
@@ -0,0 +1,6 @@
+import importable;
+
+int main(int argc, char* argv[])
+{
+ return from_import();
+}
diff --git a/Tests/RunCMake/CXXModules/sources/c-anchor.c b/Tests/RunCMake/CXXModules/sources/c-anchor.c
new file mode 100644
index 0000000000..c782188925
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/sources/c-anchor.c
@@ -0,0 +1,4 @@
+int c_anchor()
+{
+ return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/sources/cxx-anchor.cxx b/Tests/RunCMake/CXXModules/sources/cxx-anchor.cxx
new file mode 100644
index 0000000000..9c94ec12bb
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/sources/cxx-anchor.cxx
@@ -0,0 +1,4 @@
+int cxx_anchor()
+{
+ return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/sources/module-header.h b/Tests/RunCMake/CXXModules/sources/module-header.h
new file mode 100644
index 0000000000..982617e5c6
--- /dev/null
+++ b/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
diff --git a/Tests/RunCMake/CXXModules/sources/module-impl.cxx b/Tests/RunCMake/CXXModules/sources/module-impl.cxx
new file mode 100644
index 0000000000..4718999160
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/sources/module-impl.cxx
@@ -0,0 +1,6 @@
+module M;
+
+int f()
+{
+ return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/sources/module-internal-part-impl.cxx b/Tests/RunCMake/CXXModules/sources/module-internal-part-impl.cxx
new file mode 100644
index 0000000000..be77b0d54a
--- /dev/null
+++ b/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;
+}
diff --git a/Tests/RunCMake/CXXModules/sources/module-internal-part.cxx b/Tests/RunCMake/CXXModules/sources/module-internal-part.cxx
new file mode 100644
index 0000000000..fa82afbdd3
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/sources/module-internal-part.cxx
@@ -0,0 +1,3 @@
+module M : internal_part;
+
+int i();
diff --git a/Tests/RunCMake/CXXModules/sources/module-part-impl.cxx b/Tests/RunCMake/CXXModules/sources/module-part-impl.cxx
new file mode 100644
index 0000000000..46d5d9f541
--- /dev/null
+++ b/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();
+}
diff --git a/Tests/RunCMake/CXXModules/sources/module-part.cxx b/Tests/RunCMake/CXXModules/sources/module-part.cxx
new file mode 100644
index 0000000000..137c16f29e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/sources/module-part.cxx
@@ -0,0 +1,3 @@
+export module M : part;
+
+int p();
diff --git a/Tests/RunCMake/CXXModules/sources/module-use.cxx b/Tests/RunCMake/CXXModules/sources/module-use.cxx
new file mode 100644
index 0000000000..2d060cdbde
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/sources/module-use.cxx
@@ -0,0 +1,6 @@
+import M;
+
+int main(int argc, char* argv[])
+{
+ return f() + p();
+}
diff --git a/Tests/RunCMake/CXXModules/sources/module.cxx b/Tests/RunCMake/CXXModules/sources/module.cxx
new file mode 100644
index 0000000000..a631354cf5
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/sources/module.cxx
@@ -0,0 +1,5 @@
+export module M;
+export import M : part;
+import M : internal_part;
+
+int f();
diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt
new file mode 100644
index 0000000000..042d67df6b
--- /dev/null
+++ b/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\)$
diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake
new file mode 100644
index 0000000000..2dbcee7f8d
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt
new file mode 100644
index 0000000000..a1b784fa2c
--- /dev/null
+++ b/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\)$
diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake
new file mode 100644
index 0000000000..024c2cbddc
--- /dev/null
+++ b/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)
diff --git a/Tests/RunCMake/target_sources/RunCMakeTest.cmake b/Tests/RunCMake/target_sources/RunCMakeTest.cmake
index e78ee9daba..135777b7c6 100644
--- a/Tests/RunCMake/target_sources/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_sources/RunCMakeTest.cmake
@@ -26,6 +26,8 @@ run_cmake(FileSetProperties)
run_cmake(FileSetNoType)
run_cmake(FileSetWrongType)
run_cmake(FileSetDefaultWrongType)
+run_cmake(FileSetWrongTypeExperimental)
+run_cmake(FileSetDefaultWrongTypeExperimental)
run_cmake(FileSetChangeScope)
run_cmake(FileSetChangeType)
run_cmake(FileSetWrongBaseDirs)
diff --git a/Tests/RunCMake/try_compile/CxxStandard-stderr.txt b/Tests/RunCMake/try_compile/CxxStandard-stderr.txt
index ec7245fdd4..cee1b4451f 100644
--- a/Tests/RunCMake/try_compile/CxxStandard-stderr.txt
+++ b/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'
-+
+
+(
+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\):
Failed to generate test project build system.
Call Stack \(most recent call first\):
diff --git a/bootstrap b/bootstrap
index 9a87413984..f7e1544a4d 100755
--- a/bootstrap
+++ b/bootstrap
@@ -328,6 +328,7 @@ CMAKE_CXX_SOURCES="\
cmCustomCommand \
cmCustomCommandGenerator \
cmCustomCommandLines \
+ cmCxxModuleMapper \
cmDefinePropertyCommand \
cmDefinitions \
cmDocumentationFormatter \
@@ -337,6 +338,7 @@ CMAKE_CXX_SOURCES="\
cmExecProgramCommand \
cmExecuteProcessCommand \
cmExpandedCommandArgument \
+ cmExperimental \
cmExportBuildFileGenerator \
cmExportFileGenerator \
cmExportInstallFileGenerator \