diff options
author | Brad King <brad.king@kitware.com> | 2024-04-15 13:56:40 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2024-04-15 13:56:53 (GMT) |
commit | ca449572ef8b1c6066e88879795900aea9727834 (patch) | |
tree | b7f4f67018d8fead9292e9d0133fa02e22879918 /Source | |
parent | abd572de909de2f30ca93e3b1cca1311e33c4f75 (diff) | |
parent | 429902ebad620c877b869068583d2a32bb6197b4 (diff) | |
download | CMake-ca449572ef8b1c6066e88879795900aea9727834.zip CMake-ca449572ef8b1c6066e88879795900aea9727834.tar.gz CMake-ca449572ef8b1c6066e88879795900aea9727834.tar.bz2 |
Merge topic 'cxxmodules-import-std'
429902ebad Clang: support creating a target for imported modules
4617f272b4 MSVC: support `import std`
62a71047bb cmGraphVizWriter: ignore `__cmake_`-prefixed targets
442086c1dc fileapi: ignore `__cmake_`-prefixed targets
ffe74289b3 CMakeDetermineCompilerId: extract C++ standard library impl
15bbd1d9b8 Experimental: add an experimental feature gate for `import std`
f80c60df02 CMakeDetermineCompilerSupport: construct C++ modules targets
19341e2582 ci: enable `import_std23` C++ module tests on MSVC
...
Acked-by: Kitware Robot <kwrobot@kitware.com>
Tested-by: buildbot <buildbot@kitware.com>
Merge-request: !9337
Diffstat (limited to 'Source')
-rw-r--r-- | Source/cmExperimental.cxx | 9 | ||||
-rw-r--r-- | Source/cmExperimental.h | 1 | ||||
-rw-r--r-- | Source/cmExportFileGenerator.cxx | 41 | ||||
-rw-r--r-- | Source/cmFileAPICodemodel.cxx | 5 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.cxx | 124 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.h | 1 | ||||
-rw-r--r-- | Source/cmGlobalGenerator.cxx | 49 | ||||
-rw-r--r-- | Source/cmGlobalGenerator.h | 1 | ||||
-rw-r--r-- | Source/cmGraphVizWriter.cxx | 3 | ||||
-rw-r--r-- | Source/cmStandardLevelResolver.cxx | 15 | ||||
-rw-r--r-- | Source/cmStandardLevelResolver.h | 3 | ||||
-rw-r--r-- | Source/cmTarget.cxx | 2 |
12 files changed, 234 insertions, 20 deletions
diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx index 8adf4e2..a2e6e70 100644 --- a/Source/cmExperimental.cxx +++ b/Source/cmExperimental.cxx @@ -37,6 +37,15 @@ cmExperimental::FeatureData LookupTable[] = { {}, cmExperimental::TryCompileCondition::Always, false }, + // CxxImportStd + { "CxxImportStd", + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508", + "CMAKE_EXPERIMENTAL_CXX_IMPORT_STD", + "CMake's support for `import std;` in C++23 and newer is experimental. It " + "is meant only for experimentation and feedback to CMake developers.", + {}, + cmExperimental::TryCompileCondition::Always, + false }, }; static_assert(sizeof(LookupTable) / sizeof(LookupTable[0]) == static_cast<size_t>(cmExperimental::Feature::Sentinel), diff --git a/Source/cmExperimental.h b/Source/cmExperimental.h index 1abfbc3..05764f9 100644 --- a/Source/cmExperimental.h +++ b/Source/cmExperimental.h @@ -19,6 +19,7 @@ public: { ExportPackageDependencies, WindowsKernelModeDriver, + CxxImportStd, Sentinel, }; diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 7867a8e..9bd7f49 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -1388,6 +1388,12 @@ void cmExportFileGenerator::GenerateImportedFileChecksCode( os << ")\n\n"; } +enum class ExportWhen +{ + Defined, + Always, +}; + enum class PropertyType { Strings, @@ -1409,6 +1415,12 @@ bool PropertyTypeIsForPaths(PropertyType pt) } } +struct ModuleTargetPropertyTable +{ + cm::static_string_view Name; + ExportWhen Cond; +}; + struct ModulePropertyTable { cm::static_string_view Name; @@ -1424,18 +1436,29 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( return true; } - const cm::static_string_view exportedDirectModuleProperties[] = { - "CXX_EXTENSIONS"_s, + const ModuleTargetPropertyTable exportedDirectModuleProperties[] = { + { "CXX_EXTENSIONS"_s, ExportWhen::Defined }, + // Always define this property as it is an intrinsic property of the target + // and should not be inherited from the in-scope `CMAKE_CXX_MODULE_STD` + // variable. + // + // TODO(cxxmodules): A future policy may make this "ON" based on the target + // policies if unset. Add a new `ExportWhen` condition to handle it when + // this happens. + { "CXX_MODULE_STD"_s, ExportWhen::Always }, }; - for (auto const& propName : exportedDirectModuleProperties) { - auto const propNameStr = std::string(propName); - cmValue prop = gte->Target->GetComputedProperty( + for (auto const& prop : exportedDirectModuleProperties) { + auto const propNameStr = std::string(prop.Name); + cmValue propValue = gte->Target->GetComputedProperty( propNameStr, *gte->Target->GetMakefile()); - if (!prop) { - prop = gte->Target->GetProperty(propNameStr); + if (!propValue) { + propValue = gte->Target->GetProperty(propNameStr); } - if (prop) { - properties[propNameStr] = cmGeneratorExpression::Preprocess(*prop, ctx); + if (propValue) { + properties[propNameStr] = + cmGeneratorExpression::Preprocess(*propValue, ctx); + } else if (prop.Cond == ExportWhen::Always) { + properties[propNameStr] = ""; } } diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index e9302da..869b94a 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx @@ -685,6 +685,11 @@ Json::Value CodemodelConfig::DumpTargets() continue; } + // Ignore targets starting with `__cmake_` as they are internal. + if (cmHasLiteralPrefix(gt->GetName(), "__cmake_")) { + continue; + } + targets.append(this->DumpTarget(gt, targets.size())); } diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 2ec1a29..be82099 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -32,6 +32,7 @@ #include "cmCustomCommandGenerator.h" #include "cmCxxModuleUsageEffects.h" #include "cmEvaluatedTargetProperty.h" +#include "cmExperimental.h" #include "cmFileSet.h" #include "cmFileTimes.h" #include "cmGeneratedFileStream.h" @@ -8411,9 +8412,119 @@ void ComputeLinkImplTransitive(cmGeneratorTarget const* self, } } +bool cmGeneratorTarget::ApplyCXXStdTargets() +{ + cmStandardLevelResolver standardResolver(this->Makefile); + cmStandardLevel const cxxStd23 = + *standardResolver.LanguageStandardLevel("CXX", "23"); + std::vector<std::string> const& configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + auto std_prop = this->GetProperty("CXX_MODULE_STD"); + if (!std_prop) { + // TODO(cxxmodules): Add a target policy to flip the default here. Set + // `std_prop` based on it. + return true; + } + + std::string std_prop_value; + if (std_prop) { + // Evaluate generator expressions. + cmGeneratorExpression ge(*this->LocalGenerator->GetCMakeInstance()); + auto cge = ge.Parse(*std_prop); + if (!cge) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(R"(The "CXX_MODULE_STD" property on the target ")", + this->GetName(), "\" is not a valid generator expression.")); + return false; + } + // But do not allow context-sensitive queries. Whether a target uses + // `import std` should not depend on configuration or properties of the + // consumer (head target). The link language also shouldn't matter, so ban + // it as well. + if (cge->GetHadHeadSensitiveCondition()) { + // Not reachable; all target-sensitive genexes actually fail to parse. + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(R"(The "CXX_MODULE_STD" property on the target ")", + this->GetName(), + "\" contains a condition that queries the " + "consuming target which is not supported.")); + return false; + } + if (cge->GetHadLinkLanguageSensitiveCondition()) { + // Not reachable; all link language genexes actually fail to parse. + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(R"(The "CXX_MODULE_STD" property on the target ")", + this->GetName(), + "\" contains a condition that queries the " + "link language which is not supported.")); + return false; + } + std_prop_value = cge->Evaluate(this->LocalGenerator, ""); + if (cge->GetHadContextSensitiveCondition()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(R"(The "CXX_MODULE_STD" property on the target ")", + this->GetName(), + "\" contains a context-sensitive condition " + "that is not supported.")); + return false; + } + } + auto use_std = cmIsOn(std_prop_value); + + // If we have a value and it is not true, there's nothing to do. + if (std_prop && !use_std) { + return true; + } + + for (auto const& config : configs) { + if (this->HaveCxxModuleSupport(config) != Cxx20SupportLevel::Supported) { + continue; + } + + cm::optional<cmStandardLevel> explicitLevel = + this->GetExplicitStandardLevel("CXX", config); + if (!explicitLevel || *explicitLevel < cxxStd23) { + continue; + } + + auto const targetName = cmStrCat( + "__CMAKE::CXX", standardResolver.GetLevelString("CXX", *explicitLevel)); + if (!this->Makefile->FindTargetToUse(targetName)) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + R"(The "CXX_MODULE_STD" property on the target ")", this->GetName(), + "\" requires that the \"", targetName, + "\" target exist, but it was not provided by the toolchain.")); + break; + } + + // Check the experimental feature here as well. A toolchain may have + // provided the target and skipped the check in the toolchain preparation + // logic. + if (!cmExperimental::HasSupportEnabled( + *this->Makefile, cmExperimental::Feature::CxxImportStd)) { + break; + } + + this->Target->AppendProperty( + "LINK_LIBRARIES", + cmStrCat("$<BUILD_LOCAL_INTERFACE:$<$<CONFIG:", config, ">:", targetName, + ">>")); + } + + return true; +} + bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, std::string const& config) { + std::vector<std::string> allConfigs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); cmOptionalLinkImplementation impl; this->ComputeLinkImplementationLibraries(config, impl, this, LinkInterfaceFor::Link); @@ -8488,9 +8599,22 @@ bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, // Create the generator target and attach it to the local generator. auto gtp = cm::make_unique<cmGeneratorTarget>(tgt, lg); + synthDep = gtp.get(); cache.CxxModuleTargets[targetName] = synthDep; + + // See `localGen->ComputeTargetCompileFeatures()` call in + // `cmGlobalGenerator::Compute` for where non-synthetic targets resolve + // this. + for (auto const& innerConfig : allConfigs) { + gtp->ComputeCompileFeatures(innerConfig); + } + // See `cmGlobalGenerator::ApplyCXXStdTargets` in + // `cmGlobalGenerator::Compute` for non-synthetic target resolutions. + gtp->ApplyCXXStdTargets(); + gtp->DiscoverSyntheticTargets(cache, config); + lg->AddGeneratorTarget(std::move(gtp)); } else { synthDep = cached->second; diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index fd4b2ec..28ad898 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -961,6 +961,7 @@ public: std::string GetImportedXcFrameworkPath(const std::string& config) const; + bool ApplyCXXStdTargets(); bool DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, std::string const& config); diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 213142a..e397fa2 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -1581,6 +1581,29 @@ bool cmGlobalGenerator::Compute() } #endif + // Perform up-front computation in order to handle errors (such as unknown + // features) at this point. While processing the compile features we also + // calculate and cache the language standard required by the compile + // features. + // + // Synthetic targets performed this inside of + // `cmLocalGenerator::DiscoverSyntheticTargets` + for (const auto& localGen : this->LocalGenerators) { + if (!localGen->ComputeTargetCompileFeatures()) { + return false; + } + } + + // We now have all targets set up and std levels constructed. Add + // `__CMAKE::CXX*` targets as link dependencies to all targets which need + // them. + // + // Synthetic targets performed this inside of + // `cmLocalGenerator::DiscoverSyntheticTargets` + if (!this->ApplyCXXStdTargets()) { + return false; + } + // 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? @@ -1588,6 +1611,9 @@ bool cmGlobalGenerator::Compute() // Make `add_dependencies(imported_target // $<$<TARGET_NAME_IF_EXISTS:uses_imported>:synth1> // $<$<TARGET_NAME_IF_EXISTS:other_uses_imported>:synth2>)` + // + // Note that synthetic target creation performs the above marked + // steps on the created targets. if (!this->DiscoverSyntheticTargets()) { return false; } @@ -1597,16 +1623,6 @@ bool cmGlobalGenerator::Compute() localGen->AddHelperCommands(); } - // Perform up-front computation in order to handle errors (such as unknown - // features) at this point. While processing the compile features we also - // calculate and cache the language standard required by the compile - // features. - for (const auto& localGen : this->LocalGenerators) { - if (!localGen->ComputeTargetCompileFeatures()) { - return false; - } - } - // Add automatically generated sources (e.g. unity build). // Add unity sources after computing compile features. Unity sources do // not change the set of languages or features, but we need to know them @@ -1824,6 +1840,19 @@ void cmGlobalGenerator::ComputeTargetOrder(cmGeneratorTarget const* gt, entry->second = index++; } +bool cmGlobalGenerator::ApplyCXXStdTargets() +{ + for (auto const& gen : this->LocalGenerators) { + for (auto const& tgt : gen->GetGeneratorTargets()) { + if (!tgt->ApplyCXXStdTargets()) { + return false; + } + } + } + + return true; +} + bool cmGlobalGenerator::DiscoverSyntheticTargets() { cmSyntheticTargetCache cache; diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index ba39768..1ca02d9 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -674,6 +674,7 @@ protected: virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const; + bool ApplyCXXStdTargets(); bool DiscoverSyntheticTargets(); bool AddHeaderSetVerification(); diff --git a/Source/cmGraphVizWriter.cxx b/Source/cmGraphVizWriter.cxx index a8a7abb..6c3afef 100644 --- a/Source/cmGraphVizWriter.cxx +++ b/Source/cmGraphVizWriter.cxx @@ -284,7 +284,8 @@ void cmGraphVizWriter::Write() // Reserved targets have inconsistent names across platforms (e.g. 'all' // vs. 'ALL_BUILD'), which can disrupt the traversal ordering. // We don't need or want them anyway. - if (!cmGlobalGenerator::IsReservedTarget(gt->GetName())) { + if (!cmGlobalGenerator::IsReservedTarget(gt->GetName()) && + !cmHasLiteralPrefix(gt->GetName(), "__cmake_")) { sortedGeneratorTargets.insert(gt.get()); } } diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx index c9ed380..44c4ae1 100644 --- a/Source/cmStandardLevelResolver.cxx +++ b/Source/cmStandardLevelResolver.cxx @@ -541,6 +541,21 @@ std::string cmStandardLevelResolver::GetEffectiveStandard( return mapping->second.GetEffectiveStandard(this->Makefile, target, config); } +std::string cmStandardLevelResolver::GetLevelString( + std::string const& lang, cmStandardLevel const& level) const +{ + auto mapping = StandardComputerMapping.find(lang); + if (mapping == StandardComputerMapping.end()) { + return {}; + } + + if (mapping->second.LevelsAsStrings.size() <= level.Index()) { + return {}; + } + + return mapping->second.LevelsAsStrings[level.Index()]; +} + bool cmStandardLevelResolver::AddRequiredTargetFeature( cmTarget* target, const std::string& feature, std::string* error) const { diff --git a/Source/cmStandardLevelResolver.h b/Source/cmStandardLevelResolver.h index 29cab55..523aa73 100644 --- a/Source/cmStandardLevelResolver.h +++ b/Source/cmStandardLevelResolver.h @@ -29,6 +29,9 @@ public: std::string const& lang, std::string const& config) const; + std::string GetLevelString(std::string const& lang, + cmStandardLevel const& level) const; + bool AddRequiredTargetFeature(cmTarget* target, const std::string& feature, std::string* error = nullptr) const; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 3f7e29b..1284130 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -412,6 +412,7 @@ TargetProperty const StaticTargetProperties[] = { COMMON_LANGUAGE_PROPERTIES(C), // ---- C++ COMMON_LANGUAGE_PROPERTIES(CXX), + { "CXX_MODULE_STD"_s, IC::CanCompileSources }, // ---- CSharp { "DOTNET_SDK"_s, IC::NonImportedTarget }, { "DOTNET_TARGET_FRAMEWORK"_s, IC::TargetWithCommands }, @@ -1842,6 +1843,7 @@ void cmTarget::CopyImportedCxxModulesProperties(cmTarget const* tgt) "CXX_STANDARD_REQUIRED", "CXX_EXTENSIONS", "CXX_VISIBILITY_PRESET", + "CXX_MODULE_STD", // Static analysis "CXX_CLANG_TIDY", |