c++modules: use synthetic interface objects for import std

Fixes: #27999
This commit is contained in:
Vito Gamberini
2026-08-01 19:55:07 -04:00
committed by Vito Gamberini
parent 437cf232c5
commit 00d8e23414
10 changed files with 197 additions and 16 deletions
+17 -6
View File
@@ -708,7 +708,7 @@ void cmGeneratorTarget::GetObjectSources(
this->VisitedConfigsForObjects.insert(config);
}
void cmGeneratorTarget::ComputeObjectMapping()
void cmGeneratorTarget::ComputeObjectMapping() const
{
auto const& configs =
this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
@@ -863,7 +863,8 @@ bool cmGeneratorTarget::IsIPOEnabled(std::string const& lang,
return false;
}
std::string const& cmGeneratorTarget::GetObjectName(cmSourceFile const* file)
std::string const& cmGeneratorTarget::GetObjectName(
cmSourceFile const* file) const
{
this->ComputeObjectMapping();
auto const useShortPaths = this->GetUseShortObjectNames()
@@ -906,7 +907,7 @@ void cmGeneratorTarget::AddExplicitObjectName(cmSourceFile const* sf)
bool cmGeneratorTarget::HasExplicitObjectName(cmSourceFile const* file) const
{
const_cast<cmGeneratorTarget*>(this)->ComputeObjectMapping();
this->ComputeObjectMapping();
auto it = this->ExplicitObjectName.find(file);
return it != this->ExplicitObjectName.end();
}
@@ -5377,8 +5378,9 @@ bool CreateCxxStdlibTarget(cmMakefile* makefile, cmLocalGenerator* lg,
metadata = std::move(*parseResult.Meta);
}
auto* stdlibTgt = makefile->AddLibrary(
"@cmake_cxx_std", cm::TargetType::STATIC_LIBRARY, {}, true);
auto* stdlibTgt = makefile->AddImportedTarget(
"@cmake_cxx_std", cm::TargetType::INTERFACE_LIBRARY,
cm::ImportedTargetScope::Local);
cmCxxModuleMetadata::PopulateTarget(*stdlibTgt, *metadata, configs);
cmStandardLevelResolver standardResolver(makefile);
standardResolver.AddRequiredTargetFeature(stdlibTgt, "cxx_std_20");
@@ -5387,7 +5389,13 @@ bool CreateCxxStdlibTarget(cmMakefile* makefile, cmLocalGenerator* lg,
gt->ComputeCompileFeatures(config);
}
lg->AddGeneratorTarget(std::move(gt));
auto compilerId = makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_ID");
if (compilerId == "MSVC") {
stdlibTgt->SetCxxModuleNeedsInterfaceObjects(true);
}
lg->AddImportedGeneratorTarget(gt.get());
lg->AddOwnedImportedGeneratorTarget(std::move(gt));
#endif // CMAKE_BOOTSTRAP
@@ -5541,6 +5549,9 @@ cmGeneratorTarget const* cmGeneratorTarget::GetCxxSyntheticTarget(
auto* lg = this->GetLocalGenerator();
auto* tgt =
mf->AddSynthesizedTarget(cm::TargetType::INTERFACE_LIBRARY, targetName);
if (model->CxxModuleNeedsInterfaceObjects()) {
tgt->SetCxxModuleNeedsInterfaceObjects(true);
}
// Copy relevant information from the existing target.
+2 -2
View File
@@ -195,7 +195,7 @@ public:
void GetObjectSources(std::vector<cmSourceFile const*>&,
std::string const& config) const;
std::string const& GetObjectName(cmSourceFile const* file);
std::string const& GetObjectName(cmSourceFile const* file) const;
char const* GetCustomObjectExtension() const;
bool HasExplicitObjectName(cmSourceFile const* file) const;
@@ -233,7 +233,7 @@ public:
std::set<cmLinkItem> const& GetUtilityItems() const;
void ComputeObjectMapping();
void ComputeObjectMapping() const;
cmValue GetFeature(std::string const& feature,
std::string const& config) const;
+3 -1
View File
@@ -422,7 +422,9 @@ void cmGeneratorTarget::ComputeKindedSources(KindedSources& files,
kind = SourceKindCustomCommand;
} else if (!this->Target->IsNormal() && !this->Target->IsImported() &&
fs && (fs->GetType() == cm::FileSetMetadata::CXX_MODULES)) {
kind = SourceKindCxxModuleSource;
kind = this->Target->CxxModuleNeedsInterfaceObjects()
? SourceKindObjectSource
: SourceKindCxxModuleSource;
} else if (this->Target->GetType() == cm::TargetType::UTILITY ||
this->Target->GetType() == cm::TargetType::INTERFACE_LIBRARY
// XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
+42 -4
View File
@@ -2680,7 +2680,8 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
std::vector<std::string> const& linked_target_dirs,
std::vector<std::string> const& forward_modules_from_target_dirs,
std::string const& native_target_dir, std::string const& arg_lang,
std::string const& arg_modmapfmt, cmCxxModuleExportInfo const& export_info)
std::string const& arg_modmapfmt, cmCxxModuleExportInfo const& export_info,
Json::Value const* cxx_interface_objects)
{
// Setup path conversions.
{
@@ -3066,8 +3067,40 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
return {};
};
return cmDyndepCollation::WriteDyndepMetadata(arg_lang, objects, export_info,
cb);
if (!cmDyndepCollation::WriteDyndepMetadata(arg_lang, objects, export_info,
cb)) {
return false;
}
// Write the interface objects response file if configured.
if (cxx_interface_objects && cxx_interface_objects->isObject()) {
Json::Value const& rspFile = (*cxx_interface_objects)["rsp-file"];
Json::Value const& moduleObjects =
(*cxx_interface_objects)["module-objects"];
if (rspFile.isString() && moduleObjects.isObject()) {
std::set<std::string> usedModules;
for (cmScanDepInfo const& object : objects) {
for (auto const& r : object.Requires) {
usedModules.insert(r.LogicalName);
}
}
cmGeneratedFileStream rsp(rspFile.asString());
rsp.SetCopyIfDifferent(true);
auto* lg = this->LocalGenerators.back().get();
for (auto i = moduleObjects.begin(); i != moduleObjects.end(); ++i) {
std::string moduleName = i.key().asString();
if (usedModules.count(moduleName)) {
rsp << lg->ConvertToOutputFormat(
lg->MaybeRelativeToTopBinDir((*i).asString()),
cmOutputConverter::RESPONSE)
<< "\n";
}
}
}
}
return true;
}
int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
@@ -3182,6 +3215,11 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
auto export_info = cmDyndepCollation::ParseExportInfo(tdi);
Json::Value const* cxx_interface_objects = nullptr;
if (tdi.isMember("cxx-interface-objects")) {
cxx_interface_objects = &tdi["cxx-interface-objects"];
}
cmake cm(cmState::Role::Internal);
cm.SetHomeDirectory(dir_top_src);
cm.SetHomeOutputDirectory(dir_top_bld);
@@ -3200,7 +3238,7 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
arg_dd, arg_ddis, module_dir, linked_target_dirs,
forward_modules_from_target_dirs,
native_target_dir, arg_lang, arg_modmapfmt,
*export_info)
*export_info, cxx_interface_objects)
? 0
: 1;
}
+2 -2
View File
@@ -434,8 +434,8 @@ public:
std::vector<std::string> const& linked_target_dirs,
std::vector<std::string> const& forward_modules_from_target_dirs,
std::string const& native_target_dir, std::string const& arg_lang,
std::string const& arg_modmapfmt,
cmCxxModuleExportInfo const& export_info);
std::string const& arg_modmapfmt, cmCxxModuleExportInfo const& export_info,
Json::Value const* cxx_interface_objects = nullptr);
virtual std::string BuildAlias(std::string const& alias,
std::string const& /*config*/) const
+25
View File
@@ -42,6 +42,7 @@
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmTargetTypes.h"
#include "cmValue.h"
@@ -1386,6 +1387,30 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
vars["LINK_PATH"] = frameworkPath + linkPath;
vars["CONFIG"] = config;
// Add interface objects response file to link flags if configured.
{
bool multiConfig = this->GetGlobalGenerator()->IsMultiConfig();
std::string configDir = multiConfig ? cmStrCat('/', config) : "";
bool hasInterfaceObjects = false;
for (auto const& pair :
this->GetGeneratorTarget()->GetSyntheticDeps(config)) {
if (pair.first->Target->CxxModuleNeedsInterfaceObjects()) {
hasInterfaceObjects = true;
break;
}
}
if (hasInterfaceObjects) {
std::string rspPath =
cmStrCat(this->GeneratorTarget->GetSupportDirectory(), configDir,
"/CXXInterfaceObjects.rsp");
vars["LINK_FLAGS"] += cmStrCat(
" @",
this->GetLocalGenerator()->ConvertToOutputFormat(
this->ConvertToNinjaPath(rspPath), cmOutputConverter::SHELL));
linkBuild.ImplicitDeps.emplace_back(this->ConvertToNinjaPath(rspPath));
}
}
// Compute architecture specific link flags. Yes, these go into a different
// variable for executables, probably due to a mistake made when duplicating
// code between the Makefile executable and library generators.
+86 -1
View File
@@ -50,6 +50,7 @@
#include "cmRange.h"
#include "cmRulePlaceholderExpander.h"
#include "cmSourceFile.h"
#include "cmSourceFileLocationKind.h"
#include "cmState.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
@@ -291,7 +292,8 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
R"(" but the source is not classified as a "CXX" source.)"));
}
if (!this->GeneratorTarget->Target->IsNormal()) {
if (!this->GeneratorTarget->Target->IsNormal() &&
!this->GeneratorTarget->Target->CxxModuleNeedsInterfaceObjects()) {
if (this->GetMakefile()
->GetDefinition("CMAKE_CXX_COMPILE_BMI")
.IsEmpty()) {
@@ -1356,6 +1358,25 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements(
build.Outputs.push_back(this->GetDyndepFilePath(language, config));
build.ImplicitOuts.emplace_back(
cmStrCat(this->GetObjectFileDir(config), '/', language, "Modules.json"));
// Add interface objects response file as implicit output if needed.
{
bool multiConfig = this->GetGlobalGenerator()->IsMultiConfig();
std::string configDir = multiConfig ? cmStrCat('/', config) : "";
std::string rspPath =
cmStrCat(this->GeneratorTarget->GetSupportDirectory(), configDir,
"/CXXInterfaceObjects.rsp");
bool hasInterfaceObjects = false;
for (auto const& pair :
this->GeneratorTarget->GetSyntheticDeps(config)) {
if (pair.first->Target->CxxModuleNeedsInterfaceObjects()) {
hasInterfaceObjects = true;
break;
}
}
if (hasInterfaceObjects) {
build.ImplicitOuts.emplace_back(this->ConvertToNinjaPath(rspPath));
}
}
build.ImplicitDeps.emplace_back(
this->GetTargetDependInfoPath(language, config));
{
@@ -2440,6 +2461,70 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang,
config, cb);
#endif
// Collect interface objects from tagged synthetics for the response file.
{
auto const& synthDeps = this->GeneratorTarget->GetSyntheticDeps(config);
if (!synthDeps.empty()) {
bool multiConfig = this->GetGlobalGenerator()->IsMultiConfig();
std::string configDir = multiConfig ? cmStrCat('/', config) : "";
std::string configUpper = cmSystemTools::UpperCase(config);
std::string modulesProp = cmStrCat("IMPORTED_CXX_MODULES_", configUpper);
Json::Value interfaceObjects(Json::objectValue);
Json::Value moduleObjects(Json::objectValue);
bool hasInterfaceObjects = false;
for (auto const& pair : synthDeps) {
auto const* nativeGT = pair.first;
if (!nativeGT->Target->CxxModuleNeedsInterfaceObjects()) {
continue;
}
hasInterfaceObjects = true;
for (auto const* synthGT : pair.second) {
std::string objDir =
cmStrCat(synthGT->GetSupportDirectory(), configDir);
cmValue importedModules = synthGT->Target->GetProperty(modulesProp);
if (!importedModules) {
continue;
}
for (auto const& entry : cmList{ *importedModules }) {
auto nameSep = entry.find('=');
if (nameSep == std::string::npos) {
continue;
}
auto moduleName = entry.substr(0, nameSep);
auto nameAndPath = entry.substr(nameSep + 1);
auto commaSep = nameAndPath.find(',');
std::string sourcePath = commaSep == std::string::npos
? nameAndPath
: nameAndPath.substr(0, commaSep);
cmSourceFile const* sf = synthGT->Makefile->GetSource(
sourcePath, cmSourceFileLocationKind::Known);
if (!sf) {
continue;
}
std::string const& objName = synthGT->GetObjectName(sf);
std::string objPath = cmStrCat(objDir, '/', objName);
moduleObjects[moduleName] = objPath;
}
}
}
if (hasInterfaceObjects) {
std::string rspPath =
cmStrCat(this->GeneratorTarget->GetSupportDirectory(), configDir,
"/CXXInterfaceObjects.rsp");
interfaceObjects["rsp-file"] = rspPath;
interfaceObjects["module-objects"] = moduleObjects;
tdi["cxx-interface-objects"] = interfaceObjects;
}
}
}
std::string const tdin = this->GetTargetDependInfoPath(lang, config);
cmGeneratedFileStream tdif(tdin);
tdif << tdi;
+11
View File
@@ -632,6 +632,7 @@ public:
bool IsSymbolic = false;
bool IsForTryCompile = false;
bool IsExportPassthrough = false;
bool CxxModuleNeedsInterfaceObjects = false;
cmTarget::Visibility TargetVisibility;
std::set<BT<std::pair<std::string, bool>>> Utilities;
std::set<std::string> CodegenDependencies;
@@ -1190,6 +1191,16 @@ cmGlobalGenerator* cmTarget::GetGlobalGenerator() const
return this->impl->Makefile->GetGlobalGenerator();
}
bool cmTarget::CxxModuleNeedsInterfaceObjects() const
{
return this->impl->CxxModuleNeedsInterfaceObjects;
}
void cmTarget::SetCxxModuleNeedsInterfaceObjects(bool v)
{
this->impl->CxxModuleNeedsInterfaceObjects = v;
}
BTs<std::string> const* cmTarget::GetLanguageStandardProperty(
std::string const& propertyName) const
{
+4
View File
@@ -314,6 +314,10 @@ public:
cmStringRange GetInstallIncludeDirectoriesEntries(
cmTargetExport const& te) const;
//! Control if the target generates object files for module interfaces
bool CxxModuleNeedsInterfaceObjects() const;
void SetCxxModuleNeedsInterfaceObjects(bool);
BTs<std::string> const* GetLanguageStandardProperty(
std::string const& propertyName) const;
@@ -2,6 +2,11 @@ import std;
int main(int argc, char* argv[])
{
// std::chrono::current_zone() references _Global_tzdb_list, which is
// provided by std.ixx.obj on MSVC. Ensure we get a failed link if this
// definition isn't available.
static_cast<void>(std::chrono::current_zone());
if (argc > 0 && argv[0]) {
std::string argv0 = argv[0];
std::cout << "program: " << argv0 << std::endl;