fileapi: Test import std target references in codemodel

Add a regression test for the File API codemodel graph produced for an
import std target. The test verifies that emitted target references,
including those to the CMake-provided stdlib target, resolve to targets
present in the codemodel.

Closes: #27890
This commit is contained in:
kkkzbh
2026-06-24 03:02:18 +08:00
parent 066b9cf7a1
commit 11c93dd6da
8 changed files with 262 additions and 1 deletions
+4 -1
View File
@@ -437,7 +437,10 @@ endif()
add_RunCMake_test(FileAPI -DPython_EXECUTABLE=${Python_EXECUTABLE}
-DCMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID}
-DCMAKE_CXX_SIMULATE_ID=${CMAKE_CXX_SIMULATE_ID}
-DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA})
-DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}
-DCMake_TEST_MODULE_COMPILATION=${CMake_TEST_MODULE_COMPILATION}
-DCMake_TEST_MODULE_COMPILATION_RULES=${CMake_TEST_MODULE_COMPILATION_RULES}
-DCMake_TEST_CXX_STDLIB_MODULES_JSON=${CMake_TEST_CXX_STDLIB_MODULES_JSON})
if(CMAKE_GENERATOR MATCHES "Make|Ninja|FASTBuild")
add_RunCMake_test(Instrumentation -DPython_EXECUTABLE=${Python_EXECUTABLE}
-DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
+74
View File
@@ -164,3 +164,77 @@ run_object(cmakeFiles-v1)
run_object(toolchains-v1)
run_object(toolchains-v1 -DTOOLCHAINSV1_COMPILERARGS=1)
run_object(toolchains-v1 -DTOOLCHAINSV1_COMPILERARGS=2)
function(run_import_std_codemodel_test)
if(NOT CMake_TEST_MODULE_COMPILATION)
return()
endif()
set(stdlib_custom_json)
if(CMake_TEST_CXX_STDLIB_MODULES_JSON)
list(APPEND stdlib_custom_json
-DCMAKE_CXX_STDLIB_MODULES_JSON=${CMake_TEST_CXX_STDLIB_MODULES_JSON})
endif()
run_cmake(codemodel-v2-import-std-Inspect ${stdlib_custom_json})
include("${RunCMake_BINARY_DIR}/codemodel-v2-import-std-Inspect-build/info.cmake")
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()
set(generator_supports_cxx_modules 0)
if(RunCMake_GENERATOR MATCHES "Ninja" AND
ninja_version VERSION_GREATER_EQUAL "1.11" AND
"cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
set(generator_supports_cxx_modules 1)
endif()
if(RunCMake_GENERATOR MATCHES "Visual Studio" AND
CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "19.34")
set(generator_supports_cxx_modules 1)
endif()
if(NOT generator_supports_cxx_modules)
return()
endif()
string(REPLACE "," ";" cxx_module_features "${CMake_TEST_MODULE_COMPILATION}")
if(RunCMake_GENERATOR MATCHES "Ninja")
list(APPEND cxx_module_features
"ninja")
endif()
if(NOT "import_std23" IN_LIST cxx_module_features OR
NOT "cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
return()
endif()
set(RunCMake_TEST_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/codemodel-v2-import-std")
set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/codemodel-v2-import-std-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()
list(APPEND RunCMake_TEST_OPTIONS
${stdlib_custom_json}
"-DCMake_TEST_MODULE_COMPILATION_RULES=${CMake_TEST_MODULE_COMPILATION_RULES}")
run_cmake(codemodel-v2-import-std)
endfunction()
run_import_std_codemodel_test()
@@ -0,0 +1 @@
include("${CMAKE_CURRENT_LIST_DIR}/../CXXModulesCompile/Inspect.cmake")
@@ -0,0 +1,10 @@
set(expect
reply
reply/codemodel-v2-[0-9a-f]+\\.json
.*
reply/index-[0-9.T-]+\\.json
.*
)
check_api("^${expect}$")
check_python(codemodel-v2-import-std index)
@@ -0,0 +1,140 @@
from check_index import *
import json
import os
def read_json(filename):
with open(os.path.join(reply_dir, filename)) as f:
return json.load(f)
def check_target_id(target_ids, std_target_ids, state, target_file, context, target_id):
assert target_id in target_ids, \
"Target '%s' has dangling %s target id '%s'." % (target_file, context, target_id)
if target_id in std_target_ids:
state["referenced_std_target"] = True
def collect_target_ids(codemodel):
target_ids = set()
target_json_files = set()
std_target_ids = set()
seen_main_target = False
seen_std_target = False
assert is_list(codemodel["configurations"])
assert len(codemodel["configurations"]) > 0
for configuration in codemodel["configurations"]:
for target_list_name in ("targets", "abstractTargets"):
for target in configuration[target_list_name]:
assert is_dict(target)
target_id = target["id"]
target_ids.add(target_id)
target_json_files.add(target["jsonFile"])
if target["name"] == "main":
seen_main_target = True
if target["name"] == "@cmake_cxx_std":
seen_std_target = True
std_target_ids.add(target_id)
assert seen_main_target, "The File API codemodel did not report the user target 'main'."
assert seen_std_target, \
"The File API codemodel did not report the import std target '@cmake_cxx_std'."
return target_ids, target_json_files, std_target_ids
def check_target_references(codemodel):
target_ids, target_json_files, std_target_ids = collect_target_ids(codemodel)
target_id_reference_fields = (
"dependencies",
"compileDependencies",
"interfaceCompileDependencies",
"objectDependencies",
"orderDependencies",
)
link_reference_fields = (
"linkLibraries",
"interfaceLinkLibraries",
)
state = {
"referenced_std_target": False,
}
for target_json_file in sorted(target_json_files):
target = read_json(target_json_file)
for target_reference_field in target_id_reference_fields:
if target_reference_field not in target:
continue
assert is_list(target[target_reference_field])
for reference in target[target_reference_field]:
assert is_dict(reference)
check_target_id(
target_ids,
std_target_ids,
state,
target_json_file,
target_reference_field,
reference["id"])
if "fromDependency" in reference:
assert is_dict(reference["fromDependency"])
assert sorted(reference["fromDependency"].keys()) == ["id"]
check_target_id(
target_ids,
std_target_ids,
state,
target_json_file,
target_reference_field + ".fromDependency",
reference["fromDependency"]["id"])
for target_reference_field in link_reference_fields:
if target_reference_field not in target:
continue
assert is_list(target[target_reference_field])
for reference in target[target_reference_field]:
assert is_dict(reference)
has_id = "id" in reference
has_fragment = "fragment" in reference
assert has_id != has_fragment
if has_id:
check_target_id(
target_ids,
std_target_ids,
state,
target_json_file,
target_reference_field,
reference["id"])
if "fromDependency" in reference:
assert is_dict(reference["fromDependency"])
assert sorted(reference["fromDependency"].keys()) == ["id"]
check_target_id(
target_ids,
std_target_ids,
state,
target_json_file,
target_reference_field + ".fromDependency",
reference["fromDependency"]["id"])
assert state["referenced_std_target"], \
"The File API codemodel did not reference '@cmake_cxx_std' from any target relationship."
assert is_dict(index)
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
assert is_list(index["objects"])
codemodel_index = None
for index_object in index["objects"]:
if index_object["kind"] == "codemodel":
codemodel_index = index_object
break
assert codemodel_index is not None, "No codemodel object found in File API index."
assert codemodel_index["version"]["major"] == 2
codemodel = read_json(codemodel_index["jsonFile"])
check_target_references(codemodel)
@@ -0,0 +1,12 @@
CMake Warning \(experimental\) 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-author or -Wno-experimental
to suppress it\.
@@ -0,0 +1,15 @@
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD
"f35a9ac6-8463-4d38-8eec-5d6008153e7d")
cmake_minimum_required(VERSION 3.29)
project(codemodel_v2_import_std CXX)
cmake_file_api(QUERY API_VERSION 1 CODEMODEL 2)
include("${CMAKE_CURRENT_SOURCE_DIR}/../../CXXModulesCompile/cxx-modules-rules.cmake")
set(CMAKE_CXX_MODULE_STD 1)
add_executable(main
main.cxx)
target_compile_features(main PRIVATE cxx_std_23)
@@ -0,0 +1,6 @@
import std;
auto main() -> int
{
return 0;
}