c++modules: Restore use of local target for import std

Restore use of a project-local `STATIC` library to provide the stdlib's
module initialization symbols, as CMake 4.2's experimental support did.

In commit fa10dc6c22 (Experimental/CXXModules: Implement EcoStd Module
Metadata parser, 2025-10-21, v4.3.0-rc1~483^2) we switched to an
imported target to provide the BMI under the assumption that stdlib
implementations provide the symbols themselves, but they don't yet.

While refactoring the module metadata parser's properties logic, also
fix a relative path issue for local arguments.

Fixes: #27426, #27626, #27635
This commit is contained in:
Vito Gamberini
2026-03-04 16:40:37 -05:00
committed by Brad King
parent 772ed482e9
commit 3101d70902
2 changed files with 113 additions and 50 deletions
+100 -43
View File
@@ -435,99 +435,140 @@ cmCxxModuleMetadata::SaveResult cmCxxModuleMetadata::SaveToFile(
return st;
}
void cmCxxModuleMetadata::PopulateTarget(
cmTarget& target, cmCxxModuleMetadata const& meta,
std::vector<std::string> const& configs)
namespace {
struct MetaDataProperties
{
std::set<cm::string_view> allIncludeDirectories;
std::set<cm::string_view> allCompileOptions;
std::set<cm::string_view> allCompileFeatures;
std::set<std::string> allCompileDefinitions;
std::set<std::string> baseDirs;
std::string MetadataDir;
std::set<cm::string_view> AllCompileFeatures;
std::set<cm::string_view> AllCompileOptions;
std::set<cm::string_view> AllIncludeDirectories;
std::set<std::string> AllCompileDefinitions;
std::set<std::string> BaseDirs;
std::set<std::string> Sources;
std::string metadataDir =
cmSystemTools::GetFilenamePath(meta.MetadataFilePath);
std::string NormalizePath(std::string const& in) const
{
std::string out = in;
if (!cmSystemTools::FileIsFullPath(in)) {
out = cmStrCat(MetadataDir, '/', in);
}
return cmSystemTools::CollapseFullPath(out);
}
};
auto fileSet = target.GetOrCreateFileSet("CXX_MODULES", "CXX_MODULES",
cmFileSetVisibility::Interface);
MetaDataProperties CollectMetaProperties(cmCxxModuleMetadata const& meta)
{
MetaDataProperties props;
props.MetadataDir = cmSystemTools::GetFilenamePath(meta.MetadataFilePath);
for (auto const& module : meta.Modules) {
std::string sourcePath = module.SourcePath;
if (!cmSystemTools::FileIsFullPath(sourcePath)) {
sourcePath = cmStrCat(metadataDir, '/', sourcePath);
}
sourcePath = cmSystemTools::ToNormalizedPathOnDisk(std::move(sourcePath));
std::string sourcePath = props.NormalizePath(module.SourcePath);
props.Sources.insert(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);
props.BaseDirs.insert(cmSystemTools::GetFilenamePath(sourcePath));
if (module.LocalArguments) {
for (auto const& incDir : module.LocalArguments->IncludeDirectories) {
allIncludeDirectories.emplace(incDir);
props.AllIncludeDirectories.emplace(incDir);
}
for (auto const& sysIncDir :
module.LocalArguments->SystemIncludeDirectories) {
allIncludeDirectories.emplace(sysIncDir);
props.AllIncludeDirectories.emplace(sysIncDir);
}
for (auto const& opt : module.LocalArguments->CompileOptions) {
allCompileOptions.emplace(opt);
props.AllCompileOptions.emplace(opt);
}
for (auto const& opt : module.LocalArguments->CompileFeatures) {
allCompileFeatures.emplace(opt);
props.AllCompileFeatures.emplace(opt);
}
for (auto const& def : module.LocalArguments->Definitions) {
if (!def.Undef) {
if (def.Value) {
allCompileDefinitions.emplace(
props.AllCompileDefinitions.emplace(
cmStrCat(def.Name, "="_s, *def.Value));
} else {
allCompileDefinitions.emplace(def.Name);
props.AllCompileDefinitions.emplace(def.Name);
}
}
}
}
}
for (auto const& baseDir : baseDirs) {
return props;
}
void PopulateFileSet(cmTarget& target, MetaDataProperties const& props)
{
auto fileSet = target.GetOrCreateFileSet("CXX_MODULES", "CXX_MODULES",
cmFileSetVisibility::Public);
for (auto const& source : props.Sources) {
fileSet.first->AddFileEntry(source);
}
for (auto const& baseDir : props.BaseDirs) {
fileSet.first->AddDirectoryEntry(baseDir);
}
}
if (!allIncludeDirectories.empty()) {
void PopulateLocalTarget(cmTarget& target, MetaDataProperties const& props)
{
if (!props.AllIncludeDirectories.empty()) {
target.AppendProperty("INCLUDE_DIRECTORIES",
cmJoin(props.AllIncludeDirectories, ";"));
}
if (!props.AllCompileDefinitions.empty()) {
target.AppendProperty("COMPILE_DEFINITIONS",
cmJoin(props.AllCompileDefinitions, ";"));
}
if (!props.AllCompileOptions.empty()) {
target.AppendProperty("COMPILE_OPTIONS",
cmJoin(props.AllCompileOptions, ";"));
}
if (!props.AllCompileFeatures.empty()) {
target.AppendProperty("COMPILE_FEATURES",
cmJoin(props.AllCompileFeatures, ";"));
}
}
void PopulateImportedTarget(cmTarget& target, MetaDataProperties const& props,
cmCxxModuleMetadata const& meta,
std::vector<std::string> const& configs)
{
if (!props.AllIncludeDirectories.empty()) {
target.SetProperty("IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES",
cmJoin(allIncludeDirectories, ";"));
cmJoin(props.AllIncludeDirectories, ";"));
}
if (!allCompileDefinitions.empty()) {
if (!props.AllCompileDefinitions.empty()) {
target.SetProperty("IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS",
cmJoin(allCompileDefinitions, ";"));
cmJoin(props.AllCompileDefinitions, ";"));
}
if (!allCompileOptions.empty()) {
if (!props.AllCompileOptions.empty()) {
target.SetProperty("IMPORTED_CXX_MODULES_COMPILE_OPTIONS",
cmJoin(allCompileOptions, ";"));
cmJoin(props.AllCompileOptions, ";"));
}
if (!allCompileFeatures.empty()) {
if (!props.AllCompileFeatures.empty()) {
target.SetProperty("IMPORTED_CXX_MODULES_COMPILE_FEATURES",
cmJoin(allCompileFeatures, ";"));
cmJoin(props.AllCompileFeatures, ";"));
}
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);
}
sourcePath =
cmSystemTools::ToNormalizedPathOnDisk(std::move(sourcePath));
moduleList.push_back(cmStrCat(module.LogicalName, "="_s, sourcePath));
moduleList.push_back(cmStrCat(module.LogicalName, "="_s,
props.NormalizePath(module.SourcePath)));
}
}
@@ -539,3 +580,19 @@ void cmCxxModuleMetadata::PopulateTarget(
}
}
}
} // namespace
void cmCxxModuleMetadata::PopulateTarget(
cmTarget& target, cmCxxModuleMetadata const& meta,
std::vector<std::string> const& configs)
{
auto props = CollectMetaProperties(meta);
PopulateFileSet(target, props);
if (target.IsImported()) {
PopulateImportedTarget(target, props, meta, configs);
} else {
PopulateLocalTarget(target, props);
}
}
+13 -7
View File
@@ -5194,13 +5194,20 @@ bool CreateCxxStdlibTarget(cmMakefile* makefile, cmLocalGenerator* lg,
metadata = std::move(*parseResult.Meta);
}
auto* stdlibTgt = makefile->AddImportedTarget(
cxxTargetName, cmStateEnums::INTERFACE_LIBRARY, true);
auto const localTargetName = cmStrCat("__cmake_cxx_std_", stdLevel);
cmStandardLevelResolver standardResolver(makefile);
auto* stdlibTgt = makefile->AddLibrary(
localTargetName, cmStateEnums::STATIC_LIBRARY, {}, true);
cmCxxModuleMetadata::PopulateTarget(*stdlibTgt, *metadata, configs);
stdlibTgt->AppendProperty("IMPORTED_CXX_MODULES_COMPILE_FEATURES",
cmStrCat("cxx_std_", stdLevel));
standardResolver.AddRequiredTargetFeature(stdlibTgt,
cmStrCat("cxx_std_", stdLevel));
auto gt = cm::make_unique<cmGeneratorTarget>(stdlibTgt, lg);
for (auto const& config : configs) {
gt->ComputeCompileFeatures(config);
}
lg->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(stdlibTgt, lg));
lg->AddGeneratorTarget(std::move(gt));
makefile->AddAlias(cxxTargetName, localTargetName);
#endif // CMAKE_BOOTSTRAP
@@ -5291,8 +5298,7 @@ bool cmGeneratorTarget::ApplyCXXStdTargets()
standardResolver.GetLevelString("CXX", *explicitLevel);
auto const cxxTargetName = cmStrCat("__CMAKE::CXX", stdLevel);
// Create the __CMAKE::CXX## IMPORTED interface target if it doesn't
// already exist
// Create the __CMAKE::CXX## target if it doesn't already exist
if (!this->Makefile->FindTargetToUse(cxxTargetName) &&
!CreateCxxStdlibTarget(this->Makefile, this->LocalGenerator,
this->GetName(), cxxTargetName, stdLevel,