From 2c8361f923317a784adfa6502e1a0c3a17c1dff9 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 27 Feb 2024 11:53:32 -0500 Subject: cxxmodules: link to `std`-providing targets when available --- Help/manual/cmake-cxxmodules.7.rst | 9 +++- Source/cmGeneratorTarget.cxx | 103 +++++++++++++++++++++++++++++++++++++ Source/cmGeneratorTarget.h | 1 + Source/cmGlobalGenerator.cxx | 23 +++++++++ Source/cmGlobalGenerator.h | 1 + 5 files changed, 136 insertions(+), 1 deletion(-) diff --git a/Help/manual/cmake-cxxmodules.7.rst b/Help/manual/cmake-cxxmodules.7.rst index 8f62b5a..0c9c46e 100644 --- a/Help/manual/cmake-cxxmodules.7.rst +++ b/Help/manual/cmake-cxxmodules.7.rst @@ -86,6 +86,12 @@ Compilers which CMake natively supports module dependency scanning include: * LLVM/Clang 16.0 and newer * GCC 14 (for the in-development branch, after 2023-09-20) and newer +``import std`` Support +====================== + +Support for ``import std`` is limited to the following toolchain and standard +library combinations: + Generator Support ================= @@ -116,6 +122,7 @@ For the :ref:`Visual Studio Generators`: - Only Visual Studio 2022 and MSVC toolsets 14.34 (Visual Studio 17.4) and newer. - No support for exporting or installing BMI or module information. -- No support for compiling BMIs from ``IMPORTED`` targets with C++ modules. +- No support for compiling BMIs from ``IMPORTED`` targets with C++ modules + (including ``import std``). - No diagnosis of using modules provided by ``PRIVATE`` sources from ``PUBLIC`` module sources. diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index f0acb9c..1dba976 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -8411,6 +8411,106 @@ void ComputeLinkImplTransitive(cmGeneratorTarget const* self, } } +bool cmGeneratorTarget::ApplyCXXStdTargets() +{ + cmStandardLevelResolver standardResolver(this->Makefile); + cmStandardLevel const cxxStd23 = + *standardResolver.LanguageStandardLevel("CXX", "23"); + std::vector 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 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; + } + + this->Target->AppendProperty( + "LINK_LIBRARIES", + cmStrCat("$:", targetName, + ">>")); + } + + return true; +} + bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, std::string const& config) { @@ -8500,6 +8600,9 @@ bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache, 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); 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 c0af34b..e397fa2 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -1594,6 +1594,16 @@ bool cmGlobalGenerator::Compute() } } + // 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? @@ -1830,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(); -- cgit v0.12