浏览代码

Merge topic 'cxx-module-metadata'

fa10dc6c22 Experimental/CXXModules: Implement EcoStd Module Metadata parser

Acked-by: Kitware Robot <[email protected]>
Tested-by: buildbot <[email protected]>
Merge-request: !11422
Brad King 1 月之前
父节点
当前提交
bb0216e0a2
共有 25 个文件被更改,包括 806 次插入467 次删除
  1. 5 2
      Modules/CMakeCXXCompiler.cmake.in
  2. 15 13
      Modules/CMakeDetermineCompilerSupport.cmake
  3. 10 40
      Modules/Compiler/CMakeCommonCompilerMacros.cmake
  4. 6 157
      Modules/Compiler/Clang-CXX-CXXImportStd.cmake
  5. 5 119
      Modules/Compiler/GNU-CXX-CXXImportStd.cmake
  6. 77 92
      Modules/Compiler/MSVC-CXX-CXXImportStd.cmake
  7. 2 0
      Source/CMakeLists.txt
  8. 460 0
      Source/cmCxxModuleMetadata.cxx
  9. 84 0
      Source/cmCxxModuleMetadata.h
  10. 91 26
      Source/cmGeneratorTarget.cxx
  11. 11 1
      Source/cmGlobalGenerator.cxx
  12. 1 0
      Tests/RunCMake/CXXModules/Inspect.cmake
  13. 3 4
      Tests/RunCMake/CXXModules/NoCXX23TargetRequired-stderr.txt
  14. 3 4
      Tests/RunCMake/CXXModules/NoCXX26TargetRequired-stderr.txt
  15. 1 1
      Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build-stderr.txt
  16. 1 1
      Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install-stderr.txt
  17. 1 1
      Tests/RunCMake/CXXModulesCompile/imp-std-no-std-prop-stderr.txt
  18. 1 1
      Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build-stderr.txt
  19. 1 1
      Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install-stderr.txt
  20. 1 1
      Tests/RunCMake/CXXModulesCompile/imp-std-stderr.txt
  21. 8 0
      Tests/RunCMake/CXXModulesCompile/imp-std-trans-exp-no-std-build-stderr.txt
  22. 8 0
      Tests/RunCMake/CXXModulesCompile/imp-std-trans-exp-no-std-install-stderr.txt
  23. 1 1
      Tests/RunCMake/CXXModulesCompile/imp-std-trans-not-in-exp-build-stderr.txt
  24. 1 1
      Tests/RunCMake/CXXModulesCompile/imp-std-trans-not-in-exp-install-stderr.txt
  25. 9 1
      Tests/RunCMake/property_init/CompileSources.cmake

+ 5 - 2
Modules/CMakeCXXCompiler.cmake.in

@@ -96,5 +96,8 @@ set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "@CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES@")
 set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@")
 set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@")
 set(CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR "@CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR@")
 set(CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR "@CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR@")
 
 
-set(CMAKE_CXX_COMPILER_IMPORT_STD "")
-@CMAKE_CXX_IMPORT_STD@
+set(CMAKE_CXX_COMPILER_IMPORT_STD "@CMAKE_CXX_COMPILER_IMPORT_STD@")
+set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE  "@CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE@")
+if(CMAKE_CXX_STDLIB_MODULES_JSON)
+  set(CMAKE_CXX_STDLIB_MODULES_JSON "@CMAKE_CXX_STDLIB_MODULES_JSON@")
+endif()

+ 15 - 13
Modules/CMakeDetermineCompilerSupport.cmake

@@ -105,18 +105,21 @@ function(cmake_determine_compiler_support lang)
       )
       )
     endif()
     endif()
 
 
-    # Create targets for use with `import std;` here.
-    set(CMAKE_CXX_IMPORT_STD "")
-    foreach (_cmake_import_std_version IN ITEMS 23 26)
-      if (CMAKE_CXX${_cmake_import_std_version}_COMPILE_FEATURES)
-        set(_cmake_cxx_import_std "")
-        cmake_create_cxx_import_std("${_cmake_import_std_version}" _cmake_cxx_import_std)
-        if (_cmake_cxx_import_std)
-          string(APPEND CMAKE_CXX_IMPORT_STD "### Imported target for C++${_cmake_import_std_version} standard library\n")
-          string(APPEND CMAKE_CXX_IMPORT_STD "${_cmake_cxx_import_std}\n\n")
-        endif ()
-      endif ()
-    endforeach ()
+    # Find the module metadata for import std
+    set(CMAKE_CXX_COMPILER_IMPORT_STD "")
+    cmake_cxx_find_modules_json()
+    foreach(_cmake_import_std_version IN ITEMS 23 26)
+      if(CMAKE_CXX${_cmake_import_std_version}_COMPILE_FEATURES)
+        # Modules JSON covers all versions, otherwise rely on toolchain targets
+        if(CMAKE_CXX_STDLIB_MODULES_JSON OR (TARGET "__CMAKE:CXX${_cmake_import_std_version}"))
+          list(APPEND CMAKE_CXX_COMPILER_IMPORT_STD ${_cmake_import_std_version})
+        endif()
+      endif()
+    endforeach()
+
+    set(CMAKE_CXX_COMPILER_IMPORT_STD ${CMAKE_CXX_COMPILER_IMPORT_STD} PARENT_SCOPE)
+    set(CMAKE_CXX_STDLIB_MODULES_JSON ${CMAKE_CXX_STDLIB_MODULES_JSON} PARENT_SCOPE)
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "${CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE}" PARENT_SCOPE)
 
 
     set(CMAKE_CXX_COMPILE_FEATURES ${CMAKE_CXX_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_CXX_COMPILE_FEATURES ${CMAKE_CXX_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_CXX98_COMPILE_FEATURES ${CMAKE_CXX98_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_CXX98_COMPILE_FEATURES ${CMAKE_CXX98_COMPILE_FEATURES} PARENT_SCOPE)
@@ -126,7 +129,6 @@ function(cmake_determine_compiler_support lang)
     set(CMAKE_CXX20_COMPILE_FEATURES ${CMAKE_CXX20_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_CXX20_COMPILE_FEATURES ${CMAKE_CXX20_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_CXX23_COMPILE_FEATURES ${CMAKE_CXX23_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_CXX23_COMPILE_FEATURES ${CMAKE_CXX23_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_CXX26_COMPILE_FEATURES ${CMAKE_CXX26_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_CXX26_COMPILE_FEATURES ${CMAKE_CXX26_COMPILE_FEATURES} PARENT_SCOPE)
-    set(CMAKE_CXX_IMPORT_STD ${CMAKE_CXX_IMPORT_STD} PARENT_SCOPE)
 
 
     message(CHECK_PASS "done")
     message(CHECK_PASS "done")
 
 

+ 10 - 40
Modules/Compiler/CMakeCommonCompilerMacros.cmake

@@ -202,43 +202,21 @@ macro(cmake_record_hip_compile_features)
   _has_compiler_features_hip(98)
   _has_compiler_features_hip(98)
 endmacro()
 endmacro()
 
 
-function(cmake_create_cxx_import_std std variable)
-  set(_cmake_supported_import_std_features
-    # Compilers support `import std` in C++20 as an extension. Skip
-    # for now.
-    # 20
-    23
-    26)
-  list(FIND _cmake_supported_import_std_features "${std}" _cmake_supported_import_std_idx)
-  if (_cmake_supported_import_std_idx EQUAL "-1")
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Unsupported C++ standard: C++${std}\")\n"
-      PARENT_SCOPE)
-    return ()
-  endif ()
-  # If the target exists, skip. A toolchain file may have provided it.
-  if (TARGET "__CMAKE::CXX${std}")
-    return ()
-  endif ()
+function(cmake_cxx_find_modules_json)
   # The generator must support imported C++ modules.
   # The generator must support imported C++ modules.
   if (NOT CMAKE_GENERATOR MATCHES "Ninja")
   if (NOT CMAKE_GENERATOR MATCHES "Ninja")
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Unsupported generator: ${CMAKE_GENERATOR}\")\n"
-      PARENT_SCOPE)
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Unsupported generator: ${CMAKE_GENERATOR}" PARENT_SCOPE)
     return ()
     return ()
   endif ()
   endif ()
+
   # Check if the compiler understands how to `import std;`.
   # Check if the compiler understands how to `import std;`.
   include("${CMAKE_ROOT}/Modules/Compiler/${CMAKE_CXX_COMPILER_ID}-CXX-CXXImportStd.cmake" OPTIONAL RESULT_VARIABLE _cmake_import_std_res)
   include("${CMAKE_ROOT}/Modules/Compiler/${CMAKE_CXX_COMPILER_ID}-CXX-CXXImportStd.cmake" OPTIONAL RESULT_VARIABLE _cmake_import_std_res)
   if (NOT _cmake_import_std_res)
   if (NOT _cmake_import_std_res)
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Toolchain does not support discovering `import std` support\")\n"
-      PARENT_SCOPE)
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Toolchain does not support discovering module metadata" PARENT_SCOPE)
     return ()
     return ()
   endif ()
   endif ()
-  if (NOT COMMAND _cmake_cxx_import_std)
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Toolchain does not provide `import std` discovery command\")\n"
-      PARENT_SCOPE)
+  if (NOT COMMAND _cmake_cxx_find_modules_json)
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Toolchain does not provide module metadata discovery command" PARENT_SCOPE)
     return ()
     return ()
   endif ()
   endif ()
 
 
@@ -249,19 +227,11 @@ function(cmake_create_cxx_import_std std variable)
     "CxxImportStd"
     "CxxImportStd"
     _cmake_supported_import_std_experimental)
     _cmake_supported_import_std_experimental)
   if (NOT _cmake_supported_import_std_experimental)
   if (NOT _cmake_supported_import_std_experimental)
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled (usually a `project()` call)\")\n"
-      PARENT_SCOPE)
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled (usually a `project()` call)" PARENT_SCOPE)
     return ()
     return ()
   endif ()
   endif ()
 
 
-  _cmake_cxx_import_std("${std}" target_definition)
-  string(CONCAT guarded_target_definition
-    "if (NOT TARGET \"__CMAKE::CXX${std}\")\n"
-    "${target_definition}"
-    "endif ()\n"
-    "if (TARGET \"__CMAKE::CXX${std}\")\n"
-    "  list(APPEND CMAKE_CXX_COMPILER_IMPORT_STD \"${std}\")\n"
-    "endif ()\n")
-  set("${variable}" "${guarded_target_definition}" PARENT_SCOPE)
+  _cmake_cxx_find_modules_json()
+  set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "${CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE}" PARENT_SCOPE)
+  set(CMAKE_CXX_STDLIB_MODULES_JSON "${CMAKE_CXX_STDLIB_MODULES_JSON}" PARENT_SCOPE)
 endfunction()
 endfunction()

+ 6 - 157
Modules/Compiler/Clang-CXX-CXXImportStd.cmake

@@ -1,18 +1,14 @@
-function (_cmake_cxx_import_std std variable)
+function (_cmake_cxx_find_modules_json)
   if (CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libc++")
   if (CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libc++")
     set(_clang_modules_json_impl "libc++")
     set(_clang_modules_json_impl "libc++")
   elseif (CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libstdc++")
   elseif (CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libstdc++")
     set(_clang_modules_json_impl "libstdc++")
     set(_clang_modules_json_impl "libstdc++")
   else ()
   else ()
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Only `libc++` and `libstdc++` are supported\")\n"
-      PARENT_SCOPE)
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Only `libc++` and `libstdc++` are supported" PARENT_SCOPE)
     return ()
     return ()
   endif ()
   endif ()
 
 
-  if (CMAKE_CXX_STDLIB_MODULES_JSON)
-    set(_clang_libcxx_modules_json_file "${CMAKE_CXX_STDLIB_MODULES_JSON}")
-  else ()
+  if (NOT DEFINED CMAKE_CXX_STDLIB_MODULES_JSON)
     execute_process(
     execute_process(
       COMMAND
       COMMAND
         "${CMAKE_CXX_COMPILER}"
         "${CMAKE_CXX_COMPILER}"
@@ -24,19 +20,10 @@ function (_cmake_cxx_import_std std variable)
       OUTPUT_STRIP_TRAILING_WHITESPACE
       OUTPUT_STRIP_TRAILING_WHITESPACE
       ERROR_STRIP_TRAILING_WHITESPACE)
       ERROR_STRIP_TRAILING_WHITESPACE)
     if (_clang_libcxx_modules_json_file_res)
     if (_clang_libcxx_modules_json_file_res)
-      set("${variable}"
-        "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Could not find `${_clang_modules_json_impl}.modules.json` resource\")\n"
-        PARENT_SCOPE)
+      set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Could not find `${_clang_modules_json_impl}.modules.json` resource" PARENT_SCOPE)
       return ()
       return ()
     endif ()
     endif ()
-  endif ()
-
-  # Without this file, we do not have modules installed.
-  if (NOT EXISTS "${_clang_libcxx_modules_json_file}")
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`${_clang_modules_json_impl}.modules.json` resource does not exist\")\n"
-      PARENT_SCOPE)
-    return ()
+    set(CMAKE_CXX_STDLIB_MODULES_JSON "${_clang_libcxx_modules_json_file}" PARENT_SCOPE)
   endif ()
   endif ()
 
 
   if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "18.1.2" AND
   if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "18.1.2" AND
@@ -44,145 +31,7 @@ function (_cmake_cxx_import_std std variable)
     # The original PR had a key spelling mismatch internally. Do not support it
     # The original PR had a key spelling mismatch internally. Do not support it
     # and instead require a release known to have the fix.
     # and instead require a release known to have the fix.
     # https://github.com/llvm/llvm-project/pull/83036
     # https://github.com/llvm/llvm-project/pull/83036
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"LLVM 18.1.2 is required for `${_clang_modules_json_impl}.modules.json` format fix\")\n"
-      PARENT_SCOPE)
-    return ()
-  endif ()
-
-  file(READ "${_clang_libcxx_modules_json_file}" _clang_libcxx_modules_json)
-  string(JSON _clang_modules_json_version GET "${_clang_libcxx_modules_json}" "version")
-  string(JSON _clang_modules_json_revision GET "${_clang_libcxx_modules_json}" "revision")
-  # Require version 1.
-  if (NOT _clang_modules_json_version EQUAL "1")
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`libc++.modules.json` version ${_clang_modules_json_version}.${_clang_modules_json_revision} is not recognized\")\n"
-      PARENT_SCOPE)
-    return ()
-  endif ()
-
-  string(JSON _clang_modules_json_nmodules LENGTH "${_clang_libcxx_modules_json}" "modules")
-  # Don't declare the target without any modules.
-  if (NOT _clang_modules_json_nmodules)
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`libc++.modules.json` does not list any available modules\")\n"
-      PARENT_SCOPE)
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "LLVM 18.1.2 is required for `${_clang_modules_json_impl}.modules.json` format fix" PARENT_SCOPE)
     return ()
     return ()
   endif ()
   endif ()
-
-  # Declare the target.
-  set(_clang_libcxx_target "")
-  # Clang 18 does not provide the module initializer for the `std` modules.
-  # Create a static library to hold these. Hope that Clang 19 can provide this,
-  # but never run the code.
-  string(APPEND _clang_libcxx_target
-    "add_library(__cmake_cxx${std} STATIC)\n")
-  string(APPEND _clang_libcxx_target
-    "target_sources(__cmake_cxx${std} INTERFACE \"$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,STATIC_LIBRARY>:$<TARGET_OBJECTS:__cmake_cxx${std}>>\")\n")
-  string(APPEND _clang_libcxx_target
-    "set_property(TARGET __cmake_cxx${std} PROPERTY EXCLUDE_FROM_ALL 1)\n")
-  string(APPEND _clang_libcxx_target
-    "set_property(TARGET __cmake_cxx${std} PROPERTY CXX_SCAN_FOR_MODULES 1)\n")
-  string(APPEND _clang_libcxx_target
-    "set_property(TARGET __cmake_cxx${std} PROPERTY CXX_MODULE_STD 0)\n")
-  string(APPEND _clang_libcxx_target
-    "target_compile_features(__cmake_cxx${std} PUBLIC cxx_std_${std})\n")
-
-  set(_clang_modules_is_stdlib 0)
-  set(_clang_modules_include_dirs_list "")
-  set(_clang_modules_module_paths "")
-  get_filename_component(_clang_modules_dir "${_clang_libcxx_modules_json_file}" DIRECTORY)
-
-  # Add module sources.
-  math(EXPR _clang_modules_json_nmodules_range "${_clang_modules_json_nmodules} - 1")
-  foreach (_clang_modules_json_modules_idx RANGE 0 "${_clang_modules_json_nmodules_range}")
-    string(JSON _clang_modules_json_module GET "${_clang_libcxx_modules_json}" "modules" "${_clang_modules_json_modules_idx}")
-
-    string(JSON _clang_modules_json_module_source GET "${_clang_modules_json_module}" "source-path")
-    string(JSON _clang_modules_json_module_is_stdlib GET "${_clang_modules_json_module}" "is-std-library")
-    string(JSON _clang_modules_json_module_local_arguments ERROR_VARIABLE _clang_modules_json_module_local_arguments_error GET "${_clang_modules_json_module}" "local-arguments")
-    string(JSON _clang_modules_json_module_nsystem_include_directories ERROR_VARIABLE _clang_modules_json_module_nsystem_include_directories_error LENGTH "${_clang_modules_json_module_local_arguments}" "system-include-directories")
-
-    if (_clang_modules_json_module_local_arguments_error)
-      set(_clang_modules_json_module_local_arguments "")
-    endif ()
-    if (_clang_modules_json_module_nsystem_include_directories_error)
-      set(_clang_modules_json_module_nsystem_include_directories 0)
-    endif ()
-    if (NOT IS_ABSOLUTE "${_clang_modules_json_module_source}")
-      string(PREPEND _clang_modules_json_module_source "${_clang_modules_dir}/")
-    endif ()
-    list(APPEND _clang_modules_module_paths
-      "${_clang_modules_json_module_source}")
-
-    if (_clang_modules_json_module_is_stdlib)
-      set(_clang_modules_is_stdlib 1)
-    endif ()
-
-    if (_clang_modules_json_module_nsystem_include_directories)
-      math(EXPR _clang_modules_json_module_nsystem_include_directories_range "${_clang_modules_json_module_nsystem_include_directories} - 1")
-      foreach (_clang_modules_json_modules_system_include_directories_idx RANGE 0 "${_clang_modules_json_module_nsystem_include_directories_range}")
-        string(JSON _clang_modules_json_module_system_include_directory GET "${_clang_modules_json_module_local_arguments}" "system-include-directories" "${_clang_modules_json_modules_system_include_directories_idx}")
-
-        if (NOT IS_ABSOLUTE "${_clang_modules_json_module_system_include_directory}")
-          string(PREPEND _clang_modules_json_module_system_include_directory "${_clang_modules_dir}/")
-        endif ()
-        list(APPEND _clang_modules_include_dirs_list
-          "${_clang_modules_json_module_system_include_directory}")
-      endforeach ()
-    endif ()
-  endforeach ()
-
-  # Split the paths into basedirs and module paths.
-  set(_clang_modules_base_dirs_list "")
-  set(_clang_modules_files "")
-  foreach (_clang_modules_module_path IN LISTS _clang_modules_module_paths)
-    get_filename_component(_clang_module_dir "${_clang_modules_module_path}" DIRECTORY)
-
-    list(APPEND _clang_modules_base_dirs_list
-      "${_clang_module_dir}")
-    string(APPEND _clang_modules_files
-      " \"${_clang_modules_module_path}\"")
-  endforeach ()
-  list(REMOVE_DUPLICATES _clang_modules_base_dirs_list)
-  set(_clang_modules_base_dirs "")
-  foreach (_clang_modules_base_dir IN LISTS _clang_modules_base_dirs_list)
-    string(APPEND _clang_modules_base_dirs
-      " \"${_clang_modules_base_dir}\"")
-  endforeach ()
-
-  # If we have a standard library module, suppress warnings about reserved
-  # module names.
-  if (_clang_modules_is_stdlib)
-    string(APPEND _clang_libcxx_target
-      "target_compile_options(__cmake_cxx${std} PRIVATE -Wno-reserved-module-identifier)\n")
-  endif ()
-
-  # Set up include directories.
-  list(REMOVE_DUPLICATES _clang_modules_include_dirs_list)
-  set(_clang_modules_include_dirs "")
-  foreach (_clang_modules_include_dir IN LISTS _clang_modules_include_dirs_list)
-    string(APPEND _clang_modules_include_dirs
-      " \"${_clang_modules_include_dir}\"")
-  endforeach ()
-  string(APPEND _clang_libcxx_target
-    "target_include_directories(__cmake_cxx${std} PRIVATE ${_clang_modules_include_dirs})\n")
-
-  # Create the file set for the modules.
-  string(APPEND _clang_libcxx_target
-    "target_sources(__cmake_cxx${std}
-  PUBLIC
-  FILE_SET std TYPE CXX_MODULES
-    BASE_DIRS ${_clang_modules_base_dirs}
-    FILES ${_clang_modules_files})\n")
-
-  # Wrap the `__cmake_cxx${std}` target in a check.
-  string(PREPEND _clang_libcxx_target
-    "if (NOT TARGET \"__cmake_cxx${std}\")\n")
-  string(APPEND _clang_libcxx_target
-    "endif ()\n")
-  string(APPEND _clang_libcxx_target
-    "add_library(__CMAKE::CXX${std} ALIAS __cmake_cxx${std})\n")
-
-  set("${variable}" "${_clang_libcxx_target}" PARENT_SCOPE)
 endfunction ()
 endfunction ()

+ 5 - 119
Modules/Compiler/GNU-CXX-CXXImportStd.cmake

@@ -1,14 +1,10 @@
-function (_cmake_cxx_import_std std variable)
+function (_cmake_cxx_find_modules_json)
   if (NOT CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libstdc++")
   if (NOT CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libstdc++")
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Only `libstdc++` is supported\")\n"
-      PARENT_SCOPE)
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Only `libstdc++` is supported" PARENT_SCOPE)
     return ()
     return ()
   endif ()
   endif ()
 
 
-  if (CMAKE_CXX_STDLIB_MODULES_JSON)
-    set(_gnu_libstdcxx_modules_json_file "${CMAKE_CXX_STDLIB_MODULES_JSON}")
-  else ()
+  if (NOT DEFINED CMAKE_CXX_STDLIB_MODULES_JSON)
     execute_process(
     execute_process(
       COMMAND
       COMMAND
         "${CMAKE_CXX_COMPILER}"
         "${CMAKE_CXX_COMPILER}"
@@ -20,119 +16,9 @@ function (_cmake_cxx_import_std std variable)
       OUTPUT_STRIP_TRAILING_WHITESPACE
       OUTPUT_STRIP_TRAILING_WHITESPACE
       ERROR_STRIP_TRAILING_WHITESPACE)
       ERROR_STRIP_TRAILING_WHITESPACE)
     if (_gnu_libstdcxx_modules_json_file_res)
     if (_gnu_libstdcxx_modules_json_file_res)
-      set("${variable}"
-        "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Could not find `libstdc++.modules.json` resource\")\n"
-        PARENT_SCOPE)
+      set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Could not find `libstdc++.modules.json` resource" PARENT_SCOPE)
       return ()
       return ()
     endif ()
     endif ()
+    set(CMAKE_CXX_STDLIB_MODULES_JSON "${_gnu_libstdcxx_modules_json_file}" PARENT_SCOPE)
   endif ()
   endif ()
-
-  # Without this file, we do not have modules installed.
-  if (NOT EXISTS "${_gnu_libstdcxx_modules_json_file}")
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`libstdc++.modules.json` resource does not exist\")\n"
-      PARENT_SCOPE)
-    return ()
-  endif ()
-
-  file(READ "${_gnu_libstdcxx_modules_json_file}" _gnu_libstdcxx_modules_json)
-  string(JSON _gnu_modules_json_version GET "${_gnu_libstdcxx_modules_json}" "version")
-  string(JSON _gnu_modules_json_revision GET "${_gnu_libstdcxx_modules_json}" "revision")
-  # Require version 1.
-  if (NOT _gnu_modules_json_version EQUAL "1")
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`libstdc++.modules.json` version ${_gnu_modules_json_version}.${_gnu_modules_json_revision} is not recognized\")\n"
-      PARENT_SCOPE)
-    return ()
-  endif ()
-
-  string(JSON _gnu_modules_json_nmodules LENGTH "${_gnu_libstdcxx_modules_json}" "modules")
-  # Don't declare the target without any modules.
-  if (NOT _gnu_modules_json_nmodules)
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`libstdc++.modules.json` does not list any available modules\")\n"
-      PARENT_SCOPE)
-    return ()
-  endif ()
-
-  # Declare the target.
-  set(_gnu_libstdcxx_target "")
-  string(APPEND _gnu_libstdcxx_target
-    "add_library(__CMAKE::CXX${std} IMPORTED INTERFACE)\n")
-  string(APPEND _gnu_libstdcxx_target
-    "target_compile_features(__CMAKE::CXX${std} INTERFACE cxx_std_${std})\n")
-
-  set(_gnu_modules_is_stdlib 0)
-  set(_gnu_modules_include_dirs_list "")
-  set(_gnu_modules_module_paths "")
-  get_filename_component(_gnu_modules_dir "${_gnu_libstdcxx_modules_json_file}" DIRECTORY)
-
-  # Add module sources.
-  math(EXPR _gnu_modules_json_nmodules_range "${_gnu_modules_json_nmodules} - 1")
-  foreach (_gnu_modules_json_modules_idx RANGE 0 "${_gnu_modules_json_nmodules_range}")
-    string(JSON _gnu_modules_json_module GET "${_gnu_libstdcxx_modules_json}" "modules" "${_gnu_modules_json_modules_idx}")
-
-    string(JSON _gnu_modules_json_module_source GET "${_gnu_modules_json_module}" "source-path")
-    string(JSON _gnu_modules_json_module_is_stdlib GET "${_gnu_modules_json_module}" "is-std-library")
-    string(JSON _gnu_modules_json_module_local_arguments ERROR_VARIABLE _gnu_modules_json_module_local_arguments_error GET "${_gnu_modules_json_module}" "local-arguments")
-    string(JSON _gnu_modules_json_module_nsystem_include_directories ERROR_VARIABLE _gnu_modules_json_module_nsystem_include_directories_error LENGTH "${_gnu_modules_json_module_local_arguments}" "system-include-directories")
-
-    if (_gnu_modules_json_module_local_arguments_error STREQUAL "NOTFOUND")
-      set(_gnu_modules_json_module_local_arguments "")
-    endif ()
-    if (_gnu_modules_json_module_nsystem_include_directories_error STREQUAL "NOTFOUND")
-      set(_gnu_modules_json_module_nsystem_include_directories 0)
-    endif ()
-
-    if (NOT IS_ABSOLUTE "${_gnu_modules_json_module_source}")
-      string(PREPEND _gnu_modules_json_module_source "${_gnu_modules_dir}/")
-    endif ()
-    list(APPEND _gnu_modules_module_paths
-      "${_gnu_modules_json_module_source}")
-
-    if (_gnu_modules_json_module_is_stdlib)
-      set(_gnu_modules_is_stdlib 1)
-    endif ()
-
-    if (_gnu_modules_json_module_nsystem_include_directories)
-      math(EXPR _gnu_modules_json_module_nsystem_include_directories_range "${_gnu_modules_json_module_nsystem_include_directories} - 1")
-      foreach (_gnu_modules_json_modules_system_include_directories_idx RANGE 0 "${_gnu_modules_json_module_nsystem_include_directories_range}")
-        string(JSON _gnu_modules_json_module_system_include_directory GET "${_gnu_modules_json_module_local_arguments}" "system-include-directories" "${_gnu_modules_json_modules_system_include_directories_idx}")
-
-        if (NOT IS_ABSOLUTE "${_gnu_modules_json_module_system_include_directory}")
-          string(PREPEND _gnu_modules_json_module_system_include_directory "${_gnu_modules_dir}/")
-        endif ()
-        list(APPEND _gnu_modules_include_dirs_list
-          "${_gnu_modules_json_module_system_include_directory}")
-      endforeach ()
-    endif ()
-  endforeach ()
-
-  # Split the paths into basedirs and module paths.
-  set(_gnu_modules_base_dirs_list "")
-  set(_gnu_modules_files "")
-  foreach (_gnu_modules_module_path IN LISTS _gnu_modules_module_paths)
-    get_filename_component(_gnu_module_dir "${_gnu_modules_module_path}" DIRECTORY)
-
-    list(APPEND _gnu_modules_base_dirs_list
-      "${_gnu_module_dir}")
-    string(APPEND _gnu_modules_files
-      " \"${_gnu_modules_module_path}\"")
-  endforeach ()
-  list(REMOVE_DUPLICATES _gnu_modules_base_dirs_list)
-  set(_gnu_modules_base_dirs "")
-  foreach (_gnu_modules_base_dir IN LISTS _gnu_modules_base_dirs_list)
-    string(APPEND _gnu_modules_base_dirs
-      " \"${_gnu_modules_base_dir}\"")
-  endforeach ()
-
-  # Create the file set for the modules.
-  string(APPEND _gnu_libstdcxx_target
-    "target_sources(__CMAKE::CXX${std}
-  INTERFACE
-  FILE_SET std TYPE CXX_MODULES
-    BASE_DIRS ${_gnu_modules_base_dirs}
-    FILES ${_gnu_modules_files})\n")
-
-  set("${variable}" "${_gnu_libstdcxx_target}" PARENT_SCOPE)
 endfunction ()
 endfunction ()

+ 77 - 92
Modules/Compiler/MSVC-CXX-CXXImportStd.cmake

@@ -1,119 +1,104 @@
-function (_cmake_cxx_import_std std variable)
-  if (CMAKE_CXX_STDLIB_MODULES_JSON)
-    set(_msvc_modules_json_file "${CMAKE_CXX_STDLIB_MODULES_JSON}")
-  else ()
-    find_file(_msvc_modules_json_file
-      NAME modules.json
-      HINTS
-        "$ENV{VCToolsInstallDir}/modules"
-      PATHS
-        "$ENV{INCLUDE}"
-        "${CMAKE_CXX_COMPILER}/../../.."
-        "${CMAKE_CXX_COMPILER}/../.."    # msvc-wine layout
-      PATH_SUFFIXES
-        ../modules
-      NO_CACHE)
-    # Without this file, we do not have modules installed.
-    if (NOT EXISTS "${_msvc_modules_json_file}")
-      set("${variable}"
-        "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Could not find `modules.json` resource\")\n"
-        PARENT_SCOPE)
-      return ()
-    endif ()
+function (_cmake_cxx_find_modules_json)
+  if (DEFINED CMAKE_CXX_STDLIB_MODULES_JSON)
+    return ()
+  endif ()
+
+  find_file(_msvc_modules_json_file
+    NAME modules.json
+    HINTS
+      "$ENV{VCToolsInstallDir}/modules"
+    PATHS
+      "$ENV{INCLUDE}"
+      "${CMAKE_CXX_COMPILER}/../../.."
+      "${CMAKE_CXX_COMPILER}/../.."    # msvc-wine layout
+    PATH_SUFFIXES
+      ../modules
+    NO_CACHE)
+
+  # Without this file, we do not have modules installed.
+  if (NOT EXISTS "${_msvc_modules_json_file}")
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Could not find `modules.json` resource" PARENT_SCOPE)
+    return ()
   endif ()
   endif ()
 
 
   file(READ "${_msvc_modules_json_file}" _msvc_modules_json)
   file(READ "${_msvc_modules_json_file}" _msvc_modules_json)
+  string(JSON _msvc_json_modules ERROR_VARIABLE _msvc_json_err GET "${_msvc_modules_json}" "modules")
+
+  # This is probably a conforming module metadata file, use it as is
+  if (_msvc_json_modules)
+    set(CMAKE_CXX_STDLIB_MODULES_JSON "${_msvc_modules_json_file}" PARENT_SCOPE)
+    return ()
+  endif ()
+
+  # Otherwise it's a Microsoft STL-style modules.json, check if we recognize it
   string(JSON _msvc_json_version GET "${_msvc_modules_json}" "version")
   string(JSON _msvc_json_version GET "${_msvc_modules_json}" "version")
   string(JSON _msvc_json_revision GET "${_msvc_modules_json}" "revision")
   string(JSON _msvc_json_revision GET "${_msvc_modules_json}" "revision")
   # Require version 1.
   # Require version 1.
   if (NOT _msvc_json_version EQUAL "1")
   if (NOT _msvc_json_version EQUAL "1")
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`modules.json` version ${_msvc_json_version}.${_msvc_json_revision} is not recognized\")\n"
-      PARENT_SCOPE)
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "`modules.json` version ${_msvc_json_version}.${_msvc_json_revision} is not recognized" PARENT_SCOPE)
     return ()
     return ()
   endif ()
   endif ()
 
 
   string(JSON _msvc_json_library GET "${_msvc_modules_json}" "library")
   string(JSON _msvc_json_library GET "${_msvc_modules_json}" "library")
   # Bail if we don't understand the library.
   # Bail if we don't understand the library.
   if (NOT _msvc_json_library STREQUAL "microsoft/STL")
   if (NOT _msvc_json_library STREQUAL "microsoft/STL")
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`modules.json` library `${_msvc_json_library}` is not recognized\")\n"
-      PARENT_SCOPE)
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "`modules.json` library `${_msvc_json_library}` is not recognized" PARENT_SCOPE)
     return ()
     return ()
   endif ()
   endif ()
 
 
-  string(JSON _msvc_json_nmodules LENGTH "${_msvc_modules_json}" "module-sources")
-  # Don't declare the target without any modules.
-  if (NOT _msvc_json_nmodules)
-    set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`modules.json` does not list any available modules\")\n"
-      PARENT_SCOPE)
+  string(JSON _msvc_json_sources_len LENGTH "${_msvc_modules_json}" "module-sources")
+  # Bail if there aren't any sources
+  if (NOT _msvc_json_sources_len)
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "`modules.json` does not list any available sources" PARENT_SCOPE)
     return ()
     return ()
   endif ()
   endif ()
 
 
-  # Declare the target.
-  set(_msvc_std_target "")
-  string(APPEND _msvc_std_target
-    "add_library(__cmake_cxx${std} STATIC)\n")
-  string(APPEND _msvc_std_target
-    "target_sources(__cmake_cxx${std} INTERFACE \"$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,STATIC_LIBRARY>:$<TARGET_OBJECTS:__cmake_cxx${std}>>\")\n")
-  string(APPEND _msvc_std_target
-    "set_property(TARGET __cmake_cxx${std} PROPERTY EXCLUDE_FROM_ALL 1)\n")
-  string(APPEND _msvc_std_target
-    "set_property(TARGET __cmake_cxx${std} PROPERTY CXX_SCAN_FOR_MODULES 1)\n")
-  string(APPEND _msvc_std_target
-    "set_property(TARGET __cmake_cxx${std} PROPERTY CXX_MODULE_STD 0)\n")
-  string(APPEND _msvc_std_target
-    "target_compile_features(__cmake_cxx${std} PUBLIC cxx_std_${std})\n")
+  set(_msvc_module_metadata [=[{
+    "version": 1,
+    "revision": 1,
+    "modules": []
+  }]=])
 
 
-  set(_msvc_modules_module_paths "")
-  get_filename_component(_msvc_modules_dir "${_msvc_modules_json_file}" DIRECTORY)
+  cmake_path(GET _msvc_modules_json_file PARENT_PATH _msvc_base_dir)
+  math(EXPR _msvc_json_sources_len "${_msvc_json_sources_len}-1")
+  foreach (idx RANGE ${_msvc_json_sources_len})
+    string(JSON _msvc_source GET "${_msvc_modules_json}" "module-sources" ${idx})
 
 
-  # Add module sources.
-  math(EXPR _msvc_modules_json_nmodules_range "${_msvc_json_nmodules} - 1")
-  foreach (_msvc_modules_json_modules_idx RANGE 0 "${_msvc_modules_json_nmodules_range}")
-    string(JSON _msvc_modules_json_module_source GET "${_msvc_modules_json}" "module-sources" "${_msvc_modules_json_modules_idx}")
-
-    if (NOT IS_ABSOLUTE "${_msvc_modules_json_module_source}")
-      string(PREPEND _msvc_modules_json_module_source "${_msvc_modules_dir}/")
+    set(_msvc_path ${_msvc_source})
+    cmake_path(IS_RELATIVE _msvc_path _msvc_is_rel)
+    if (_msvc_is_rel)
+      cmake_path(ABSOLUTE_PATH _msvc_path BASE_DIRECTORY "${_msvc_base_dir}")
     endif ()
     endif ()
-    list(APPEND _msvc_modules_module_paths
-      "${_msvc_modules_json_module_source}")
-  endforeach ()
-
-  # Split the paths into basedirs and module paths.
-  set(_msvc_modules_base_dirs_list "")
-  set(_msvc_modules_files "")
-  foreach (_msvc_modules_module_path IN LISTS _msvc_modules_module_paths)
-    get_filename_component(_msvc_module_dir "${_msvc_modules_module_path}" DIRECTORY)
 
 
-    list(APPEND _msvc_modules_base_dirs_list
-      "${_msvc_module_dir}")
-    string(APPEND _msvc_modules_files
-      " \"${_msvc_modules_module_path}\"")
-  endforeach ()
-  list(REMOVE_DUPLICATES _msvc_modules_base_dirs_list)
-  set(_msvc_modules_base_dirs "")
-  foreach (_msvc_modules_base_dir IN LISTS _msvc_modules_base_dirs_list)
-    string(APPEND _msvc_modules_base_dirs
-      " \"${_msvc_modules_base_dir}\"")
-  endforeach ()
+    if (_msvc_source MATCHES "std.ixx")
+      string(JSON _msvc_module_metadata
+        SET "${_msvc_module_metadata}" "modules" ${idx}
+        "{
+            \"logical-name\": \"std\",
+            \"source-path\": \"${_msvc_path}\",
+            \"is-std-library\": true
+        }"
+      )
+    elseif (_msvc_source MATCHES "std.compat.ixx")
+      string(JSON _msvc_module_metadata
+        SET "${_msvc_module_metadata}" "modules" ${idx}
+        "{
+            \"logical-name\": \"std.compat\",
+            \"source-path\": \"${_msvc_path}\",
+            \"is-std-library\": true
+        }"
+      )
+    endif ()
+  endforeach()
 
 
-  # Create the file set for the modules.
-  string(APPEND _msvc_std_target
-    "target_sources(__cmake_cxx${std}
-  PUBLIC
-  FILE_SET std TYPE CXX_MODULES
-    BASE_DIRS ${_msvc_modules_base_dirs}
-    FILES ${_msvc_modules_files})\n")
+  string(JSON _msvc_module_metadata_modules_len LENGTH ${_msvc_module_metadata} "modules")
 
 
-  # Wrap the `__cmake_cxx${std}` target in a check.
-  string(PREPEND _msvc_std_target
-    "if (NOT TARGET \"__cmake_cxx${std}\")\n")
-  string(APPEND _msvc_std_target
-    "endif ()\n")
-  string(APPEND _msvc_std_target
-    "add_library(__CMAKE::CXX${std} ALIAS __cmake_cxx${std})\n")
+  # Bail if we didn't recognize any of the modules
+  if (NOT _msvc_module_metadata_modules_len)
+    set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "`modules.json` did not contain any recognized sources (std.ixx, std.compat.ixx)" PARENT_SCOPE)
+    return ()
+  endif ()
 
 
-  set("${variable}" "${_msvc_std_target}" PARENT_SCOPE)
+  file(WRITE "${CMAKE_PLATFORM_INFO_DIR}/msvcstl.modules.json" "${_msvc_module_metadata}")
+  set(CMAKE_CXX_STDLIB_MODULES_JSON "${CMAKE_PLATFORM_INFO_DIR}/msvcstl.modules.json" PARENT_SCOPE)
 endfunction ()
 endfunction ()

+ 2 - 0
Source/CMakeLists.txt

@@ -165,6 +165,8 @@ add_library(
   cmCustomCommandTypes.h
   cmCustomCommandTypes.h
   cmCxxModuleMapper.cxx
   cmCxxModuleMapper.cxx
   cmCxxModuleMapper.h
   cmCxxModuleMapper.h
+  cmCxxModuleMetadata.cxx
+  cmCxxModuleMetadata.h
   cmCxxModuleUsageEffects.cxx
   cmCxxModuleUsageEffects.cxx
   cmCxxModuleUsageEffects.h
   cmCxxModuleUsageEffects.h
   cmDefinitions.cxx
   cmDefinitions.cxx

+ 460 - 0
Source/cmCxxModuleMetadata.cxx

@@ -0,0 +1,460 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file LICENSE.rst or https://cmake.org/licensing for details.  */
+
+#include "cmCxxModuleMetadata.h"
+
+#include <algorithm>
+#include <set>
+#include <string>
+#include <utility>
+
+#include <cmext/string_view>
+
+#include <cm3p/json/value.h>
+#include <cm3p/json/writer.h>
+
+#include "cmsys/FStream.hxx"
+
+#include "cmFileSet.h"
+#include "cmJSONState.h"
+#include "cmListFileCache.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmTarget.h"
+
+namespace {
+
+bool JsonIsStringArray(Json::Value const& v)
+{
+  return v.isArray() &&
+    std::all_of(v.begin(), v.end(),
+                [](Json::Value const& it) { return it.isString(); });
+}
+
+bool ParsePreprocessorDefine(Json::Value& dval,
+                             cmCxxModuleMetadata::PreprocessorDefineData& out,
+                             cmJSONState* state)
+{
+  if (!dval.isObject()) {
+    state->AddErrorAtValue("each entry in 'definitions' must be an object",
+                           &dval);
+    return false;
+  }
+
+  if (!dval.isMember("name") || !dval["name"].isString() ||
+      dval["name"].asString().empty()) {
+    state->AddErrorAtValue(
+      "preprocessor definition requires a non-empty 'name'", &dval["name"]);
+    return false;
+  }
+  out.Name = dval["name"].asString();
+
+  if (dval.isMember("value")) {
+    if (dval["value"].isString()) {
+      out.Value = dval["value"].asString();
+    } else if (!dval["value"].isNull()) {
+      state->AddErrorAtValue(
+        "'value' in preprocessor definition must be string or null",
+        &dval["value"]);
+      return false;
+    }
+  }
+
+  if (dval.isMember("undef")) {
+    if (!dval["undef"].isBool()) {
+      state->AddErrorAtValue(
+        "'undef' in preprocessor definition must be boolean", &dval["undef"]);
+      return false;
+    }
+    out.Undef = dval["undef"].asBool();
+  }
+
+  if (dval.isMember("vendor")) {
+    out.Vendor = std::move(dval["vendor"]);
+  }
+
+  return true;
+}
+
+bool ParseLocalArguments(Json::Value& lav,
+                         cmCxxModuleMetadata::LocalArgumentsData& out,
+                         cmJSONState* state)
+{
+  if (!lav.isObject()) {
+    state->AddErrorAtValue("'local-arguments' must be an object", &lav);
+    return false;
+  }
+
+  if (lav.isMember("include-directories")) {
+    if (!JsonIsStringArray(lav["include-directories"])) {
+      state->AddErrorAtValue(
+        "'include-directories' must be an array of strings",
+        &lav["include-directories"]);
+      return false;
+    }
+    for (auto const& s : lav["include-directories"]) {
+      out.IncludeDirectories.push_back(s.asString());
+    }
+  }
+
+  if (lav.isMember("system-include-directories")) {
+    if (!JsonIsStringArray(lav["system-include-directories"])) {
+      state->AddErrorAtValue(
+        "'system-include-directories' must be an array of strings",
+        &lav["system-include-directories"]);
+      return false;
+    }
+    for (auto const& s : lav["system-include-directories"]) {
+      out.SystemIncludeDirectories.push_back(s.asString());
+    }
+  }
+
+  if (lav.isMember("definitions")) {
+    if (!lav["definitions"].isArray()) {
+      state->AddErrorAtValue("'definitions' must be an array",
+                             &lav["definitions"]);
+      return false;
+    }
+    for (Json::Value& dval : lav["definitions"]) {
+      out.Definitions.emplace_back();
+      if (!ParsePreprocessorDefine(dval, out.Definitions.back(), state)) {
+        return false;
+      }
+    }
+  }
+
+  if (lav.isMember("vendor")) {
+    out.Vendor = std::move(lav["vendor"]);
+  }
+
+  return true;
+}
+
+bool ParseModule(Json::Value& mval, cmCxxModuleMetadata::ModuleData& mod,
+                 cmJSONState* state)
+{
+  if (!mval.isObject()) {
+    state->AddErrorAtValue("each entry in 'modules' must be an object", &mval);
+    return false;
+  }
+
+  if (!mval.isMember("logical-name") || !mval["logical-name"].isString() ||
+      mval["logical-name"].asString().empty()) {
+    state->AddErrorAtValue(
+      "module entries require a non-empty 'logical-name' string",
+      &mval["logical-name"]);
+    return false;
+  }
+  mod.LogicalName = mval["logical-name"].asString();
+
+  if (!mval.isMember("source-path") || !mval["source-path"].isString() ||
+      mval["source-path"].asString().empty()) {
+    state->AddErrorAtValue(
+      "module entries require a non-empty 'source-path' string",
+      &mval["source-path"]);
+    return false;
+  }
+  mod.SourcePath = mval["source-path"].asString();
+
+  if (mval.isMember("is-interface")) {
+    if (!mval["is-interface"].isBool()) {
+      state->AddErrorAtValue("'is-interface' must be boolean",
+                             &mval["is-interface"]);
+      return false;
+    }
+    mod.IsInterface = mval["is-interface"].asBool();
+  } else {
+    mod.IsInterface = true;
+  }
+
+  if (mval.isMember("is-std-library")) {
+    if (!mval["is-std-library"].isBool()) {
+      state->AddErrorAtValue("'is-std-library' must be boolean",
+                             &mval["is-std-library"]);
+      return false;
+    }
+    mod.IsStdLibrary = mval["is-std-library"].asBool();
+  } else {
+    mod.IsStdLibrary = false;
+  }
+
+  if (mval.isMember("local-arguments")) {
+    mod.LocalArguments.emplace();
+    if (!ParseLocalArguments(mval["local-arguments"], *mod.LocalArguments,
+                             state)) {
+      return false;
+    }
+  }
+
+  if (mval.isMember("vendor")) {
+    mod.Vendor = std::move(mval["vendor"]);
+  }
+
+  return true;
+}
+
+bool ParseRoot(Json::Value& root, cmCxxModuleMetadata& meta,
+               cmJSONState* state)
+{
+  if (!root.isMember("version") || !root["version"].isInt()) {
+    state->AddErrorAtValue(
+      "Top-level member 'version' is required and must be an integer", &root);
+    return false;
+  }
+  meta.Version = root["version"].asInt();
+
+  if (root.isMember("revision")) {
+    if (!root["revision"].isInt()) {
+      state->AddErrorAtValue("'revision' must be an integer",
+                             &root["revision"]);
+      return false;
+    }
+    meta.Revision = root["revision"].asInt();
+  }
+
+  if (root.isMember("modules")) {
+    if (!root["modules"].isArray()) {
+      state->AddErrorAtValue("'modules' must be an array", &root["modules"]);
+      return false;
+    }
+    for (Json::Value& mval : root["modules"]) {
+      meta.Modules.emplace_back();
+      if (!ParseModule(mval, meta.Modules.back(), state)) {
+        return false;
+      }
+    }
+  }
+
+  for (std::string& key : root.getMemberNames()) {
+    if (key == "version" || key == "revision" || key == "modules") {
+      continue;
+    }
+    meta.Extensions.emplace(std::move(key), std::move(root[key]));
+  }
+
+  return true;
+}
+
+} // namespace
+
+cmCxxModuleMetadata::ParseResult cmCxxModuleMetadata::LoadFromFile(
+  std::string const& path)
+{
+  ParseResult res;
+
+  Json::Value root;
+  cmJSONState parseState(path, &root);
+  if (!parseState.errors.empty()) {
+    res.Error = parseState.GetErrorMessage();
+    return res;
+  }
+
+  cmCxxModuleMetadata meta;
+  if (!ParseRoot(root, meta, &parseState)) {
+    res.Error = parseState.GetErrorMessage();
+    return res;
+  }
+
+  meta.MetadataFilePath = path;
+  res.Meta = std::move(meta);
+  return res;
+}
+
+namespace {
+
+Json::Value SerializePreprocessorDefine(
+  cmCxxModuleMetadata::PreprocessorDefineData const& d)
+{
+  Json::Value dv(Json::objectValue);
+  dv["name"] = d.Name;
+  if (d.Value) {
+    dv["value"] = *d.Value;
+  } else {
+    dv["value"] = Json::Value::null;
+  }
+  dv["undef"] = d.Undef;
+  if (d.Vendor) {
+    dv["vendor"] = *d.Vendor;
+  }
+  return dv;
+}
+
+Json::Value SerializeLocalArguments(
+  cmCxxModuleMetadata::LocalArgumentsData const& la)
+{
+  Json::Value lav(Json::objectValue);
+
+  if (!la.IncludeDirectories.empty()) {
+    Json::Value& inc = lav["include-directories"] = Json::arrayValue;
+    for (auto const& s : la.IncludeDirectories) {
+      inc.append(s);
+    }
+  }
+
+  if (!la.SystemIncludeDirectories.empty()) {
+    Json::Value& sinc = lav["system-include-directories"] = Json::arrayValue;
+    for (auto const& s : la.SystemIncludeDirectories) {
+      sinc.append(s);
+    }
+  }
+
+  if (!la.Definitions.empty()) {
+    Json::Value& defs = lav["definitions"] = Json::arrayValue;
+    for (auto const& d : la.Definitions) {
+      defs.append(SerializePreprocessorDefine(d));
+    }
+  }
+
+  if (la.Vendor) {
+    lav["vendor"] = *la.Vendor;
+  }
+
+  return lav;
+}
+
+Json::Value SerializeModule(cmCxxModuleMetadata::ModuleData const& m)
+{
+  Json::Value mv(Json::objectValue);
+  mv["logical-name"] = m.LogicalName;
+  mv["source-path"] = m.SourcePath;
+  mv["is-interface"] = m.IsInterface;
+  mv["is-std-library"] = m.IsStdLibrary;
+
+  if (m.LocalArguments) {
+    mv["local-arguments"] = SerializeLocalArguments(*m.LocalArguments);
+  }
+
+  if (m.Vendor) {
+    mv["vendor"] = *m.Vendor;
+  }
+
+  return mv;
+}
+
+} // namespace
+
+Json::Value cmCxxModuleMetadata::ToJsonValue(cmCxxModuleMetadata const& meta)
+{
+  Json::Value root(Json::objectValue);
+
+  root["version"] = meta.Version;
+  root["revision"] = meta.Revision;
+
+  Json::Value& modules = root["modules"] = Json::arrayValue;
+  for (auto const& m : meta.Modules) {
+    modules.append(SerializeModule(m));
+  }
+
+  for (auto const& kv : meta.Extensions) {
+    root[kv.first] = kv.second;
+  }
+
+  return root;
+}
+
+cmCxxModuleMetadata::SaveResult cmCxxModuleMetadata::SaveToFile(
+  std::string const& path, cmCxxModuleMetadata const& meta)
+{
+  SaveResult st;
+
+  cmsys::ofstream ofs(path.c_str());
+  if (!ofs.is_open()) {
+    st.Error = cmStrCat("Unable to open file for writing: "_s, path);
+    return st;
+  }
+
+  Json::StreamWriterBuilder wbuilder;
+  wbuilder["indentation"] = "  ";
+  ofs << Json::writeString(wbuilder, ToJsonValue(meta));
+
+  if (!ofs.good()) {
+    st.Error = cmStrCat("Write failed for file: "_s, path);
+    return st;
+  }
+
+  st.Ok = true;
+  return st;
+}
+
+void cmCxxModuleMetadata::PopulateTarget(
+  cmTarget& target, cmCxxModuleMetadata const& meta,
+  std::vector<std::string> const& configs)
+{
+  std::vector<cm::string_view> allIncludeDirectories;
+  std::vector<std::string> allCompileDefinitions;
+  std::set<std::string> baseDirs;
+
+  std::string metadataDir =
+    cmSystemTools::GetFilenamePath(meta.MetadataFilePath);
+
+  auto fileSet = target.GetOrCreateFileSet("CXX_MODULES", "CXX_MODULES",
+                                           cmFileSetVisibility::Interface);
+
+  for (auto const& module : meta.Modules) {
+    std::string sourcePath = module.SourcePath;
+    if (!cmSystemTools::FileIsFullPath(sourcePath)) {
+      sourcePath = cmStrCat(metadataDir, '/', sourcePath);
+    }
+
+    // Module metadata files can reference files in different roots,
+    // just use the immediate parent directory as a base directory
+    baseDirs.insert(cmSystemTools::GetFilenamePath(sourcePath));
+
+    fileSet.first->AddFileEntry(sourcePath);
+
+    if (module.LocalArguments) {
+      for (auto const& incDir : module.LocalArguments->IncludeDirectories) {
+        allIncludeDirectories.push_back(incDir);
+      }
+      for (auto const& sysIncDir :
+           module.LocalArguments->SystemIncludeDirectories) {
+        allIncludeDirectories.push_back(sysIncDir);
+      }
+
+      for (auto const& def : module.LocalArguments->Definitions) {
+        if (!def.Undef) {
+          if (def.Value) {
+            allCompileDefinitions.push_back(
+              cmStrCat(def.Name, "="_s, *def.Value));
+          } else {
+            allCompileDefinitions.push_back(def.Name);
+          }
+        }
+      }
+    }
+  }
+
+  for (auto const& baseDir : baseDirs) {
+    fileSet.first->AddDirectoryEntry(baseDir);
+  }
+
+  if (!allIncludeDirectories.empty()) {
+    target.SetProperty("IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES",
+                       cmJoin(allIncludeDirectories, ";"));
+  }
+
+  if (!allCompileDefinitions.empty()) {
+    target.SetProperty("IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS",
+                       cmJoin(allCompileDefinitions, ";"));
+  }
+
+  for (auto const& config : configs) {
+    std::vector<std::string> moduleList;
+    for (auto const& module : meta.Modules) {
+      if (module.IsInterface) {
+        std::string sourcePath = module.SourcePath;
+        if (!cmSystemTools::FileIsFullPath(sourcePath)) {
+          sourcePath = cmStrCat(metadataDir, '/', sourcePath);
+        }
+        moduleList.push_back(cmStrCat(module.LogicalName, "="_s, sourcePath));
+      }
+    }
+
+    if (!moduleList.empty()) {
+      std::string upperConfig = cmSystemTools::UpperCase(config);
+      std::string propertyName =
+        cmStrCat("IMPORTED_CXX_MODULES_"_s, upperConfig);
+      target.SetProperty(propertyName, cmJoin(moduleList, ";"));
+    }
+  }
+}

+ 84 - 0
Source/cmCxxModuleMetadata.h

@@ -0,0 +1,84 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file LICENSE.rst or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <cm/optional>
+#include <cm/string_view>
+
+#include <cm3p/json/value.h>
+
+class cmTarget;
+
+class cmCxxModuleMetadata
+{
+public:
+  struct PreprocessorDefineData
+  {
+    std::string Name;
+    cm::optional<std::string> Value;
+    bool Undef = false;
+    cm::optional<Json::Value> Vendor;
+  };
+
+  struct LocalArgumentsData
+  {
+    std::vector<std::string> IncludeDirectories;
+    std::vector<std::string> SystemIncludeDirectories;
+    std::vector<PreprocessorDefineData> Definitions;
+    cm::optional<Json::Value> Vendor;
+  };
+
+  struct ModuleData
+  {
+    std::string LogicalName;
+    std::string SourcePath;
+    bool IsInterface = true;
+    bool IsStdLibrary = false;
+    cm::optional<LocalArgumentsData> LocalArguments;
+    cm::optional<Json::Value> Vendor;
+  };
+
+  int Version = 0;
+  int Revision = 0;
+  std::vector<ModuleData> Modules;
+  std::string MetadataFilePath;
+
+  std::unordered_map<std::string, Json::Value> Extensions;
+
+  struct ParseResult;
+
+  struct SaveResult
+  {
+    bool Ok = false;
+    std::string Error;
+    explicit operator bool() const { return Ok; }
+  };
+
+  static ParseResult LoadFromFile(std::string const& path);
+  static Json::Value ToJsonValue(cmCxxModuleMetadata const& meta);
+  static SaveResult SaveToFile(std::string const& path,
+                               cmCxxModuleMetadata const& meta);
+  static void PopulateTarget(cmTarget& target, cmCxxModuleMetadata const& meta,
+                             std::vector<std::string> const& configs);
+  static void PopulateTarget(cmTarget& target, cmCxxModuleMetadata const& meta,
+                             cm::string_view config)
+  {
+    PopulateTarget(target, meta,
+                   std::vector<std::string>{ std::string(config) });
+  }
+  static void PopulateTarget(cmTarget& target, cmCxxModuleMetadata const& meta)
+  {
+    PopulateTarget(target, meta, "NOCONFIG");
+  }
+};
+
+struct cmCxxModuleMetadata::ParseResult
+{
+  cm::optional<cmCxxModuleMetadata> Meta;
+  std::string Error;
+  explicit operator bool() const { return static_cast<bool>(Meta); }
+};

+ 91 - 26
Source/cmGeneratorTarget.cxx

@@ -22,6 +22,7 @@
 #include "cmAlgorithms.h"
 #include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h" // IWYU pragma: keep
 #include "cmComputeLinkInformation.h" // IWYU pragma: keep
 #include "cmCryptoHash.h"
 #include "cmCryptoHash.h"
+#include "cmCxxModuleMetadata.h"
 #include "cmCxxModuleUsageEffects.h"
 #include "cmCxxModuleUsageEffects.h"
 #include "cmExperimental.h"
 #include "cmExperimental.h"
 #include "cmFileSet.h"
 #include "cmFileSet.h"
@@ -5126,6 +5127,70 @@ bool cmGeneratorTarget::IsNullImpliedByLinkLibraries(
   return cm::contains(this->LinkImplicitNullProperties, p);
   return cm::contains(this->LinkImplicitNullProperties, p);
 }
 }
 
 
+namespace {
+bool CreateCxxStdlibTarget(cmMakefile* makefile, cmLocalGenerator* lg,
+                           std::string const& targetName,
+                           std::string const& cxxTargetName,
+                           std::string const& stdLevel,
+                           std::vector<std::string> const& configs)
+{
+#ifndef CMAKE_BOOTSTRAP
+
+  static cm::optional<cmCxxModuleMetadata> metadata;
+
+  // Load metadata only when we need to create a target
+  if (!metadata) {
+    auto errorMessage =
+      makefile->GetDefinition("CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE");
+    if (!errorMessage.IsEmpty()) {
+      makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat(R"(The "CXX_MODULE_STD" property on target ")", targetName,
+                 "\" requires toolchain support, but it was not provided.  "
+                 "Reason:\n  ",
+                 *errorMessage));
+      return false;
+    }
+
+    auto metadataPath =
+      makefile->GetDefinition("CMAKE_CXX_STDLIB_MODULES_JSON");
+    if (metadataPath.IsEmpty()) {
+      makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat(
+          R"("The "CXX_MODULE_STD" property on target ")", targetName,
+          "\" requires CMAKE_CXX_STDLIB_MODULES_JSON be set, but it was not "
+          "provided by the toolchain."));
+      return false;
+    }
+
+    auto parseResult = cmCxxModuleMetadata::LoadFromFile(*metadataPath);
+    if (!parseResult) {
+      makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Failed to load C++ standard library modules metadata "
+                 "from \"",
+                 *metadataPath, "\": ", parseResult.Error));
+      return false;
+    }
+
+    metadata = std::move(*parseResult.Meta);
+  }
+
+  auto* stdlibTgt = makefile->AddImportedTarget(
+    cxxTargetName, cmStateEnums::INTERFACE_LIBRARY, true);
+  cmCxxModuleMetadata::PopulateTarget(*stdlibTgt, *metadata, configs);
+  stdlibTgt->AppendProperty("IMPORTED_CXX_MODULES_COMPILE_FEATURES",
+                            cmStrCat("cxx_std_", stdLevel));
+
+  lg->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(stdlibTgt, lg));
+
+#endif // CMAKE_BOOTSTRAP
+
+  return true;
+}
+} // namespace
+
 bool cmGeneratorTarget::ApplyCXXStdTargets()
 bool cmGeneratorTarget::ApplyCXXStdTargets()
 {
 {
   cmStandardLevelResolver standardResolver(this->Makefile);
   cmStandardLevelResolver standardResolver(this->Makefile);
@@ -5207,35 +5272,33 @@ bool cmGeneratorTarget::ApplyCXXStdTargets()
 
 
     auto const stdLevel =
     auto const stdLevel =
       standardResolver.GetLevelString("CXX", *explicitLevel);
       standardResolver.GetLevelString("CXX", *explicitLevel);
-    auto const targetName = cmStrCat("__CMAKE::CXX", stdLevel);
-    if (!this->Makefile->FindTargetToUse(targetName)) {
-      auto basicReason = this->Makefile->GetDefinition(cmStrCat(
-        "CMAKE_CXX", stdLevel, "_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE"));
-      std::string reason;
-      if (!basicReason.IsEmpty()) {
-        reason = cmStrCat("  Reason:\n  ", basicReason);
-      }
-      this->Makefile->IssueMessage(
-        MessageType::FATAL_ERROR,
-        cmStrCat(R"(The "CXX_MODULE_STD" property on the target ")",
-                 this->GetName(), "\" requires that the \"", targetName,
-                 "\" target exist, but it was not provided by the toolchain.",
-                 reason));
-      break;
-    }
-
-    // Check the experimental feature here as well. A toolchain may have
-    // provided the target and skipped the check in the toolchain preparation
-    // logic.
-    if (!cmExperimental::HasSupportEnabled(
-          *this->Makefile, cmExperimental::Feature::CxxImportStd)) {
-      break;
+    auto const cxxTargetName = cmStrCat("__CMAKE::CXX", stdLevel);
+
+    // Create the __CMAKE::CXX## IMPORTED interface target if it doesn't
+    // already exist
+    if (!this->Makefile->FindTargetToUse(cxxTargetName) &&
+        !CreateCxxStdlibTarget(this->Makefile, this->LocalGenerator,
+                               this->GetName(), cxxTargetName, stdLevel,
+                               configs)) {
+      return false;
     }
     }
 
 
     this->Target->AppendProperty(
     this->Target->AppendProperty(
       "LINK_LIBRARIES",
       "LINK_LIBRARIES",
-      cmStrCat("$<BUILD_LOCAL_INTERFACE:$<$<CONFIG:", config, ">:", targetName,
-               ">>"));
+      cmStrCat("$<BUILD_LOCAL_INTERFACE:$<$<CONFIG:", config,
+               ">:", cxxTargetName, ">>"));
+  }
+
+  // Check the experimental feature here. A toolchain may have
+  // skipped the check in the toolchain preparation logic.
+  if (!cmExperimental::HasSupportEnabled(
+        *this->Makefile, cmExperimental::Feature::CxxImportStd)) {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      "Experimental `import std` support not enabled when detecting "
+      "toolchain; it must be set before `CXX` is enabled (usually a "
+      "`project()` call).");
+    return false;
   }
   }
 
 
   return true;
   return true;
@@ -5335,7 +5398,9 @@ bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache,
         }
         }
         // See `cmGlobalGenerator::ApplyCXXStdTargets` in
         // See `cmGlobalGenerator::ApplyCXXStdTargets` in
         // `cmGlobalGenerator::Compute` for non-synthetic target resolutions.
         // `cmGlobalGenerator::Compute` for non-synthetic target resolutions.
-        gtp->ApplyCXXStdTargets();
+        if (!gtp->ApplyCXXStdTargets()) {
+          return false;
+        }
 
 
         gtp->DiscoverSyntheticTargets(cache, config);
         gtp->DiscoverSyntheticTargets(cache, config);
 
 

+ 11 - 1
Source/cmGlobalGenerator.cxx

@@ -1787,7 +1787,17 @@ void cmGlobalGenerator::ComputeTargetOrder(cmGeneratorTarget const* gt,
 bool cmGlobalGenerator::ApplyCXXStdTargets()
 bool cmGlobalGenerator::ApplyCXXStdTargets()
 {
 {
   for (auto const& gen : this->LocalGenerators) {
   for (auto const& gen : this->LocalGenerators) {
-    for (auto const& tgt : gen->GetGeneratorTargets()) {
+
+    // tgt->ApplyCXXStd can create targets itself, so we need iterators which
+    // won't be invalidated by that target creation
+    auto const& genTgts = gen->GetGeneratorTargets();
+    std::vector<cmGeneratorTarget*> existingTgts;
+    existingTgts.reserve(genTgts.size());
+    for (auto const& tgt : genTgts) {
+      existingTgts.push_back(tgt.get());
+    }
+
+    for (auto const& tgt : existingTgts) {
       if (!tgt->ApplyCXXStdTargets()) {
       if (!tgt->ApplyCXXStdTargets()) {
         return false;
         return false;
       }
       }

+ 1 - 0
Tests/RunCMake/CXXModules/Inspect.cmake

@@ -37,6 +37,7 @@ set(CMAKE_CXX_COMPILE_FEATURES \"${CMAKE_CXX_COMPILE_FEATURES}\")
 set(CMAKE_MAKE_PROGRAM \"${CMAKE_MAKE_PROGRAM}\")
 set(CMAKE_MAKE_PROGRAM \"${CMAKE_MAKE_PROGRAM}\")
 set(forced_cxx_standard \"${forced_cxx_standard}\")
 set(forced_cxx_standard \"${forced_cxx_standard}\")
 set(have_cxx23_import_std \"${have_cxx23_import_std}\")
 set(have_cxx23_import_std \"${have_cxx23_import_std}\")
+set(have_cxx26_import_std \"${have_cxx26_import_std}\")
 set(CMAKE_CXX_COMPILER_VERSION \"${CMAKE_CXX_COMPILER_VERSION}\")
 set(CMAKE_CXX_COMPILER_VERSION \"${CMAKE_CXX_COMPILER_VERSION}\")
 set(CMAKE_CXX_OUTPUT_EXTENSION \"${CMAKE_CXX_OUTPUT_EXTENSION}\")
 set(CMAKE_CXX_OUTPUT_EXTENSION \"${CMAKE_CXX_OUTPUT_EXTENSION}\")
 set(CXXModules_default_build_type \"${CMAKE_BUILD_TYPE}\")
 set(CXXModules_default_build_type \"${CMAKE_BUILD_TYPE}\")

+ 3 - 4
Tests/RunCMake/CXXModules/NoCXX23TargetRequired-stderr.txt

@@ -1,9 +1,8 @@
 CMake Error in CMakeLists\.txt:
 CMake Error in CMakeLists\.txt:
-  The "CXX_MODULE_STD" property on the target "nocxx23target" requires that
-  the "__CMAKE::CXX23" target exist, but it was not provided by the
-  toolchain\.  Reason:
+  The "CXX_MODULE_STD" property on target "nocxx23target" requires toolchain
+  support, but it was not provided.  Reason:
 
 
-    (Toolchain does not support discovering `import std` support|Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled \(usually a `project\(\)` call\)|Unsupported generator: [^\n]*)
+    (Toolchain does not support discovering module metadata|Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled \(usually a `project\(\)` call\)|Unsupported generator: [^\n]*)
 
 
 
 
 CMake Generate step failed\.  Build files cannot be regenerated correctly\.
 CMake Generate step failed\.  Build files cannot be regenerated correctly\.

+ 3 - 4
Tests/RunCMake/CXXModules/NoCXX26TargetRequired-stderr.txt

@@ -1,9 +1,8 @@
 CMake Error in CMakeLists\.txt:
 CMake Error in CMakeLists\.txt:
-  The "CXX_MODULE_STD" property on the target "nocxx26target" requires that
-  the "__CMAKE::CXX26" target exist, but it was not provided by the
-  toolchain\.  Reason:
+  The "CXX_MODULE_STD" property on target "nocxx26target" requires toolchain
+  support, but it was not provided.  Reason:
 
 
-    (Toolchain does not support discovering `import std` support|Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled \(usually a `project\(\)` call\)|Unsupported generator: [^\n]*)
+    (Toolchain does not support discovering module metadata|Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled \(usually a `project\(\)` call\)|Unsupported generator: [^\n]*)
 
 
 
 
 CMake Generate step failed\.  Build files cannot be regenerated correctly\.
 CMake Generate step failed\.  Build files cannot be regenerated correctly\.

+ 1 - 1
Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-build-stderr.txt

@@ -2,7 +2,7 @@ CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   is meant only for experimentation and feedback to CMake developers\.
   is meant only for experimentation and feedback to CMake developers\.
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
-  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_create_cxx_import_std\)
+  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   CMakeLists\.txt:[0-9]* \(project\)
   CMakeLists\.txt:[0-9]* \(project\)
 This warning is for project developers\.  Use -Wno-dev to suppress it\.
 This warning is for project developers\.  Use -Wno-dev to suppress it\.

+ 1 - 1
Tests/RunCMake/CXXModulesCompile/imp-std-exp-no-std-install-stderr.txt

@@ -2,7 +2,7 @@ CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   is meant only for experimentation and feedback to CMake developers\.
   is meant only for experimentation and feedback to CMake developers\.
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
-  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_create_cxx_import_std\)
+  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   CMakeLists\.txt:[0-9]* \(project\)
   CMakeLists\.txt:[0-9]* \(project\)
 This warning is for project developers\.  Use -Wno-dev to suppress it\.
 This warning is for project developers\.  Use -Wno-dev to suppress it\.

+ 1 - 1
Tests/RunCMake/CXXModulesCompile/imp-std-no-std-prop-stderr.txt

@@ -2,7 +2,7 @@ CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   is meant only for experimentation and feedback to CMake developers\.
   is meant only for experimentation and feedback to CMake developers\.
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
-  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_create_cxx_import_std\)
+  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   CMakeLists\.txt:[0-9]* \(project\)
   CMakeLists\.txt:[0-9]* \(project\)
 This warning is for project developers\.  Use -Wno-dev to suppress it\.
 This warning is for project developers\.  Use -Wno-dev to suppress it\.

+ 1 - 1
Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-build-stderr.txt

@@ -2,7 +2,7 @@ CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   is meant only for experimentation and feedback to CMake developers\.
   is meant only for experimentation and feedback to CMake developers\.
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
-  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_create_cxx_import_std\)
+  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   CMakeLists\.txt:[0-9]* \(project\)
   CMakeLists\.txt:[0-9]* \(project\)
 This warning is for project developers\.  Use -Wno-dev to suppress it\.
 This warning is for project developers\.  Use -Wno-dev to suppress it\.

+ 1 - 1
Tests/RunCMake/CXXModulesCompile/imp-std-not-in-exp-install-stderr.txt

@@ -2,7 +2,7 @@ CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   is meant only for experimentation and feedback to CMake developers\.
   is meant only for experimentation and feedback to CMake developers\.
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
-  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_create_cxx_import_std\)
+  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   CMakeLists\.txt:[0-9]* \(project\)
   CMakeLists\.txt:[0-9]* \(project\)
 This warning is for project developers\.  Use -Wno-dev to suppress it\.
 This warning is for project developers\.  Use -Wno-dev to suppress it\.

+ 1 - 1
Tests/RunCMake/CXXModulesCompile/imp-std-stderr.txt

@@ -2,7 +2,7 @@ CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   is meant only for experimentation and feedback to CMake developers\.
   is meant only for experimentation and feedback to CMake developers\.
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
-  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_create_cxx_import_std\)
+  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   CMakeLists\.txt:[0-9]* \(project\)
   CMakeLists\.txt:[0-9]* \(project\)
 This warning is for project developers\.  Use -Wno-dev to suppress it\.
 This warning is for project developers\.  Use -Wno-dev to suppress it\.

+ 8 - 0
Tests/RunCMake/CXXModulesCompile/imp-std-trans-exp-no-std-build-stderr.txt

@@ -0,0 +1,8 @@
+CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\):
+  CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
+  is meant only for experimentation and feedback to CMake developers\.
+Call Stack \(most recent call first\):
+  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\)
+  .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
+  CMakeLists\.txt:[0-9]* \(project\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.

+ 8 - 0
Tests/RunCMake/CXXModulesCompile/imp-std-trans-exp-no-std-install-stderr.txt

@@ -0,0 +1,8 @@
+CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0-9]* \(cmake_language\):
+  CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
+  is meant only for experimentation and feedback to CMake developers\.
+Call Stack \(most recent call first\):
+  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\)
+  .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
+  CMakeLists\.txt:[0-9]* \(project\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.

+ 1 - 1
Tests/RunCMake/CXXModulesCompile/imp-std-trans-not-in-exp-build-stderr.txt

@@ -2,7 +2,7 @@ CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   is meant only for experimentation and feedback to CMake developers\.
   is meant only for experimentation and feedback to CMake developers\.
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
-  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_create_cxx_import_std\)
+  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   CMakeLists\.txt:[0-9]* \(project\)
   CMakeLists\.txt:[0-9]* \(project\)
 This warning is for project developers\.  Use -Wno-dev to suppress it\.
 This warning is for project developers\.  Use -Wno-dev to suppress it\.

+ 1 - 1
Tests/RunCMake/CXXModulesCompile/imp-std-trans-not-in-exp-install-stderr.txt

@@ -2,7 +2,7 @@ CMake Warning \(dev\) at .*/Modules/Compiler/CMakeCommonCompilerMacros\.cmake:[0
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   CMake's support for `import std;` in C\+\+23 and newer is experimental\.  It
   is meant only for experimentation and feedback to CMake developers\.
   is meant only for experimentation and feedback to CMake developers\.
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
-  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_create_cxx_import_std\)
+  .*/Modules/CMakeDetermineCompilerSupport\.cmake:[0-9]* \(cmake_cxx_find_modules_json\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   .*/Modules/CMakeTestCXXCompiler\.cmake:[0-9]* \(CMAKE_DETERMINE_COMPILER_SUPPORT\)
   CMakeLists\.txt:[0-9]* \(project\)
   CMakeLists\.txt:[0-9]* \(project\)
 This warning is for project developers\.  Use -Wno-dev to suppress it\.
 This warning is for project developers\.  Use -Wno-dev to suppress it\.

+ 9 - 1
Tests/RunCMake/property_init/CompileSources.cmake

@@ -102,7 +102,6 @@ set(properties
   "C_LINKER_LAUNCHER"                       "ccache"            "<SAME>"
   "C_LINKER_LAUNCHER"                       "ccache"            "<SAME>"
   ### C++
   ### C++
   "CXX_LINKER_LAUNCHER"                     "ccache"            "<SAME>"
   "CXX_LINKER_LAUNCHER"                     "ccache"            "<SAME>"
-  "CXX_MODULE_STD"                          "ON"                "<SAME>"
   ### CUDA
   ### CUDA
   "CUDA_RESOLVE_DEVICE_SYMBOLS"             "ON"                "<SAME>"
   "CUDA_RESOLVE_DEVICE_SYMBOLS"             "ON"                "<SAME>"
   "CUDA_RUNTIME_LIBRARY"                    "Static"            "<SAME>"
   "CUDA_RUNTIME_LIBRARY"                    "Static"            "<SAME>"
@@ -229,6 +228,15 @@ macro (add_language_properties lang std)
     )
     )
 endmacro ()
 endmacro ()
 
 
+set(_cmake_supported_import_std_experimental "")
+cmake_language(GET_EXPERIMENTAL_FEATURE_ENABLED "CxxImportStd" _cmake_supported_import_std_experimental)
+if(_cmake_supported_import_std_experimental)
+  list(APPEND properties
+    # property                      expected  alias
+    "CXX_MODULE_STD"                "ON"      "<SAME>"
+  )
+endif()
+
 # Mock up knowing the standard flag. This doesn't actually build, so nothing
 # Mock up knowing the standard flag. This doesn't actually build, so nothing
 # should care at this point.
 # should care at this point.
 set(CMAKE_Cc_std_11_STANDARD_COMPILE_OPTION "-std=c11")
 set(CMAKE_Cc_std_11_STANDARD_COMPILE_OPTION "-std=c11")