From 80d65443982ca1b2c98c84ae86e2bfccdbdd7678 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 7 Feb 2023 18:01:00 -0500 Subject: cxxmodules: generate synthetic targets as an initial pass We need to be able to construct BMIs that will be usable from the client modules for the target importing the module, so create BMI-only compilation rules for `IMPORTED` targets to create these BMIs. --- Source/cmFileSet.cxx | 7 ++ Source/cmFileSet.h | 2 + Source/cmGeneratorTarget.cxx | 104 ++++++++++++++++++++++++- Source/cmGeneratorTarget.h | 6 ++ Source/cmGlobalGenerator.cxx | 40 ++++++++++ Source/cmGlobalGenerator.h | 2 + Source/cmTarget.cxx | 180 +++++++++++++++++++++++++++++++++++++++++++ Source/cmTarget.h | 4 + 8 files changed, 344 insertions(+), 1 deletion(-) diff --git a/Source/cmFileSet.cxx b/Source/cmFileSet.cxx index 48a2570..bcf7fba 100644 --- a/Source/cmFileSet.cxx +++ b/Source/cmFileSet.cxx @@ -7,6 +7,7 @@ #include #include +#include #include #include "cmsys/RegularExpression.hxx" @@ -88,6 +89,12 @@ cmFileSet::cmFileSet(cmake& cmakeInstance, std::string name, std::string type, { } +void cmFileSet::CopyEntries(cmFileSet const* fs) +{ + cm::append(this->DirectoryEntries, fs->DirectoryEntries); + cm::append(this->FileEntries, fs->FileEntries); +} + void cmFileSet::ClearDirectoryEntries() { this->DirectoryEntries.clear(); diff --git a/Source/cmFileSet.h b/Source/cmFileSet.h index 54d430c..c508e2b 100644 --- a/Source/cmFileSet.h +++ b/Source/cmFileSet.h @@ -41,6 +41,8 @@ public: const std::string& GetType() const { return this->Type; } cmFileSetVisibility GetVisibility() const { return this->Visibility; } + void CopyEntries(cmFileSet const* fs); + void ClearDirectoryEntries(); void AddDirectoryEntry(BT directories); const std::vector>& GetDirectoryEntries() const diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index c15db5b..70f51b0 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -27,7 +27,9 @@ #include "cmAlgorithms.h" #include "cmComputeLinkInformation.h" +#include "cmCryptoHash.h" #include "cmCustomCommandGenerator.h" +#include "cmCxxModuleUsageEffects.h" #include "cmEvaluatedTargetProperty.h" #include "cmExperimental.h" #include "cmFileSet.h" @@ -52,6 +54,7 @@ #include "cmStandardLevelResolver.h" #include "cmState.h" #include "cmStringAlgorithms.h" +#include "cmSyntheticTargetCache.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetLinkLibraryType.h" @@ -8230,6 +8233,96 @@ void ComputeLinkImplTransitive(cmGeneratorTarget const* self, } } +bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, + std::string const& config) +{ + cmOptionalLinkImplementation impl; + this->ComputeLinkImplementationLibraries(config, impl, this, + LinkInterfaceFor::Link); + + cmCxxModuleUsageEffects usage(this); + + auto& SyntheticDeps = this->Configs[config].SyntheticDeps; + + for (auto const& entry : impl.Libraries) { + auto const* gt = entry.Target; + if (!gt || !gt->IsImported()) { + continue; + } + + if (gt->HaveCxx20ModuleSources()) { + auto hasher = cmCryptoHash::New("SHA3_512"); + constexpr size_t HASH_TRUNCATION = 12; + auto dirhash = hasher->HashString( + gt->GetLocalGenerator()->GetCurrentBinaryDirectory()); + std::string safeName = gt->GetName(); + cmSystemTools::ReplaceString(safeName, ":", "_"); + auto targetIdent = + hasher->HashString(cmStrCat("@d_", dirhash, "@u_", usage.GetHash())); + std::string targetName = + cmStrCat(safeName, "@synth_", targetIdent.substr(0, HASH_TRUNCATION)); + + // Check the cache to see if this instance of the imported target has + // already been created. + auto cached = cache.CxxModuleTargets.find(targetName); + cmGeneratorTarget const* synthDep = nullptr; + if (cached == cache.CxxModuleTargets.end()) { + auto const* model = gt->Target; + auto* mf = gt->Makefile; + auto* lg = gt->GetLocalGenerator(); + auto* tgt = mf->AddSynthesizedTarget(cmStateEnums::INTERFACE_LIBRARY, + targetName); + + // Copy relevant information from the existing IMPORTED target. + + // Copy policies to the target. + tgt->CopyPolicyStatuses(model); + + // Copy file sets. + { + auto fsNames = model->GetAllFileSetNames(); + for (auto const& fsName : fsNames) { + auto const* fs = model->GetFileSet(fsName); + if (!fs) { + mf->IssueMessage(MessageType::INTERNAL_ERROR, + cmStrCat("Failed to find file set named '", + fsName, "' on target '", + tgt->GetName(), '\'')); + continue; + } + auto* newFs = tgt + ->GetOrCreateFileSet(fs->GetName(), fs->GetType(), + fs->GetVisibility()) + .first; + newFs->CopyEntries(fs); + } + } + + // Copy imported C++ module properties. + tgt->CopyImportedCxxModulesEntries(model); + + // Copy other properties which may affect the C++ module BMI + // generation. + tgt->CopyImportedCxxModulesProperties(model); + + // Apply usage requirements to the target. + usage.ApplyToTarget(tgt); + + // Create the generator target and attach it to the local generator. + auto gtp = cm::make_unique(tgt, lg); + synthDep = gtp.get(); + lg->AddGeneratorTarget(std::move(gtp)); + } else { + synthDep = cached->second; + } + + SyntheticDeps[gt].push_back(synthDep); + } + } + + return true; +} + void cmGeneratorTarget::ComputeLinkImplementationLibraries( const std::string& config, cmOptionalLinkImplementation& impl, cmGeneratorTarget const* head, LinkInterfaceFor implFor) const @@ -8237,6 +8330,7 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries( cmLocalGenerator const* lg = this->LocalGenerator; cmMakefile const* mf = lg->GetMakefile(); cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries(); + auto const& synthTargetsForConfig = this->Configs[config].SyntheticDeps; // Collect libraries directly linked in this configuration. for (auto const& entry : entryRange) { // Keep this logic in sync with ExpandLinkItems. @@ -8326,7 +8420,15 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries( // The entry is meant for this configuration. cmLinkItem item = this->ResolveLinkItem(BT(name, entry.Backtrace), lg); - if (!item.Target) { + if (item.Target) { + auto depsForTarget = synthTargetsForConfig.find(item.Target); + if (depsForTarget != synthTargetsForConfig.end()) { + for (auto const* depForTarget : depsForTarget->second) { + cmLinkItem synthItem(depForTarget, item.Cross, item.Backtrace); + impl.Libraries.emplace_back(std::move(synthItem), false); + } + } + } else { // Report explicitly linked object files separately. std::string const& maybeObj = item.AsStr(); if (cmSystemTools::FileIsFullPath(maybeObj)) { diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 3adf5b8..f347133 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -31,6 +31,7 @@ class cmGlobalGenerator; class cmLocalGenerator; class cmMakefile; class cmSourceFile; +struct cmSyntheticTargetCache; class cmTarget; struct cmGeneratorExpressionContext; @@ -932,6 +933,9 @@ public: std::string GetImportedXcFrameworkPath(const std::string& config) const; + bool DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, + std::string const& config); + private: void AddSourceCommon(const std::string& src, bool before = false); @@ -1307,6 +1311,8 @@ private: { bool BuiltFileSetCache = false; std::map FileSetCache; + std::map> + SyntheticDeps; }; mutable std::map Configs; }; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 9af6e9b..d5099ee 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -55,6 +55,7 @@ #include "cmStateDirectory.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" +#include "cmSyntheticTargetCache.h" #include "cmSystemTools.h" #include "cmValue.h" #include "cmVersion.h" @@ -1560,6 +1561,17 @@ bool cmGlobalGenerator::Compute() } #endif + // Iterate through all targets and set up C++20 module targets. + // Create target templates for each imported target with C++20 modules. + // INTERFACE library with BMI-generating rules and a collation step? + // Maybe INTERFACE libraries with modules files should just do BMI-only? + // Make `add_dependencies(imported_target + // $<$:synth1> + // $<$:synth2>)` + if (!this->DiscoverSyntheticTargets()) { + return false; + } + // Add generator specific helper commands for (const auto& localGen : this->LocalGenerators) { localGen->AddHelperCommands(); @@ -1784,6 +1796,34 @@ void cmGlobalGenerator::ComputeTargetOrder(cmGeneratorTarget const* gt, entry->second = index++; } +bool cmGlobalGenerator::DiscoverSyntheticTargets() +{ + cmSyntheticTargetCache cache; + + for (auto const& gen : this->LocalGenerators) { + // Because DiscoverSyntheticTargets() adds generator targets, we need to + // cache the existing list of generator targets before starting. + std::vector genTargets; + genTargets.reserve(gen->GetGeneratorTargets().size()); + for (auto const& tgt : gen->GetGeneratorTargets()) { + genTargets.push_back(tgt.get()); + } + + for (auto* tgt : genTargets) { + std::vector const& configs = + tgt->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + + for (auto const& config : configs) { + if (!tgt->DiscoverSyntheticTargets(cache, config)) { + return false; + } + } + } + } + + return true; +} + bool cmGlobalGenerator::AddHeaderSetVerification() { for (auto const& gen : this->LocalGenerators) { diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 563ebb6..6d29dc1 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -662,6 +662,8 @@ protected: virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const; + bool DiscoverSyntheticTargets(); + bool AddHeaderSetVerification(); bool AddAutomaticSources(); diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 7d4b497..ace93e8 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -1748,6 +1748,186 @@ cmBTStringRange cmTarget::GetLinkInterfaceDirectExcludeEntries() const return cmMakeRange(this->impl->InterfaceLinkLibrariesDirectExclude.Entries); } +void cmTarget::CopyPolicyStatuses(cmTarget const* tgt) +{ + // Normal targets cannot be the target of a copy. + assert(!this->IsNormal()); + // Imported targets cannot be the target of a copy. + assert(!this->IsImported()); + // Only imported targets can be the source of a copy. + assert(tgt->IsImported()); + + this->impl->PolicyMap = tgt->impl->PolicyMap; +} + +void cmTarget::CopyImportedCxxModulesEntries(cmTarget const* tgt) +{ + // Normal targets cannot be the target of a copy. + assert(!this->IsNormal()); + // Imported targets cannot be the target of a copy. + assert(!this->IsImported()); + // Only imported targets can be the source of a copy. + assert(tgt->IsImported()); + + this->impl->IncludeDirectories.Entries.clear(); + this->impl->IncludeDirectories.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesIncludeDirectories.Entries)); + this->impl->CompileDefinitions.Entries.clear(); + this->impl->CompileDefinitions.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesCompileDefinitions.Entries)); + this->impl->CompileFeatures.Entries.clear(); + this->impl->CompileFeatures.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesCompileFeatures.Entries)); + this->impl->CompileOptions.Entries.clear(); + this->impl->CompileOptions.CopyFromEntries( + cmMakeRange(tgt->impl->ImportedCxxModulesCompileOptions.Entries)); + this->impl->LinkLibraries.Entries.clear(); + this->impl->LinkLibraries.CopyFromEntries( + cmMakeRange(tgt->impl->LinkLibraries.Entries)); + + // Copy the C++ module fileset entries from `tgt`'s `INTERFACE` to this + // target's `PRIVATE`. + this->impl->CxxModulesFileSets.SelfEntries.Entries.clear(); + this->impl->CxxModulesFileSets.SelfEntries.Entries = + tgt->impl->CxxModulesFileSets.InterfaceEntries.Entries; +} + +void cmTarget::CopyImportedCxxModulesProperties(cmTarget const* tgt) +{ + // Normal targets cannot be the target of a copy. + assert(!this->IsNormal()); + // Imported targets cannot be the target of a copy. + assert(!this->IsImported()); + // Only imported targets can be the source of a copy. + assert(tgt->IsImported()); + + // The list of properties that are relevant here include: + // - compilation-specific properties for any language or platform + // - compilation-specific properties for C++ + // - build graph-specific properties that affect compilation + // - IDE metadata properties + // - static analysis properties + + static const std::string propertiesToCopy[] = { + // Compilation properties + "DEFINE_SYMBOL", + "DEPRECATION", + "NO_SYSTEM_FROM_IMPORTED", + "POSITION_INDEPENDENT_CODE", + "VISIBILITY_INLINES_HIDDEN", + // -- Platforms + // ---- Android + "ANDROID_API", + "ANDROID_API_MIN", + "ANDROID_ARCH", + "ANDROID_STL_TYPE", + // ---- macOS + "OSX_ARCHITECTURES", + // ---- Windows + "MSVC_DEBUG_INFORMATION_FORMAT", + "MSVC_RUNTIME_LIBRARY", + "VS_PLATFORM_TOOLSET", + // ---- OpenWatcom + "WATCOM_RUNTIME_LIBRARY", + // -- Language + // ---- C++ + "CXX_COMPILER_LAUNCHER", + "CXX_STANDARD", + "CXX_STANDARD_REQUIRED", + "CXX_EXTENSIONS", + "CXX_VISIBILITY_PRESET", + + // Static analysis + "CXX_CLANG_TIDY", + "CXX_CLANG_TIDY_EXPORT_FIXES_DIR", + "CXX_CPPLINT", + "CXX_CPPCHECK", + "CXX_INCLUDE_WHAT_YOU_USE", + + // Build graph properties + "EXCLUDE_FROM_ALL", + "EXCLUDE_FROM_DEFAULT_BUILD", + "OPTIMIZE_DEPENDENCIES", + // -- Ninja + "JOB_POOL_COMPILE", + // -- Visual Studio + "VS_NO_COMPILE_BATCHING", + "VS_PROJECT_IMPORT", + + // Metadata + "EchoString", + "EXPORT_COMPILE_COMMANDS", + "FOLDER", + "LABELS", + "PROJECT_LABEL", + "SYSTEM", + }; + + auto copyProperty = [this, tgt](std::string const& prop) -> cmValue { + cmValue value = tgt->GetProperty(prop); + // Always set the property; it may have been explicitly unset. + this->SetProperty(prop, value); + return value; + }; + + for (auto const& prop : propertiesToCopy) { + copyProperty(prop); + } + + static const cm::static_string_view perConfigPropertiesToCopy[] = { + "EXCLUDE_FROM_DEFAULT_BUILD_"_s, + "IMPORTED_CXX_MODULES_"_s, + "MAP_IMPORTED_CONFIG_"_s, + "OSX_ARCHITECTURES_"_s, + }; + + std::vector configNames = + this->impl->Makefile->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + for (std::string const& configName : configNames) { + std::string configUpper = cmSystemTools::UpperCase(configName); + for (auto const& perConfigProp : perConfigPropertiesToCopy) { + copyProperty(cmStrCat(perConfigProp, configUpper)); + } + } + + if (this->GetGlobalGenerator()->IsXcode()) { + cmValue xcodeGenerateScheme = copyProperty("XCODE_GENERATE_SCHEME"); + + // TODO: Make sure these show up on the imported target in the first place + // XCODE_ATTRIBUTE_??? + + if (xcodeGenerateScheme.IsOn()) { +#ifdef __APPLE__ + static const std::string xcodeSchemePropertiesToCopy[] = { + // FIXME: Do all of these apply? Do they matter? + "XCODE_SCHEME_ADDRESS_SANITIZER", + "XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN", + "XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER", + "XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS", + "XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE", + "XCODE_SCHEME_ENABLE_GPU_API_VALIDATION", + "XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION", + "XCODE_SCHEME_GUARD_MALLOC", + "XCODE_SCHEME_LAUNCH_CONFIGURATION", + "XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP", + "XCODE_SCHEME_MALLOC_GUARD_EDGES", + "XCODE_SCHEME_MALLOC_SCRIBBLE", + "XCODE_SCHEME_MALLOC_STACK", + "XCODE_SCHEME_THREAD_SANITIZER", + "XCODE_SCHEME_THREAD_SANITIZER_STOP", + "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER", + "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP", + "XCODE_SCHEME_ZOMBIE_OBJECTS", + }; + + for (auto const& xcodeProperty : xcodeSchemePropertiesToCopy) { + copyProperty(xcodeProperty); + } +#endif + } + } +} + cmBTStringRange cmTarget::GetHeaderSetsEntries() const { return cmMakeRange(this->impl->HeadersFileSets.SelfEntries.Entries); diff --git a/Source/cmTarget.h b/Source/cmTarget.h index dae997f..b77ea0c 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -291,6 +291,10 @@ public: cmBTStringRange GetLinkInterfaceDirectEntries() const; cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const; + void CopyPolicyStatuses(cmTarget const* tgt); + void CopyImportedCxxModulesEntries(cmTarget const* tgt); + void CopyImportedCxxModulesProperties(cmTarget const* tgt); + cmBTStringRange GetHeaderSetsEntries() const; cmBTStringRange GetCxxModuleSetsEntries() const; -- cgit v0.12