diff options
author | Brad King <brad.king@kitware.com> | 2023-10-06 12:35:53 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2023-10-06 12:36:02 (GMT) |
commit | 24c5a3aecfee14ece66ecfe6198ddac1f465bbcd (patch) | |
tree | e4658121d42f5b6079059b590a5f72449f0d08d7 | |
parent | a48363782131c3ab45c4b40773ff25002e6c3adb (diff) | |
parent | b8ead378def8dc03dcae6b65f956c26c585eba3e (diff) | |
download | CMake-24c5a3aecfee14ece66ecfe6198ddac1f465bbcd.zip CMake-24c5a3aecfee14ece66ecfe6198ddac1f465bbcd.tar.gz CMake-24c5a3aecfee14ece66ecfe6198ddac1f465bbcd.tar.bz2 |
Merge topic 'cxxmodules-explicit-c++20' into release-3.28
b8ead378de cxxmodules: Scan only targets that explicitly enable C++ 20
68fca3eafe cmGeneratorTarget: Track explicitly enabled language standard levels
da36e0638b cmGeneratorTarget: Remove outdated const/mutable pair
c1f1aedcee cmStandardLevelResolver: Add method to look up standard level by name
23b57462aa cmStandardLevelResolver: Report feature std level from GetNewRequiredStandard
7519001ae6 cmStandardLevelResolver: Add method to get feature standard level
99fa01d3fa cmStandardLevelResolver: Factor out public representation of level
fdd81a609a cmStandardLevelResolver: Clarify local variable name
...
Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: buildbot <buildbot@kitware.com>
Reviewed-by: Ben Boeckel <ben.boeckel@kitware.com>
Merge-request: !8860
-rw-r--r-- | Source/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.cxx | 60 | ||||
-rw-r--r-- | Source/cmGeneratorTarget.h | 17 | ||||
-rw-r--r-- | Source/cmStandardLevel.h | 21 | ||||
-rw-r--r-- | Source/cmStandardLevelResolver.cxx | 143 | ||||
-rw-r--r-- | Source/cmStandardLevelResolver.h | 10 | ||||
-rw-r--r-- | Tests/RunCMake/CXXModules/ImplicitCXX20.cmake | 13 | ||||
-rw-r--r-- | Tests/RunCMake/CXXModules/Inspect.cmake (renamed from Tests/RunCMake/CXXModules/compiler_introspection.cmake) | 2 | ||||
-rw-r--r-- | Tests/RunCMake/CXXModules/RunCMakeTest.cmake | 7 |
9 files changed, 199 insertions, 75 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 37d407b..1bc855e 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -417,6 +417,7 @@ add_library( cmSourceFileLocationKind.h cmSourceGroup.cxx cmSourceGroup.h + cmStandardLevel.h cmStandardLevelResolver.cxx cmStandardLevelResolver.h cmState.cxx diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 422d927..01af14f 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -50,6 +50,7 @@ #include "cmSourceFileLocation.h" #include "cmSourceFileLocationKind.h" #include "cmSourceGroup.h" +#include "cmStandardLevel.h" #include "cmStandardLevelResolver.h" #include "cmState.h" #include "cmStringAlgorithms.h" @@ -5024,10 +5025,44 @@ void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const } } -bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config) const +cm::optional<cmStandardLevel> cmGeneratorTarget::GetExplicitStandardLevel( + std::string const& lang, std::string const& config) const +{ + cm::optional<cmStandardLevel> level; + std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang); + auto i = this->ExplicitStandardLevel.find(key); + if (i != this->ExplicitStandardLevel.end()) { + level = i->second; + } + return level; +} + +void cmGeneratorTarget::UpdateExplicitStandardLevel(std::string const& lang, + std::string const& config, + cmStandardLevel level) +{ + auto e = this->ExplicitStandardLevel.emplace( + cmStrCat(cmSystemTools::UpperCase(config), '-', lang), level); + if (!e.second && e.first->second < level) { + e.first->second = level; + } +} + +bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config) { - // Compute the language standard based on the compile features. cmStandardLevelResolver standardResolver(this->Makefile); + + for (std::string const& lang : + this->Makefile->GetState()->GetEnabledLanguages()) { + if (cmValue languageStd = this->GetLanguageStandard(lang, config)) { + if (cm::optional<cmStandardLevel> langLevel = + standardResolver.LanguageStandardLevel(lang, *languageStd)) { + this->UpdateExplicitStandardLevel(lang, config, *langLevel); + } + } + } + + // Compute the language standard based on the compile features. std::vector<BT<std::string>> features = this->GetCompileFeatures(config); for (BT<std::string> const& f : features) { std::string lang; @@ -5039,13 +5074,18 @@ bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config) const std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang); cmValue currentLanguageStandard = this->GetLanguageStandard(lang, config); + cm::optional<cmStandardLevel> featureLevel; std::string newRequiredStandard; if (!standardResolver.GetNewRequiredStandard( this->Target->GetName(), f.Value, currentLanguageStandard, - newRequiredStandard)) { + featureLevel, newRequiredStandard)) { return false; } + if (featureLevel) { + this->UpdateExplicitStandardLevel(lang, config, *featureLevel); + } + if (!newRequiredStandard.empty()) { BTs<std::string>& languageStandardProperty = this->LanguageStandardMap[key]; @@ -5061,7 +5101,7 @@ bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config) const } bool cmGeneratorTarget::ComputeCompileFeatures( - std::string const& config, std::set<LanguagePair> const& languagePairs) const + std::string const& config, std::set<LanguagePair> const& languagePairs) { for (const auto& language : languagePairs) { BTs<std::string> const* generatorTargetLanguageStandard = @@ -9102,13 +9142,11 @@ cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport( } cmStandardLevelResolver standardResolver(this->Makefile); - if (!standardResolver.HaveStandardAvailable(this, "CXX", config, - "cxx_std_20") || - // During the ABI detection step we do not know the compiler's features. - // HaveStandardAvailable may return true as a fallback, but in this code - // path we do not want to assume C++ 20 is available. - this->Makefile->GetDefinition("CMAKE_CXX20_COMPILE_FEATURES") - .IsEmpty()) { + cmStandardLevel const cxxStd20 = + *standardResolver.LanguageStandardLevel("CXX", "20"); + cm::optional<cmStandardLevel> explicitLevel = + this->GetExplicitStandardLevel("CXX", config); + if (!explicitLevel || *explicitLevel < cxxStd20) { return Cxx20SupportLevel::NoCxx20; } diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 72920d6..bf49914 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -21,6 +21,7 @@ #include "cmLinkItem.h" #include "cmListFileCache.h" #include "cmPolicies.h" +#include "cmStandardLevel.h" #include "cmStateTypes.h" #include "cmValue.h" @@ -613,12 +614,11 @@ public: /** Add the target output files to the global generator manifest. */ void ComputeTargetManifest(const std::string& config) const; - bool ComputeCompileFeatures(std::string const& config) const; + bool ComputeCompileFeatures(std::string const& config); using LanguagePair = std::pair<std::string, std::string>; - bool ComputeCompileFeatures( - std::string const& config, - std::set<LanguagePair> const& languagePairs) const; + bool ComputeCompileFeatures(std::string const& config, + std::set<LanguagePair> const& languagePairs); /** * Trace through the source files in this target and add al source files @@ -1241,7 +1241,14 @@ private: bool GetRPATH(const std::string& config, const std::string& prop, std::string& rpath) const; - mutable std::map<std::string, BTs<std::string>> LanguageStandardMap; + std::map<std::string, BTs<std::string>> LanguageStandardMap; + + cm::optional<cmStandardLevel> GetExplicitStandardLevel( + std::string const& lang, std::string const& config) const; + void UpdateExplicitStandardLevel(std::string const& lang, + std::string const& config, + cmStandardLevel level); + std::map<std::string, cmStandardLevel> ExplicitStandardLevel; cmValue GetPropertyWithPairedLanguageSupport(std::string const& lang, const char* suffix) const; diff --git a/Source/cmStandardLevel.h b/Source/cmStandardLevel.h new file mode 100644 index 0000000..86d178b --- /dev/null +++ b/Source/cmStandardLevel.h @@ -0,0 +1,21 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +class cmStandardLevel +{ + size_t index_; + +public: + cmStandardLevel(size_t index) + : index_(index) + { + } + size_t Index() const { return index_; } + friend bool operator<(cmStandardLevel const& l, cmStandardLevel const& r) + { + return l.index_ < r.index_; + } +}; diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx index de42bc6..e814690 100644 --- a/Source/cmStandardLevelResolver.cxx +++ b/Source/cmStandardLevelResolver.cxx @@ -13,6 +13,7 @@ #include <vector> #include <cm/iterator> +#include <cm/optional> #include <cm/string_view> #include <cmext/algorithm> #include <cmext/string_view> @@ -25,6 +26,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmStandardLevel.h" #include "cmStringAlgorithms.h" #include "cmTarget.h" #include "cmValue.h" @@ -46,12 +48,6 @@ const char* const HIP_FEATURES[] = { nullptr FOR_EACH_HIP_FEATURE( FEATURE_STRING) }; #undef FEATURE_STRING -struct StandardNeeded -{ - int index; - int value; -}; - int ParseStd(std::string const& level) { try { @@ -341,7 +337,7 @@ struct StandardLevelComputer bool GetNewRequiredStandard(cmMakefile* makefile, std::string const& targetName, - const std::string& feature, + cm::optional<cmStandardLevel> featureLevel, cmValue currentLangStandardValue, std::string& newRequiredStandard, std::string* error) const @@ -352,8 +348,6 @@ struct StandardLevelComputer newRequiredStandard.clear(); } - auto needed = this->HighestStandardNeeded(makefile, feature); - cmValue existingStandard = currentLangStandardValue; if (!existingStandard) { cmValue defaultStandard = makefile->GetDefinition( @@ -382,12 +376,12 @@ struct StandardLevelComputer } } - if (needed.index != -1) { + if (featureLevel) { // Ensure the C++ language level is high enough to support // the needed C++ features. if (existingLevelIter == cm::cend(this->Levels) || - existingLevelIter < this->Levels.begin() + needed.index) { - newRequiredStandard = this->LevelsAsStrings[needed.index]; + existingLevelIter < this->Levels.begin() + featureLevel->Index()) { + newRequiredStandard = this->LevelsAsStrings[featureLevel->Index()]; } } @@ -439,29 +433,43 @@ struct StandardLevelComputer return false; } - auto needed = this->HighestStandardNeeded(makefile, feature); + cm::optional<cmStandardLevel> needed = + this->CompileFeatureStandardLevel(makefile, feature); - return (needed.index == -1) || - (this->Levels.begin() + needed.index) <= existingLevelIter; + return !needed || + (this->Levels.begin() + needed->Index()) <= existingLevelIter; } - StandardNeeded HighestStandardNeeded(cmMakefile* makefile, - std::string const& feature) const + cm::optional<cmStandardLevel> CompileFeatureStandardLevel( + cmMakefile* makefile, std::string const& feature) const { std::string prefix = cmStrCat("CMAKE_", this->Language); - StandardNeeded maxLevel = { -1, -1 }; + cm::optional<cmStandardLevel> maxLevel; for (size_t i = 0; i < this->Levels.size(); ++i) { if (cmValue prop = makefile->GetDefinition( cmStrCat(prefix, this->LevelsAsStrings[i], "_COMPILE_FEATURES"))) { cmList props{ *prop }; if (cm::contains(props, feature)) { - maxLevel = { static_cast<int>(i), this->Levels[i] }; + maxLevel = cmStandardLevel(i); } } } return maxLevel; } + cm::optional<cmStandardLevel> LanguageStandardLevel( + std::string const& standardStr) const + { + cm::optional<cmStandardLevel> langLevel; + auto const& stds = this->Levels; + auto stdIt = + std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr)); + if (stdIt != cm::cend(stds)) { + langLevel = cmStandardLevel(std::distance(cm::cbegin(stds), stdIt)); + } + return langLevel; + } + bool IsLaterStandard(int lhs, int rhs) const { auto rhsIt = @@ -476,37 +484,33 @@ struct StandardLevelComputer std::vector<std::string> LevelsAsStrings; }; -std::unordered_map<std::string, StandardLevelComputer> - StandardComputerMapping = { - { "C", - StandardLevelComputer{ - "C", std::vector<int>{ 90, 99, 11, 17, 23 }, - std::vector<std::string>{ "90", "99", "11", "17", "23" } } }, - { "CXX", - StandardLevelComputer{ "CXX", - std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 }, - std::vector<std::string>{ "98", "11", "14", "17", - "20", "23", "26" } } }, - { "CUDA", - StandardLevelComputer{ "CUDA", - std::vector<int>{ 03, 11, 14, 17, 20, 23, 26 }, - std::vector<std::string>{ "03", "11", "14", "17", - "20", "23", "26" } } }, - { "OBJC", - StandardLevelComputer{ - "OBJC", std::vector<int>{ 90, 99, 11, 17, 23 }, - std::vector<std::string>{ "90", "99", "11", "17", "23" } } }, - { "OBJCXX", - StandardLevelComputer{ "OBJCXX", - std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 }, - std::vector<std::string>{ "98", "11", "14", "17", - "20", "23", "26" } } }, - { "HIP", - StandardLevelComputer{ "HIP", - std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 }, - std::vector<std::string>{ "98", "11", "14", "17", - "20", "23", "26" } } } - }; +std::unordered_map<std::string, + StandardLevelComputer> const StandardComputerMapping = { + { "C", + StandardLevelComputer{ + "C", std::vector<int>{ 90, 99, 11, 17, 23 }, + std::vector<std::string>{ "90", "99", "11", "17", "23" } } }, + { "CXX", + StandardLevelComputer{ + "CXX", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 }, + std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } }, + { "CUDA", + StandardLevelComputer{ + "CUDA", std::vector<int>{ 03, 11, 14, 17, 20, 23, 26 }, + std::vector<std::string>{ "03", "11", "14", "17", "20", "23", "26" } } }, + { "OBJC", + StandardLevelComputer{ + "OBJC", std::vector<int>{ 90, 99, 11, 17, 23 }, + std::vector<std::string>{ "90", "99", "11", "17", "23" } } }, + { "OBJCXX", + StandardLevelComputer{ + "OBJCXX", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 }, + std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } }, + { "HIP", + StandardLevelComputer{ + "HIP", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 }, + std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } } +}; } std::string cmStandardLevelResolver::GetCompileOptionDef( @@ -556,15 +560,16 @@ bool cmStandardLevelResolver::AddRequiredTargetFeature( // should be done purely at generate time based on whatever the project // code put in these properties explicitly. That is mostly true now, // but for compatibility we need to continue updating the property here. + cm::optional<cmStandardLevel> featureLevel; std::string newRequiredStandard; - bool newRequired = this->GetNewRequiredStandard( + bool succeeded = this->GetNewRequiredStandard( target->GetName(), feature, - target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard, - error); + target->GetProperty(cmStrCat(lang, "_STANDARD")), featureLevel, + newRequiredStandard, error); if (!newRequiredStandard.empty()) { target->SetProperty(cmStrCat(lang, "_STANDARD"), newRequiredStandard); } - return newRequired; + return succeeded; } bool cmStandardLevelResolver::CheckCompileFeaturesAvailable( @@ -658,6 +663,27 @@ bool cmStandardLevelResolver::CompileFeatureKnown( return false; } +cm::optional<cmStandardLevel> +cmStandardLevelResolver::CompileFeatureStandardLevel( + std::string const& lang, std::string const& feature) const +{ + auto mapping = StandardComputerMapping.find(lang); + if (mapping == cm::cend(StandardComputerMapping)) { + return cm::nullopt; + } + return mapping->second.CompileFeatureStandardLevel(this->Makefile, feature); +} + +cm::optional<cmStandardLevel> cmStandardLevelResolver::LanguageStandardLevel( + std::string const& lang, std::string const& standardStr) const +{ + auto mapping = StandardComputerMapping.find(lang); + if (mapping == cm::cend(StandardComputerMapping)) { + return cm::nullopt; + } + return mapping->second.LanguageStandardLevel(standardStr); +} + cmValue cmStandardLevelResolver::CompileFeaturesAvailable( const std::string& lang, std::string* error) const { @@ -706,18 +732,21 @@ cmValue cmStandardLevelResolver::CompileFeaturesAvailable( bool cmStandardLevelResolver::GetNewRequiredStandard( const std::string& targetName, const std::string& feature, - cmValue currentLangStandardValue, std::string& newRequiredStandard, - std::string* error) const + cmValue currentLangStandardValue, + cm::optional<cmStandardLevel>& featureLevel, + std::string& newRequiredStandard, std::string* error) const { std::string lang; if (!this->CheckCompileFeaturesAvailable(targetName, feature, lang, error)) { return false; } + featureLevel = this->CompileFeatureStandardLevel(lang, feature); + auto mapping = StandardComputerMapping.find(lang); if (mapping != cm::cend(StandardComputerMapping)) { return mapping->second.GetNewRequiredStandard( - this->Makefile, targetName, feature, currentLangStandardValue, + this->Makefile, targetName, featureLevel, currentLangStandardValue, newRequiredStandard, error); } return false; diff --git a/Source/cmStandardLevelResolver.h b/Source/cmStandardLevelResolver.h index 03adf3f..29cab55 100644 --- a/Source/cmStandardLevelResolver.h +++ b/Source/cmStandardLevelResolver.h @@ -4,10 +4,13 @@ #include <string> +#include <cm/optional> + #include "cmValue.h" class cmMakefile; class cmGeneratorTarget; +class cmStandardLevel; class cmTarget; class cmStandardLevelResolver @@ -33,12 +36,19 @@ public: const std::string& feature, std::string& lang, std::string* error) const; + cm::optional<cmStandardLevel> CompileFeatureStandardLevel( + std::string const& lang, std::string const& feature) const; + + cm::optional<cmStandardLevel> LanguageStandardLevel( + std::string const& lang, std::string const& standardStr) const; + cmValue CompileFeaturesAvailable(const std::string& lang, std::string* error) const; bool GetNewRequiredStandard(const std::string& targetName, const std::string& feature, cmValue currentLangStandardValue, + cm::optional<cmStandardLevel>& featureLevel, std::string& newRequiredStandard, std::string* error = nullptr) const; diff --git a/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake b/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake new file mode 100644 index 0000000..cac1777 --- /dev/null +++ b/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake @@ -0,0 +1,13 @@ +# Enable scanning by default for targets that explicitly use C++ 20. +cmake_policy(SET CMP0155 NEW) + +# Force CMAKE_CXX_STANDARD_DEFAULT to be C++ 20. +set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} ${CMAKE_CXX20_STANDARD_COMPILE_OPTION}") +enable_language(CXX) + +# Hide any real scanning rule that may be available. +unset(CMAKE_CXX_SCANDEP_SOURCE) + +# Create a target that does not explicitly use C++ 20 to verify it works +# without any scanning rule available. +add_executable(cmp0155-new sources/module-use.cxx) diff --git a/Tests/RunCMake/CXXModules/compiler_introspection.cmake b/Tests/RunCMake/CXXModules/Inspect.cmake index 8c74940..612b01b 100644 --- a/Tests/RunCMake/CXXModules/compiler_introspection.cmake +++ b/Tests/RunCMake/CXXModules/Inspect.cmake @@ -23,6 +23,8 @@ set(forced_cxx_standard \"${forced_cxx_standard}\") set(CMAKE_CXX_COMPILER_VERSION \"${CMAKE_CXX_COMPILER_VERSION}\") set(CMAKE_CXX_OUTPUT_EXTENSION \"${CMAKE_CXX_OUTPUT_EXTENSION}\") set(CXXModules_default_build_type \"${CMAKE_BUILD_TYPE}\") +set(CMAKE_CXX_STANDARD_DEFAULT \"${CMAKE_CXX_STANDARD_DEFAULT}\") +set(CMAKE_CXX20_STANDARD_COMPILE_OPTION \"${CMAKE_CXX20_STANDARD_COMPILE_OPTION}\") ") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}") diff --git a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake index 8b22687..05d2655 100644 --- a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake +++ b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake @@ -3,8 +3,8 @@ include(RunCMake) # For `if (IN_LIST)` cmake_policy(SET CMP0057 NEW) -run_cmake(compiler_introspection) -include("${RunCMake_BINARY_DIR}/compiler_introspection-build/info.cmake") +run_cmake(Inspect) +include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake") # Test negative cases where C++20 modules do not work. run_cmake(NoCXX) @@ -13,6 +13,9 @@ if ("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES) # standard. If the compiler forces a standard to be used, skip it. if (NOT forced_cxx_standard) run_cmake(NoCXX20) + if(CMAKE_CXX_STANDARD_DEFAULT AND CMAKE_CXX20_STANDARD_COMPILE_OPTION) + run_cmake_with_options(ImplicitCXX20 -DCMAKE_CXX20_STANDARD_COMPILE_OPTION=${CMAKE_CXX20_STANDARD_COMPILE_OPTION}) + endif() endif () run_cmake(NoScanningSourceFileProperty) |