浏览代码

cxxmodules: link to `std`-providing targets when available

Ben Boeckel 1 年之前
父节点
当前提交
2c8361f923

+ 8 - 1
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.

+ 103 - 0
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<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;
+    }
+
+    this->Target->AppendProperty(
+      "LINK_LIBRARIES",
+      cmStrCat("$<BUILD_LOCAL_INTERFACE:$<$<CONFIG:", config, ">:", 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);
 

+ 1 - 0
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);
 

+ 23 - 0
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;

+ 1 - 0
Source/cmGlobalGenerator.h

@@ -674,6 +674,7 @@ protected:
 
   virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const;
 
+  bool ApplyCXXStdTargets();
   bool DiscoverSyntheticTargets();
 
   bool AddHeaderSetVerification();