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.
This commit is contained in:
Ben Boeckel
2023-02-07 18:01:00 -05:00
parent 3dc6676ecc
commit 80d6544398
8 changed files with 344 additions and 1 deletions

View File

@@ -7,6 +7,7 @@
#include <utility>
#include <vector>
#include <cmext/algorithm>
#include <cmext/string_view>
#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();

View File

@@ -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<std::string> directories);
const std::vector<BT<std::string>>& GetDirectoryEntries() const

View File

@@ -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<cmGeneratorTarget>(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<std::string>(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)) {

View File

@@ -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<std::string, cmFileSet const*> FileSetCache;
std::map<cmGeneratorTarget const*, std::vector<cmGeneratorTarget const*>>
SyntheticDeps;
};
mutable std::map<std::string, InfoByConfig> Configs;
};

View File

@@ -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
// $<$<TARGET_NAME_IF_EXISTS:uses_imported>:synth1>
// $<$<TARGET_NAME_IF_EXISTS:other_uses_imported>: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<cmGeneratorTarget*> genTargets;
genTargets.reserve(gen->GetGeneratorTargets().size());
for (auto const& tgt : gen->GetGeneratorTargets()) {
genTargets.push_back(tgt.get());
}
for (auto* tgt : genTargets) {
std::vector<std::string> 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) {

View File

@@ -662,6 +662,8 @@ protected:
virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const;
bool DiscoverSyntheticTargets();
bool AddHeaderSetVerification();
bool AddAutomaticSources();

View File

@@ -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<std::string> 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);

View File

@@ -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;