Skip to content

Commit fa10dc6

Browse files
committed
Experimental/CXXModules: Implement EcoStd Module Metadata parser
Adds a parser and serializer for the EcoStd Module Metadata format RFC: ecostd/rfcs#3 This adapts the existing experimental support for import std; to use the new parser. The CMAKE_CXX_STDLIB_MODULES_JSON is now the canonical variable for controlling how CMake discovers the stdlib module metadata, and is used directly by compiler detection. Toolchains can still override the __CMAKE::CXX## targets if they wish, either in conjunction with CMAKE_CXX_STDLIB_MODULE_JSON or not. It is possible to disable automatic detection of CMAKE_CXX_STDLIB_MODULE_JSON by setting it to the empty string. When available, the CMAKE_CXX_STDLIB_MODULE_JSON will be used to create all requested C++ stdlibs which do not already have targets.
1 parent 6087f56 commit fa10dc6

25 files changed

+806
-467
lines changed

Modules/CMakeCXXCompiler.cmake.in

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,8 @@ set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "@CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES@")
9696
set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@")
9797
set(CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR "@CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR@")
9898

99-
set(CMAKE_CXX_COMPILER_IMPORT_STD "")
100-
@CMAKE_CXX_IMPORT_STD@
99+
set(CMAKE_CXX_COMPILER_IMPORT_STD "@CMAKE_CXX_COMPILER_IMPORT_STD@")
100+
set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "@CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE@")
101+
if(CMAKE_CXX_STDLIB_MODULES_JSON)
102+
set(CMAKE_CXX_STDLIB_MODULES_JSON "@CMAKE_CXX_STDLIB_MODULES_JSON@")
103+
endif()

Modules/CMakeDetermineCompilerSupport.cmake

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,18 +105,21 @@ function(cmake_determine_compiler_support lang)
105105
)
106106
endif()
107107

108-
# Create targets for use with `import std;` here.
109-
set(CMAKE_CXX_IMPORT_STD "")
110-
foreach (_cmake_import_std_version IN ITEMS 23 26)
111-
if (CMAKE_CXX${_cmake_import_std_version}_COMPILE_FEATURES)
112-
set(_cmake_cxx_import_std "")
113-
cmake_create_cxx_import_std("${_cmake_import_std_version}" _cmake_cxx_import_std)
114-
if (_cmake_cxx_import_std)
115-
string(APPEND CMAKE_CXX_IMPORT_STD "### Imported target for C++${_cmake_import_std_version} standard library\n")
116-
string(APPEND CMAKE_CXX_IMPORT_STD "${_cmake_cxx_import_std}\n\n")
117-
endif ()
118-
endif ()
119-
endforeach ()
108+
# Find the module metadata for import std
109+
set(CMAKE_CXX_COMPILER_IMPORT_STD "")
110+
cmake_cxx_find_modules_json()
111+
foreach(_cmake_import_std_version IN ITEMS 23 26)
112+
if(CMAKE_CXX${_cmake_import_std_version}_COMPILE_FEATURES)
113+
# Modules JSON covers all versions, otherwise rely on toolchain targets
114+
if(CMAKE_CXX_STDLIB_MODULES_JSON OR (TARGET "__CMAKE:CXX${_cmake_import_std_version}"))
115+
list(APPEND CMAKE_CXX_COMPILER_IMPORT_STD ${_cmake_import_std_version})
116+
endif()
117+
endif()
118+
endforeach()
119+
120+
set(CMAKE_CXX_COMPILER_IMPORT_STD ${CMAKE_CXX_COMPILER_IMPORT_STD} PARENT_SCOPE)
121+
set(CMAKE_CXX_STDLIB_MODULES_JSON ${CMAKE_CXX_STDLIB_MODULES_JSON} PARENT_SCOPE)
122+
set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "${CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE}" PARENT_SCOPE)
120123

121124
set(CMAKE_CXX_COMPILE_FEATURES ${CMAKE_CXX_COMPILE_FEATURES} PARENT_SCOPE)
122125
set(CMAKE_CXX98_COMPILE_FEATURES ${CMAKE_CXX98_COMPILE_FEATURES} PARENT_SCOPE)
@@ -126,7 +129,6 @@ function(cmake_determine_compiler_support lang)
126129
set(CMAKE_CXX20_COMPILE_FEATURES ${CMAKE_CXX20_COMPILE_FEATURES} PARENT_SCOPE)
127130
set(CMAKE_CXX23_COMPILE_FEATURES ${CMAKE_CXX23_COMPILE_FEATURES} PARENT_SCOPE)
128131
set(CMAKE_CXX26_COMPILE_FEATURES ${CMAKE_CXX26_COMPILE_FEATURES} PARENT_SCOPE)
129-
set(CMAKE_CXX_IMPORT_STD ${CMAKE_CXX_IMPORT_STD} PARENT_SCOPE)
130132

131133
message(CHECK_PASS "done")
132134

Modules/Compiler/CMakeCommonCompilerMacros.cmake

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -202,43 +202,21 @@ macro(cmake_record_hip_compile_features)
202202
_has_compiler_features_hip(98)
203203
endmacro()
204204

205-
function(cmake_create_cxx_import_std std variable)
206-
set(_cmake_supported_import_std_features
207-
# Compilers support `import std` in C++20 as an extension. Skip
208-
# for now.
209-
# 20
210-
23
211-
26)
212-
list(FIND _cmake_supported_import_std_features "${std}" _cmake_supported_import_std_idx)
213-
if (_cmake_supported_import_std_idx EQUAL "-1")
214-
set("${variable}"
215-
"set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Unsupported C++ standard: C++${std}\")\n"
216-
PARENT_SCOPE)
217-
return ()
218-
endif ()
219-
# If the target exists, skip. A toolchain file may have provided it.
220-
if (TARGET "__CMAKE::CXX${std}")
221-
return ()
222-
endif ()
205+
function(cmake_cxx_find_modules_json)
223206
# The generator must support imported C++ modules.
224207
if (NOT CMAKE_GENERATOR MATCHES "Ninja")
225-
set("${variable}"
226-
"set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Unsupported generator: ${CMAKE_GENERATOR}\")\n"
227-
PARENT_SCOPE)
208+
set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Unsupported generator: ${CMAKE_GENERATOR}" PARENT_SCOPE)
228209
return ()
229210
endif ()
211+
230212
# Check if the compiler understands how to `import std;`.
231213
include("${CMAKE_ROOT}/Modules/Compiler/${CMAKE_CXX_COMPILER_ID}-CXX-CXXImportStd.cmake" OPTIONAL RESULT_VARIABLE _cmake_import_std_res)
232214
if (NOT _cmake_import_std_res)
233-
set("${variable}"
234-
"set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Toolchain does not support discovering `import std` support\")\n"
235-
PARENT_SCOPE)
215+
set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Toolchain does not support discovering module metadata" PARENT_SCOPE)
236216
return ()
237217
endif ()
238-
if (NOT COMMAND _cmake_cxx_import_std)
239-
set("${variable}"
240-
"set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Toolchain does not provide `import std` discovery command\")\n"
241-
PARENT_SCOPE)
218+
if (NOT COMMAND _cmake_cxx_find_modules_json)
219+
set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Toolchain does not provide module metadata discovery command" PARENT_SCOPE)
242220
return ()
243221
endif ()
244222

@@ -249,19 +227,11 @@ function(cmake_create_cxx_import_std std variable)
249227
"CxxImportStd"
250228
_cmake_supported_import_std_experimental)
251229
if (NOT _cmake_supported_import_std_experimental)
252-
set("${variable}"
253-
"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"
254-
PARENT_SCOPE)
230+
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)
255231
return ()
256232
endif ()
257233

258-
_cmake_cxx_import_std("${std}" target_definition)
259-
string(CONCAT guarded_target_definition
260-
"if (NOT TARGET \"__CMAKE::CXX${std}\")\n"
261-
"${target_definition}"
262-
"endif ()\n"
263-
"if (TARGET \"__CMAKE::CXX${std}\")\n"
264-
" list(APPEND CMAKE_CXX_COMPILER_IMPORT_STD \"${std}\")\n"
265-
"endif ()\n")
266-
set("${variable}" "${guarded_target_definition}" PARENT_SCOPE)
234+
_cmake_cxx_find_modules_json()
235+
set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "${CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE}" PARENT_SCOPE)
236+
set(CMAKE_CXX_STDLIB_MODULES_JSON "${CMAKE_CXX_STDLIB_MODULES_JSON}" PARENT_SCOPE)
267237
endfunction()
Lines changed: 6 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
1-
function (_cmake_cxx_import_std std variable)
1+
function (_cmake_cxx_find_modules_json)
22
if (CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libc++")
33
set(_clang_modules_json_impl "libc++")
44
elseif (CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libstdc++")
55
set(_clang_modules_json_impl "libstdc++")
66
else ()
7-
set("${variable}"
8-
"set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Only `libc++` and `libstdc++` are supported\")\n"
9-
PARENT_SCOPE)
7+
set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Only `libc++` and `libstdc++` are supported" PARENT_SCOPE)
108
return ()
119
endif ()
1210

13-
if (CMAKE_CXX_STDLIB_MODULES_JSON)
14-
set(_clang_libcxx_modules_json_file "${CMAKE_CXX_STDLIB_MODULES_JSON}")
15-
else ()
11+
if (NOT DEFINED CMAKE_CXX_STDLIB_MODULES_JSON)
1612
execute_process(
1713
COMMAND
1814
"${CMAKE_CXX_COMPILER}"
@@ -24,165 +20,18 @@ function (_cmake_cxx_import_std std variable)
2420
OUTPUT_STRIP_TRAILING_WHITESPACE
2521
ERROR_STRIP_TRAILING_WHITESPACE)
2622
if (_clang_libcxx_modules_json_file_res)
27-
set("${variable}"
28-
"set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Could not find `${_clang_modules_json_impl}.modules.json` resource\")\n"
29-
PARENT_SCOPE)
23+
set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Could not find `${_clang_modules_json_impl}.modules.json` resource" PARENT_SCOPE)
3024
return ()
3125
endif ()
32-
endif ()
33-
34-
# Without this file, we do not have modules installed.
35-
if (NOT EXISTS "${_clang_libcxx_modules_json_file}")
36-
set("${variable}"
37-
"set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`${_clang_modules_json_impl}.modules.json` resource does not exist\")\n"
38-
PARENT_SCOPE)
39-
return ()
26+
set(CMAKE_CXX_STDLIB_MODULES_JSON "${_clang_libcxx_modules_json_file}" PARENT_SCOPE)
4027
endif ()
4128

4229
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "18.1.2" AND
4330
CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libc++")
4431
# The original PR had a key spelling mismatch internally. Do not support it
4532
# and instead require a release known to have the fix.
4633
# https://github.com/llvm/llvm-project/pull/83036
47-
set("${variable}"
48-
"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"
49-
PARENT_SCOPE)
50-
return ()
51-
endif ()
52-
53-
file(READ "${_clang_libcxx_modules_json_file}" _clang_libcxx_modules_json)
54-
string(JSON _clang_modules_json_version GET "${_clang_libcxx_modules_json}" "version")
55-
string(JSON _clang_modules_json_revision GET "${_clang_libcxx_modules_json}" "revision")
56-
# Require version 1.
57-
if (NOT _clang_modules_json_version EQUAL "1")
58-
set("${variable}"
59-
"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"
60-
PARENT_SCOPE)
61-
return ()
62-
endif ()
63-
64-
string(JSON _clang_modules_json_nmodules LENGTH "${_clang_libcxx_modules_json}" "modules")
65-
# Don't declare the target without any modules.
66-
if (NOT _clang_modules_json_nmodules)
67-
set("${variable}"
68-
"set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"`libc++.modules.json` does not list any available modules\")\n"
69-
PARENT_SCOPE)
34+
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)
7035
return ()
7136
endif ()
72-
73-
# Declare the target.
74-
set(_clang_libcxx_target "")
75-
# Clang 18 does not provide the module initializer for the `std` modules.
76-
# Create a static library to hold these. Hope that Clang 19 can provide this,
77-
# but never run the code.
78-
string(APPEND _clang_libcxx_target
79-
"add_library(__cmake_cxx${std} STATIC)\n")
80-
string(APPEND _clang_libcxx_target
81-
"target_sources(__cmake_cxx${std} INTERFACE \"$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,STATIC_LIBRARY>:$<TARGET_OBJECTS:__cmake_cxx${std}>>\")\n")
82-
string(APPEND _clang_libcxx_target
83-
"set_property(TARGET __cmake_cxx${std} PROPERTY EXCLUDE_FROM_ALL 1)\n")
84-
string(APPEND _clang_libcxx_target
85-
"set_property(TARGET __cmake_cxx${std} PROPERTY CXX_SCAN_FOR_MODULES 1)\n")
86-
string(APPEND _clang_libcxx_target
87-
"set_property(TARGET __cmake_cxx${std} PROPERTY CXX_MODULE_STD 0)\n")
88-
string(APPEND _clang_libcxx_target
89-
"target_compile_features(__cmake_cxx${std} PUBLIC cxx_std_${std})\n")
90-
91-
set(_clang_modules_is_stdlib 0)
92-
set(_clang_modules_include_dirs_list "")
93-
set(_clang_modules_module_paths "")
94-
get_filename_component(_clang_modules_dir "${_clang_libcxx_modules_json_file}" DIRECTORY)
95-
96-
# Add module sources.
97-
math(EXPR _clang_modules_json_nmodules_range "${_clang_modules_json_nmodules} - 1")
98-
foreach (_clang_modules_json_modules_idx RANGE 0 "${_clang_modules_json_nmodules_range}")
99-
string(JSON _clang_modules_json_module GET "${_clang_libcxx_modules_json}" "modules" "${_clang_modules_json_modules_idx}")
100-
101-
string(JSON _clang_modules_json_module_source GET "${_clang_modules_json_module}" "source-path")
102-
string(JSON _clang_modules_json_module_is_stdlib GET "${_clang_modules_json_module}" "is-std-library")
103-
string(JSON _clang_modules_json_module_local_arguments ERROR_VARIABLE _clang_modules_json_module_local_arguments_error GET "${_clang_modules_json_module}" "local-arguments")
104-
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")
105-
106-
if (_clang_modules_json_module_local_arguments_error)
107-
set(_clang_modules_json_module_local_arguments "")
108-
endif ()
109-
if (_clang_modules_json_module_nsystem_include_directories_error)
110-
set(_clang_modules_json_module_nsystem_include_directories 0)
111-
endif ()
112-
if (NOT IS_ABSOLUTE "${_clang_modules_json_module_source}")
113-
string(PREPEND _clang_modules_json_module_source "${_clang_modules_dir}/")
114-
endif ()
115-
list(APPEND _clang_modules_module_paths
116-
"${_clang_modules_json_module_source}")
117-
118-
if (_clang_modules_json_module_is_stdlib)
119-
set(_clang_modules_is_stdlib 1)
120-
endif ()
121-
122-
if (_clang_modules_json_module_nsystem_include_directories)
123-
math(EXPR _clang_modules_json_module_nsystem_include_directories_range "${_clang_modules_json_module_nsystem_include_directories} - 1")
124-
foreach (_clang_modules_json_modules_system_include_directories_idx RANGE 0 "${_clang_modules_json_module_nsystem_include_directories_range}")
125-
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}")
126-
127-
if (NOT IS_ABSOLUTE "${_clang_modules_json_module_system_include_directory}")
128-
string(PREPEND _clang_modules_json_module_system_include_directory "${_clang_modules_dir}/")
129-
endif ()
130-
list(APPEND _clang_modules_include_dirs_list
131-
"${_clang_modules_json_module_system_include_directory}")
132-
endforeach ()
133-
endif ()
134-
endforeach ()
135-
136-
# Split the paths into basedirs and module paths.
137-
set(_clang_modules_base_dirs_list "")
138-
set(_clang_modules_files "")
139-
foreach (_clang_modules_module_path IN LISTS _clang_modules_module_paths)
140-
get_filename_component(_clang_module_dir "${_clang_modules_module_path}" DIRECTORY)
141-
142-
list(APPEND _clang_modules_base_dirs_list
143-
"${_clang_module_dir}")
144-
string(APPEND _clang_modules_files
145-
" \"${_clang_modules_module_path}\"")
146-
endforeach ()
147-
list(REMOVE_DUPLICATES _clang_modules_base_dirs_list)
148-
set(_clang_modules_base_dirs "")
149-
foreach (_clang_modules_base_dir IN LISTS _clang_modules_base_dirs_list)
150-
string(APPEND _clang_modules_base_dirs
151-
" \"${_clang_modules_base_dir}\"")
152-
endforeach ()
153-
154-
# If we have a standard library module, suppress warnings about reserved
155-
# module names.
156-
if (_clang_modules_is_stdlib)
157-
string(APPEND _clang_libcxx_target
158-
"target_compile_options(__cmake_cxx${std} PRIVATE -Wno-reserved-module-identifier)\n")
159-
endif ()
160-
161-
# Set up include directories.
162-
list(REMOVE_DUPLICATES _clang_modules_include_dirs_list)
163-
set(_clang_modules_include_dirs "")
164-
foreach (_clang_modules_include_dir IN LISTS _clang_modules_include_dirs_list)
165-
string(APPEND _clang_modules_include_dirs
166-
" \"${_clang_modules_include_dir}\"")
167-
endforeach ()
168-
string(APPEND _clang_libcxx_target
169-
"target_include_directories(__cmake_cxx${std} PRIVATE ${_clang_modules_include_dirs})\n")
170-
171-
# Create the file set for the modules.
172-
string(APPEND _clang_libcxx_target
173-
"target_sources(__cmake_cxx${std}
174-
PUBLIC
175-
FILE_SET std TYPE CXX_MODULES
176-
BASE_DIRS ${_clang_modules_base_dirs}
177-
FILES ${_clang_modules_files})\n")
178-
179-
# Wrap the `__cmake_cxx${std}` target in a check.
180-
string(PREPEND _clang_libcxx_target
181-
"if (NOT TARGET \"__cmake_cxx${std}\")\n")
182-
string(APPEND _clang_libcxx_target
183-
"endif ()\n")
184-
string(APPEND _clang_libcxx_target
185-
"add_library(__CMAKE::CXX${std} ALIAS __cmake_cxx${std})\n")
186-
187-
set("${variable}" "${_clang_libcxx_target}" PARENT_SCOPE)
18837
endfunction ()

0 commit comments

Comments
 (0)