1
0
Эх сурвалжийг харах

Merge topic 'export-refactor-for-cps'

ff24058e46 export: Use std::all_of to collect exports
20fa4ce8d8 export: Factor out CMake-specific export generation (2/2)
6c66340a64 export: Fix const placement
1bceab3520 export: Factor out CMake-specific export generation (*/2)
a6cc595772 export: Factor out CMake-specific export generation (1/2)
0352376e44 export: Immediately report actual version required

Acked-by: Kitware Robot <[email protected]>
Acked-by: buildbot <[email protected]>
Merge-request: !9646
Brad King 1 жил өмнө
parent
commit
262a89c92b
30 өөрчлөгдсөн 3141 нэмэгдсэн , 2404 устгасан
  1. 12 0
      Source/CMakeLists.txt
  2. 165 0
      Source/cmExportAndroidMKGenerator.cxx
  3. 73 0
      Source/cmExportAndroidMKGenerator.h
  4. 34 161
      Source/cmExportBuildAndroidMKGenerator.cxx
  5. 13 28
      Source/cmExportBuildAndroidMKGenerator.h
  6. 343 0
      Source/cmExportBuildCMakeConfigGenerator.cxx
  7. 52 0
      Source/cmExportBuildCMakeConfigGenerator.h
  8. 60 388
      Source/cmExportBuildFileGenerator.cxx
  9. 30 31
      Source/cmExportBuildFileGenerator.h
  10. 686 0
      Source/cmExportCMakeConfigGenerator.cxx
  11. 109 0
      Source/cmExportCMakeConfigGenerator.h
  12. 9 5
      Source/cmExportCommand.cxx
  13. 93 873
      Source/cmExportFileGenerator.cxx
  14. 76 128
      Source/cmExportFileGenerator.h
  15. 51 65
      Source/cmExportInstallAndroidMKGenerator.cxx
  16. 30 32
      Source/cmExportInstallAndroidMKGenerator.h
  17. 507 0
      Source/cmExportInstallCMakeConfigGenerator.cxx
  18. 74 0
      Source/cmExportInstallCMakeConfigGenerator.h
  19. 380 530
      Source/cmExportInstallFileGenerator.cxx
  20. 74 44
      Source/cmExportInstallFileGenerator.h
  21. 14 7
      Source/cmExportTryCompileFileGenerator.cxx
  22. 15 11
      Source/cmExportTryCompileFileGenerator.h
  23. 30 0
      Source/cmInstallAndroidMKExportGenerator.cxx
  24. 36 0
      Source/cmInstallAndroidMKExportGenerator.h
  25. 43 0
      Source/cmInstallCMakeConfigExportGenerator.cxx
  26. 42 0
      Source/cmInstallCMakeConfigExportGenerator.h
  27. 7 6
      Source/cmInstallCommand.cxx
  28. 69 82
      Source/cmInstallExportGenerator.cxx
  29. 10 13
      Source/cmInstallExportGenerator.h
  30. 4 0
      bootstrap

+ 12 - 0
Source/CMakeLists.txt

@@ -199,14 +199,22 @@ add_library(
   cmEvaluatedTargetProperty.cxx
   cmEvaluatedTargetProperty.h
   cmExprParserHelper.cxx
+  cmExportAndroidMKGenerator.h
+  cmExportAndroidMKGenerator.cxx
   cmExportBuildAndroidMKGenerator.h
   cmExportBuildAndroidMKGenerator.cxx
+  cmExportBuildCMakeConfigGenerator.h
+  cmExportBuildCMakeConfigGenerator.cxx
   cmExportBuildFileGenerator.h
   cmExportBuildFileGenerator.cxx
+  cmExportCMakeConfigGenerator.h
+  cmExportCMakeConfigGenerator.cxx
   cmExportFileGenerator.h
   cmExportFileGenerator.cxx
   cmExportInstallAndroidMKGenerator.h
   cmExportInstallAndroidMKGenerator.cxx
+  cmExportInstallCMakeConfigGenerator.h
+  cmExportInstallCMakeConfigGenerator.cxx
   cmExportInstallFileGenerator.h
   cmExportInstallFileGenerator.cxx
   cmExportTryCompileFileGenerator.h
@@ -311,6 +319,10 @@ add_library(
   cmGraphVizWriter.h
   cmImportedCxxModuleInfo.cxx
   cmImportedCxxModuleInfo.h
+  cmInstallAndroidMKExportGenerator.cxx
+  cmInstallAndroidMKExportGenerator.h
+  cmInstallCMakeConfigExportGenerator.cxx
+  cmInstallCMakeConfigExportGenerator.h
   cmInstallGenerator.h
   cmInstallGenerator.cxx
   cmInstallGetRuntimeDependenciesGenerator.h

+ 165 - 0
Source/cmExportAndroidMKGenerator.cxx

@@ -0,0 +1,165 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmExportAndroidMKGenerator.h"
+
+#include <sstream>
+#include <utility>
+#include <vector>
+
+#include <cmext/algorithm>
+#include <cmext/string_view>
+
+#include "cmGeneratorTarget.h"
+#include "cmLinkItem.h"
+#include "cmList.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmPolicies.h"
+#include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmTarget.h"
+
+cmExportAndroidMKGenerator::cmExportAndroidMKGenerator() = default;
+
+cm::string_view cmExportAndroidMKGenerator::GetImportPrefixWithSlash() const
+{
+  return "$(_IMPORT_PREFIX)/"_s;
+}
+
+bool cmExportAndroidMKGenerator::GenerateImportFile(std::ostream& os)
+{
+  if (!this->AppendMode) {
+    // Start with the import file header.
+    this->GenerateImportHeaderCode(os);
+  }
+
+  // Create all the imported targets.
+  std::stringstream mainFileBuffer;
+  bool result = this->GenerateMainFile(mainFileBuffer);
+
+  // Write cached import code.
+  os << mainFileBuffer.rdbuf();
+
+  return result;
+}
+
+void cmExportAndroidMKGenerator::GenerateInterfaceProperties(
+  cmGeneratorTarget const* target, std::ostream& os,
+  ImportPropertyMap const& properties)
+{
+  std::string const config =
+    (this->Configurations.empty() ? std::string{} : this->Configurations[0]);
+  GenerateType const type = this->GetGenerateType();
+
+  bool const newCMP0022Behavior =
+    target->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
+    target->GetPolicyStatusCMP0022() != cmPolicies::OLD;
+  if (!newCMP0022Behavior) {
+    std::ostringstream w;
+    if (type == BUILD) {
+      w << "export(TARGETS ... ANDROID_MK) called with policy CMP0022";
+    } else {
+      w << "install( EXPORT_ANDROID_MK ...) called with policy CMP0022";
+    }
+    w << " set to OLD for target " << target->Target->GetName() << ". "
+      << "The export will only work with CMP0022 set to NEW.";
+    target->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
+  }
+  if (!properties.empty()) {
+    os << "LOCAL_CPP_FEATURES := rtti exceptions\n";
+    for (auto const& property : properties) {
+      if (property.first == "INTERFACE_COMPILE_OPTIONS") {
+        os << "LOCAL_CPP_FEATURES += ";
+        os << (property.second) << "\n";
+      } else if (property.first == "INTERFACE_LINK_LIBRARIES") {
+        std::string staticLibs;
+        std::string sharedLibs;
+        std::string ldlibs;
+        cmLinkInterfaceLibraries const* linkIFace =
+          target->GetLinkInterfaceLibraries(config, target,
+                                            cmGeneratorTarget::UseTo::Link);
+        for (cmLinkItem const& item : linkIFace->Libraries) {
+          cmGeneratorTarget const* gt = item.Target;
+          std::string const& lib = item.AsStr();
+          if (gt) {
+
+            if (gt->GetType() == cmStateEnums::SHARED_LIBRARY ||
+                gt->GetType() == cmStateEnums::MODULE_LIBRARY) {
+              sharedLibs += " " + lib;
+            } else {
+              staticLibs += " " + lib;
+            }
+          } else {
+            bool relpath = false;
+            if (type == INSTALL) {
+              relpath = cmHasLiteralPrefix(lib, "../");
+            }
+            // check for full path or if it already has a -l, or
+            // in the case of an install check for relative paths
+            // if it is full or a link library then use string directly
+            if (cmSystemTools::FileIsFullPath(lib) ||
+                cmHasLiteralPrefix(lib, "-l") || relpath) {
+              ldlibs += " " + lib;
+              // if it is not a path and does not have a -l then add -l
+            } else if (!lib.empty()) {
+              ldlibs += " -l" + lib;
+            }
+          }
+        }
+        if (!sharedLibs.empty()) {
+          os << "LOCAL_SHARED_LIBRARIES :=" << sharedLibs << "\n";
+        }
+        if (!staticLibs.empty()) {
+          os << "LOCAL_STATIC_LIBRARIES :=" << staticLibs << "\n";
+        }
+        if (!ldlibs.empty()) {
+          os << "LOCAL_EXPORT_LDLIBS :=" << ldlibs << "\n";
+        }
+      } else if (property.first == "INTERFACE_INCLUDE_DIRECTORIES") {
+        std::string includes = property.second;
+        cmList includeList{ includes };
+        os << "LOCAL_EXPORT_C_INCLUDES := ";
+        std::string end;
+        for (std::string const& i : includeList) {
+          os << end << i;
+          end = "\\\n";
+        }
+        os << "\n";
+      } else if (property.first == "INTERFACE_LINK_OPTIONS") {
+        os << "LOCAL_EXPORT_LDFLAGS := ";
+        cmList linkFlagsList{ property.second };
+        os << linkFlagsList.join(" ") << "\n";
+      } else {
+        os << "# " << property.first << " " << (property.second) << "\n";
+      }
+    }
+  }
+
+  // Tell the NDK build system if prebuilt static libraries use C++.
+  if (target->GetType() == cmStateEnums::STATIC_LIBRARY) {
+    cmLinkImplementation const* li =
+      target->GetLinkImplementation(config, cmGeneratorTarget::UseTo::Link);
+    if (cm::contains(li->Languages, "CXX")) {
+      os << "LOCAL_HAS_CPP := true\n";
+    }
+  }
+
+  switch (target->GetType()) {
+    case cmStateEnums::SHARED_LIBRARY:
+    case cmStateEnums::MODULE_LIBRARY:
+      os << "include $(PREBUILT_SHARED_LIBRARY)\n";
+      break;
+    case cmStateEnums::STATIC_LIBRARY:
+      os << "include $(PREBUILT_STATIC_LIBRARY)\n";
+      break;
+    case cmStateEnums::EXECUTABLE:
+    case cmStateEnums::UTILITY:
+    case cmStateEnums::OBJECT_LIBRARY:
+    case cmStateEnums::GLOBAL_TARGET:
+    case cmStateEnums::INTERFACE_LIBRARY:
+    case cmStateEnums::UNKNOWN_LIBRARY:
+      break;
+  }
+  os << "\n";
+}

+ 73 - 0
Source/cmExportAndroidMKGenerator.h

@@ -0,0 +1,73 @@
+/* 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
+
+#include <iosfwd>
+#include <map>
+#include <string>
+
+#include <cm/string_view>
+
+#include "cmExportFileGenerator.h"
+#include "cmStateTypes.h"
+
+class cmGeneratorTarget;
+
+/** \class cmExportAndroidMKGenerator
+ * \brief Generate CMake configuration files exporting targets from a build or
+ * install tree.
+ *
+ * cmExportAndroidMKGenerator is the superclass for
+ * cmExportBuildAndroidMKGenerator and cmExportInstallAndroidMKGenerator.
+ * It contains common code generation routines for the two kinds of export
+ * implementations.
+ */
+class cmExportAndroidMKGenerator : virtual public cmExportFileGenerator
+{
+public:
+  cmExportAndroidMKGenerator();
+
+  using cmExportFileGenerator::GenerateImportFile;
+
+protected:
+  enum GenerateType
+  {
+    BUILD,
+    INSTALL
+  };
+  virtual GenerateType GetGenerateType() const = 0;
+
+  using ImportPropertyMap = std::map<std::string, std::string>;
+
+  cm::string_view GetImportPrefixWithSlash() const override;
+
+  void GenerateInterfaceProperties(cmGeneratorTarget const* target,
+                                   std::ostream& os,
+                                   ImportPropertyMap const& properties);
+
+  // Methods to implement export file code generation.
+  bool GenerateImportFile(std::ostream& os) override;
+  virtual void GenerateImportHeaderCode(std::ostream& os,
+                                        std::string const& config = "") = 0;
+  virtual void GenerateImportTargetCode(
+    std::ostream& os, cmGeneratorTarget const* target,
+    cmStateEnums::TargetType targetType) = 0;
+
+  void GenerateImportTargetsConfig(std::ostream& /*os*/,
+                                   std::string const& /*config*/,
+                                   std::string const& /*suffix*/) override
+  {
+  }
+
+  std::string GetCxxModuleFile(std::string const& /*name*/) const override
+  {
+    return {};
+  }
+
+  void GenerateCxxModuleConfigInformation(std::string const& /*name*/,
+                                          std::ostream& /*os*/) const override
+  {
+  }
+};

+ 34 - 161
Source/cmExportBuildAndroidMKGenerator.cxx

@@ -2,43 +2,56 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportBuildAndroidMKGenerator.h"
 
-#include <map>
 #include <sstream>
-#include <utility>
 #include <vector>
 
-#include <cmext/algorithm>
-
+#include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
-#include "cmLinkItem.h"
-#include "cmList.h"
-#include "cmMakefile.h"
-#include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 
-cmExportBuildAndroidMKGenerator::cmExportBuildAndroidMKGenerator()
-{
-  this->LG = nullptr;
-  this->ExportSet = nullptr;
-}
+cmExportBuildAndroidMKGenerator::cmExportBuildAndroidMKGenerator() = default;
 
-void cmExportBuildAndroidMKGenerator::GenerateImportHeaderCode(
-  std::ostream& os, const std::string&)
+bool cmExportBuildAndroidMKGenerator::GenerateMainFile(std::ostream& os)
 {
-  os << "LOCAL_PATH := $(call my-dir)\n\n";
-}
+  if (!this->CollectExports([&](cmGeneratorTarget const*) {})) {
+    return false;
+  }
 
-void cmExportBuildAndroidMKGenerator::GenerateImportFooterCode(std::ostream&)
-{
+  // Create all the imported targets.
+  for (auto const& exp : this->Exports) {
+    cmGeneratorTarget* gte = exp.Target;
+
+    this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
+
+    gte->Target->AppendBuildInterfaceIncludes();
+
+    ImportPropertyMap properties;
+    if (!this->PopulateInterfaceProperties(gte, properties)) {
+      return false;
+    }
+
+    bool const newCMP0022Behavior =
+      gte->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
+      gte->GetPolicyStatusCMP0022() != cmPolicies::OLD;
+    if (newCMP0022Behavior) {
+      this->PopulateInterfaceLinkLibrariesProperty(
+        gte, cmGeneratorExpression::BuildInterface, properties);
+    }
+
+    this->GenerateInterfaceProperties(gte, os, properties);
+  }
+
+  return true;
 }
 
-void cmExportBuildAndroidMKGenerator::GenerateExpectedTargetsCode(
-  std::ostream&, const std::string&)
+void cmExportBuildAndroidMKGenerator::GenerateImportHeaderCode(
+  std::ostream& os, std::string const&)
 {
+  os << "LOCAL_PATH := $(call my-dir)\n\n";
 }
 
 void cmExportBuildAndroidMKGenerator::GenerateImportTargetCode(
@@ -55,143 +68,3 @@ void cmExportBuildAndroidMKGenerator::GenerateImportTargetCode(
     cmSystemTools::ConvertToOutputPath(target->GetFullPath(noConfig));
   os << path << "\n";
 }
-
-void cmExportBuildAndroidMKGenerator::GenerateImportPropertyCode(
-  std::ostream&, const std::string&, const std::string&,
-  cmGeneratorTarget const*, ImportPropertyMap const&, const std::string&)
-{
-}
-
-void cmExportBuildAndroidMKGenerator::GenerateMissingTargetsCheckCode(
-  std::ostream&)
-{
-}
-
-void cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties(
-  const cmGeneratorTarget* target, std::ostream& os,
-  const ImportPropertyMap& properties)
-{
-  std::string config;
-  if (!this->Configurations.empty()) {
-    config = this->Configurations[0];
-  }
-  cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties(
-    target, os, properties, cmExportBuildAndroidMKGenerator::BUILD, config);
-}
-
-void cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties(
-  const cmGeneratorTarget* target, std::ostream& os,
-  const ImportPropertyMap& properties, GenerateType type,
-  std::string const& config)
-{
-  const bool newCMP0022Behavior =
-    target->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
-    target->GetPolicyStatusCMP0022() != cmPolicies::OLD;
-  if (!newCMP0022Behavior) {
-    std::ostringstream w;
-    if (type == cmExportBuildAndroidMKGenerator::BUILD) {
-      w << "export(TARGETS ... ANDROID_MK) called with policy CMP0022";
-    } else {
-      w << "install( EXPORT_ANDROID_MK ...) called with policy CMP0022";
-    }
-    w << " set to OLD for target " << target->Target->GetName() << ". "
-      << "The export will only work with CMP0022 set to NEW.";
-    target->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
-  }
-  if (!properties.empty()) {
-    os << "LOCAL_CPP_FEATURES := rtti exceptions\n";
-    for (auto const& property : properties) {
-      if (property.first == "INTERFACE_COMPILE_OPTIONS") {
-        os << "LOCAL_CPP_FEATURES += ";
-        os << (property.second) << "\n";
-      } else if (property.first == "INTERFACE_LINK_LIBRARIES") {
-        std::string staticLibs;
-        std::string sharedLibs;
-        std::string ldlibs;
-        cmLinkInterfaceLibraries const* linkIFace =
-          target->GetLinkInterfaceLibraries(config, target,
-                                            cmGeneratorTarget::UseTo::Link);
-        for (cmLinkItem const& item : linkIFace->Libraries) {
-          cmGeneratorTarget const* gt = item.Target;
-          std::string const& lib = item.AsStr();
-          if (gt) {
-
-            if (gt->GetType() == cmStateEnums::SHARED_LIBRARY ||
-                gt->GetType() == cmStateEnums::MODULE_LIBRARY) {
-              sharedLibs += " " + lib;
-            } else {
-              staticLibs += " " + lib;
-            }
-          } else {
-            bool relpath = false;
-            if (type == cmExportBuildAndroidMKGenerator::INSTALL) {
-              relpath = cmHasLiteralPrefix(lib, "../");
-            }
-            // check for full path or if it already has a -l, or
-            // in the case of an install check for relative paths
-            // if it is full or a link library then use string directly
-            if (cmSystemTools::FileIsFullPath(lib) ||
-                cmHasLiteralPrefix(lib, "-l") || relpath) {
-              ldlibs += " " + lib;
-              // if it is not a path and does not have a -l then add -l
-            } else if (!lib.empty()) {
-              ldlibs += " -l" + lib;
-            }
-          }
-        }
-        if (!sharedLibs.empty()) {
-          os << "LOCAL_SHARED_LIBRARIES :=" << sharedLibs << "\n";
-        }
-        if (!staticLibs.empty()) {
-          os << "LOCAL_STATIC_LIBRARIES :=" << staticLibs << "\n";
-        }
-        if (!ldlibs.empty()) {
-          os << "LOCAL_EXPORT_LDLIBS :=" << ldlibs << "\n";
-        }
-      } else if (property.first == "INTERFACE_INCLUDE_DIRECTORIES") {
-        std::string includes = property.second;
-        cmList includeList{ includes };
-        os << "LOCAL_EXPORT_C_INCLUDES := ";
-        std::string end;
-        for (std::string const& i : includeList) {
-          os << end << i;
-          end = "\\\n";
-        }
-        os << "\n";
-      } else if (property.first == "INTERFACE_LINK_OPTIONS") {
-        os << "LOCAL_EXPORT_LDFLAGS := ";
-        cmList linkFlagsList{ property.second };
-        os << linkFlagsList.join(" ") << "\n";
-      } else {
-        os << "# " << property.first << " " << (property.second) << "\n";
-      }
-    }
-  }
-
-  // Tell the NDK build system if prebuilt static libraries use C++.
-  if (target->GetType() == cmStateEnums::STATIC_LIBRARY) {
-    cmLinkImplementation const* li =
-      target->GetLinkImplementation(config, cmGeneratorTarget::UseTo::Link);
-    if (cm::contains(li->Languages, "CXX")) {
-      os << "LOCAL_HAS_CPP := true\n";
-    }
-  }
-
-  switch (target->GetType()) {
-    case cmStateEnums::SHARED_LIBRARY:
-    case cmStateEnums::MODULE_LIBRARY:
-      os << "include $(PREBUILT_SHARED_LIBRARY)\n";
-      break;
-    case cmStateEnums::STATIC_LIBRARY:
-      os << "include $(PREBUILT_STATIC_LIBRARY)\n";
-      break;
-    case cmStateEnums::EXECUTABLE:
-    case cmStateEnums::UTILITY:
-    case cmStateEnums::OBJECT_LIBRARY:
-    case cmStateEnums::GLOBAL_TARGET:
-    case cmStateEnums::INTERFACE_LIBRARY:
-    case cmStateEnums::UNKNOWN_LIBRARY:
-      break;
-  }
-  os << "\n";
-}

+ 13 - 28
Source/cmExportBuildAndroidMKGenerator.h

@@ -7,6 +7,7 @@
 #include <iosfwd>
 #include <string>
 
+#include "cmExportAndroidMKGenerator.h"
 #include "cmExportBuildFileGenerator.h"
 #include "cmStateTypes.h"
 
@@ -21,42 +22,26 @@ class cmGeneratorTarget;
  *
  * This is used to implement the export() command.
  */
-class cmExportBuildAndroidMKGenerator : public cmExportBuildFileGenerator
+class cmExportBuildAndroidMKGenerator
+  : public cmExportBuildFileGenerator
+  , public cmExportAndroidMKGenerator
 {
 public:
   cmExportBuildAndroidMKGenerator();
-  // this is so cmExportInstallAndroidMKGenerator can share this
-  // function as they are almost the same
-  enum GenerateType
-  {
-    BUILD,
-    INSTALL
-  };
-  static void GenerateInterfaceProperties(cmGeneratorTarget const* target,
-                                          std::ostream& os,
-                                          const ImportPropertyMap& properties,
-                                          GenerateType type,
-                                          std::string const& config);
+
+  /** Set whether to append generated code to the output file.  */
+  void SetAppendMode(bool append) { this->AppendMode = append; }
 
 protected:
+  GenerateType GetGenerateType() const override { return BUILD; }
+
   // Implement virtual methods from the superclass.
-  void GeneratePolicyHeaderCode(std::ostream&) override {}
-  void GeneratePolicyFooterCode(std::ostream&) override {}
+  bool GenerateMainFile(std::ostream& os) override;
   void GenerateImportHeaderCode(std::ostream& os,
-                                const std::string& config = "") override;
-  void GenerateImportFooterCode(std::ostream& os) override;
+                                std::string const& config = "") override;
   void GenerateImportTargetCode(
     std::ostream& os, cmGeneratorTarget const* target,
     cmStateEnums::TargetType /*targetType*/) override;
-  void GenerateExpectedTargetsCode(
-    std::ostream& os, const std::string& expectedTargets) override;
-  void GenerateImportPropertyCode(
-    std::ostream& os, const std::string& config, const std::string& suffix,
-    cmGeneratorTarget const* target, ImportPropertyMap const& properties,
-    const std::string& importedXcFrameworkLocation) override;
-  void GenerateMissingTargetsCheckCode(std::ostream& os) override;
-  void GenerateFindDependencyCalls(std::ostream&) override {}
-  void GenerateInterfaceProperties(
-    cmGeneratorTarget const* target, std::ostream& os,
-    const ImportPropertyMap& properties) override;
+
+  std::string GetCxxModulesDirectory() const override { return {}; }
 };

+ 343 - 0
Source/cmExportBuildCMakeConfigGenerator.cxx

@@ -0,0 +1,343 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmExportBuildCMakeConfigGenerator.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <map>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+#include <cm/string_view>
+#include <cmext/string_view>
+
+#include "cmCryptoHash.h"
+#include "cmExportSet.h"
+#include "cmFileSet.h"
+#include "cmGeneratedFileStream.h"
+#include "cmGeneratorExpression.h"
+#include "cmGeneratorTarget.h"
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmOutputConverter.h"
+#include "cmPolicies.h"
+#include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmTarget.h"
+
+cmExportBuildCMakeConfigGenerator::cmExportBuildCMakeConfigGenerator()
+{
+  this->LG = nullptr;
+  this->ExportSet = nullptr;
+}
+
+bool cmExportBuildCMakeConfigGenerator::GenerateMainFile(std::ostream& os)
+{
+  {
+    std::string expectedTargets;
+    std::string sep;
+    bool generatedInterfaceRequired = false;
+    auto visitor = [&](cmGeneratorTarget const* te) {
+      expectedTargets += sep + this->Namespace + te->GetExportName();
+      sep = " ";
+
+      generatedInterfaceRequired |=
+        this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY;
+    };
+
+    if (!this->CollectExports(visitor)) {
+      return false;
+    }
+
+    if (generatedInterfaceRequired) {
+      this->SetRequiredCMakeVersion(3, 0, 0);
+    }
+    this->GenerateExpectedTargetsCode(os, expectedTargets);
+  }
+
+  // Create all the imported targets.
+  for (auto const& exp : this->Exports) {
+    cmGeneratorTarget* gte = exp.Target;
+    this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
+
+    gte->Target->AppendBuildInterfaceIncludes();
+
+    ImportPropertyMap properties;
+    if (!this->PopulateInterfaceProperties(gte, properties)) {
+      return false;
+    }
+
+    bool const newCMP0022Behavior =
+      gte->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
+      gte->GetPolicyStatusCMP0022() != cmPolicies::OLD;
+    if (newCMP0022Behavior) {
+      this->PopulateInterfaceLinkLibrariesProperty(
+        gte, cmGeneratorExpression::BuildInterface, properties);
+    }
+
+    this->GenerateInterfaceProperties(gte, os, properties);
+
+    this->GenerateTargetFileSets(gte, os);
+  }
+
+  std::string cxx_modules_name;
+  if (this->ExportSet) {
+    cxx_modules_name = this->ExportSet->GetName();
+  } else {
+    cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
+    constexpr std::size_t HASH_TRUNCATION = 12;
+    for (auto const& target : this->Targets) {
+      hasher.Append(target.Name);
+    }
+    cxx_modules_name = hasher.FinalizeHex().substr(0, HASH_TRUNCATION);
+  }
+
+  this->GenerateCxxModuleInformation(cxx_modules_name, os);
+
+  // Generate import file content for each configuration.
+  for (std::string const& c : this->Configurations) {
+    this->GenerateImportConfig(os, c);
+  }
+
+  // Generate import file content for each configuration.
+  for (std::string const& c : this->Configurations) {
+    this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name, c);
+  }
+
+  this->GenerateMissingTargetsCheckCode(os);
+
+  return true;
+}
+
+void cmExportBuildCMakeConfigGenerator::GenerateImportTargetsConfig(
+  std::ostream& os, std::string const& config, std::string const& suffix)
+{
+  for (auto const& exp : this->Exports) {
+    cmGeneratorTarget* target = exp.Target;
+
+    // Collect import properties for this target.
+    ImportPropertyMap properties;
+
+    if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) {
+      this->SetImportLocationProperty(config, suffix, target, properties);
+    }
+    if (!properties.empty()) {
+      // Get the rest of the target details.
+      if (this->GetExportTargetType(target) !=
+          cmStateEnums::INTERFACE_LIBRARY) {
+        this->SetImportDetailProperties(config, suffix, target, properties);
+        this->SetImportLinkInterface(config, suffix,
+                                     cmGeneratorExpression::BuildInterface,
+                                     target, properties);
+      }
+
+      // TODO: PUBLIC_HEADER_LOCATION
+      // This should wait until the build feature propagation stuff
+      // is done.  Then this can be a propagated include directory.
+      // this->GenerateImportProperty(config, te->HeaderGenerator,
+      //                              properties);
+
+      // Generate code in the export file.
+      std::string importedXcFrameworkLocation = exp.XcFrameworkLocation;
+      if (!importedXcFrameworkLocation.empty()) {
+        importedXcFrameworkLocation = cmGeneratorExpression::Preprocess(
+          importedXcFrameworkLocation,
+          cmGeneratorExpression::PreprocessContext::BuildInterface);
+        importedXcFrameworkLocation = cmGeneratorExpression::Evaluate(
+          importedXcFrameworkLocation, exp.Target->GetLocalGenerator(), config,
+          exp.Target, nullptr, exp.Target);
+        if (!importedXcFrameworkLocation.empty() &&
+            !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation)) {
+          importedXcFrameworkLocation =
+            cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/',
+                     importedXcFrameworkLocation);
+        }
+      }
+      this->GenerateImportPropertyCode(os, config, suffix, target, properties,
+                                       importedXcFrameworkLocation);
+    }
+  }
+}
+
+namespace {
+bool EntryIsContextSensitive(
+  std::unique_ptr<cmCompiledGeneratorExpression> const& cge)
+{
+  return cge->GetHadContextSensitiveCondition();
+}
+}
+
+std::string cmExportBuildCMakeConfigGenerator::GetFileSetDirectories(
+  cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* /*te*/)
+{
+  std::vector<std::string> resultVector;
+
+  auto configs =
+    gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
+  auto directoryEntries = fileSet->CompileDirectoryEntries();
+
+  for (auto const& config : configs) {
+    auto directories = fileSet->EvaluateDirectoryEntries(
+      directoryEntries, gte->LocalGenerator, config, gte);
+
+    bool const contextSensitive =
+      std::any_of(directoryEntries.begin(), directoryEntries.end(),
+                  EntryIsContextSensitive);
+
+    auto const& type = fileSet->GetType();
+    // C++ modules do not support interface file sets which are dependent upon
+    // the configuration.
+    if (contextSensitive && type == "CXX_MODULES"_s) {
+      auto* mf = this->LG->GetMakefile();
+      std::ostringstream e;
+      e << "The \"" << gte->GetName() << "\" target's interface file set \""
+        << fileSet->GetName() << "\" of type \"" << type
+        << "\" contains context-sensitive base directory entries which is not "
+           "supported.";
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return std::string{};
+    }
+
+    for (auto const& directory : directories) {
+      auto dest = cmOutputConverter::EscapeForCMake(
+        directory, cmOutputConverter::WrapQuotes::NoWrap);
+
+      if (contextSensitive && configs.size() != 1) {
+        resultVector.push_back(
+          cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
+      } else {
+        resultVector.emplace_back(cmStrCat('"', dest, '"'));
+        break;
+      }
+    }
+  }
+
+  return cmJoin(resultVector, " ");
+}
+
+std::string cmExportBuildCMakeConfigGenerator::GetFileSetFiles(
+  cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* /*te*/)
+{
+  std::vector<std::string> resultVector;
+
+  auto configs =
+    gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
+
+  auto fileEntries = fileSet->CompileFileEntries();
+  auto directoryEntries = fileSet->CompileDirectoryEntries();
+
+  for (auto const& config : configs) {
+    auto directories = fileSet->EvaluateDirectoryEntries(
+      directoryEntries, gte->LocalGenerator, config, gte);
+
+    std::map<std::string, std::vector<std::string>> files;
+    for (auto const& entry : fileEntries) {
+      fileSet->EvaluateFileEntry(directories, files, entry,
+                                 gte->LocalGenerator, config, gte);
+    }
+
+    bool const contextSensitive =
+      std::any_of(directoryEntries.begin(), directoryEntries.end(),
+                  EntryIsContextSensitive) ||
+      std::any_of(fileEntries.begin(), fileEntries.end(),
+                  EntryIsContextSensitive);
+
+    auto const& type = fileSet->GetType();
+    // C++ modules do not support interface file sets which are dependent upon
+    // the configuration.
+    if (contextSensitive && type == "CXX_MODULES"_s) {
+      auto* mf = this->LG->GetMakefile();
+      std::ostringstream e;
+      e << "The \"" << gte->GetName() << "\" target's interface file set \""
+        << fileSet->GetName() << "\" of type \"" << type
+        << "\" contains context-sensitive file entries which is not "
+           "supported.";
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return std::string{};
+    }
+
+    for (auto const& it : files) {
+      for (auto const& filename : it.second) {
+        auto escapedFile = cmOutputConverter::EscapeForCMake(
+          filename, cmOutputConverter::WrapQuotes::NoWrap);
+        if (contextSensitive && configs.size() != 1) {
+          resultVector.push_back(
+            cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
+        } else {
+          resultVector.emplace_back(cmStrCat('"', escapedFile, '"'));
+        }
+      }
+    }
+
+    if (!(contextSensitive && configs.size() != 1)) {
+      break;
+    }
+  }
+
+  return cmJoin(resultVector, " ");
+}
+
+void cmExportBuildCMakeConfigGenerator::GenerateCxxModuleConfigInformation(
+  std::string const& name, std::ostream& os) const
+{
+  char const* opt = "";
+  if (this->Configurations.size() > 1) {
+    // With more than one configuration, each individual file is optional.
+    opt = " OPTIONAL";
+  }
+
+  // Generate import file content for each configuration.
+  for (std::string c : this->Configurations) {
+    if (c.empty()) {
+      c = "noconfig";
+    }
+    os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << '-'
+       << c << ".cmake\"" << opt << ")\n";
+  }
+}
+
+bool cmExportBuildCMakeConfigGenerator::
+  GenerateImportCxxModuleConfigTargetInclusion(std::string const& name,
+                                               std::string config) const
+{
+  auto cxx_modules_dirname = this->GetCxxModulesDirectory();
+  if (cxx_modules_dirname.empty()) {
+    return true;
+  }
+
+  if (config.empty()) {
+    config = "noconfig";
+  }
+
+  std::string fileName =
+    cmStrCat(this->FileDir, '/', cxx_modules_dirname, "/cxx-modules-", name,
+             '-', config, ".cmake");
+
+  cmGeneratedFileStream os(fileName, true);
+  if (!os) {
+    std::string se = cmSystemTools::GetLastSystemError();
+    std::ostringstream e;
+    e << "cannot write to file \"" << fileName << "\": " << se;
+    cmSystemTools::Error(e.str());
+    return false;
+  }
+  os.SetCopyIfDifferent(true);
+
+  for (auto const* tgt : this->ExportedTargets) {
+    // Only targets with C++ module sources will have a
+    // collator-generated install script.
+    if (!tgt->HaveCxx20ModuleSources()) {
+      continue;
+    }
+
+    os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-"
+       << tgt->GetFilesystemExportName() << '-' << config << ".cmake\")\n";
+  }
+
+  return true;
+}

+ 52 - 0
Source/cmExportBuildCMakeConfigGenerator.h

@@ -0,0 +1,52 @@
+/* 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
+
+#include <iosfwd>
+#include <string>
+
+#include "cmExportBuildFileGenerator.h"
+#include "cmExportCMakeConfigGenerator.h"
+
+class cmFileSet;
+class cmGeneratorTarget;
+class cmTargetExport;
+
+/** \class cmExportBuildCMakeConfigGenerator
+ * \brief Generate a file exporting targets from a build tree.
+ *
+ * cmExportBuildCMakeConfigGenerator generates a file exporting targets from
+ * a build tree.  This exports the targets to CMake's native package
+ * configuration format.  A single file exports information for all
+ * configurations built.
+ *
+ * This is used to implement the export() command.
+ */
+class cmExportBuildCMakeConfigGenerator
+  : public cmExportCMakeConfigGenerator
+  , public cmExportBuildFileGenerator
+{
+public:
+  cmExportBuildCMakeConfigGenerator();
+
+  /** Set whether to append generated code to the output file.  */
+  void SetAppendMode(bool append) { this->AppendMode = append; }
+
+protected:
+  // Implement virtual methods from the superclass.
+  bool GenerateMainFile(std::ostream& os) override;
+  void GenerateImportTargetsConfig(std::ostream& os, std::string const& config,
+                                   std::string const& suffix) override;
+
+  std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet,
+                                    cmTargetExport const* te) override;
+  std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet,
+                              cmTargetExport const* te) override;
+
+  void GenerateCxxModuleConfigInformation(std::string const&,
+                                          std::ostream&) const override;
+  bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&,
+                                                    std::string) const;
+};

+ 60 - 388
Source/cmExportBuildFileGenerator.cxx

@@ -3,20 +3,13 @@
 #include "cmExportBuildFileGenerator.h"
 
 #include <algorithm>
-#include <cstddef>
 #include <map>
 #include <memory>
 #include <set>
 #include <sstream>
 #include <utility>
 
-#include <cm/string_view>
-#include <cmext/string_view>
-
-#include "cmCryptoHash.h"
 #include "cmExportSet.h"
-#include "cmFileSet.h"
-#include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -24,11 +17,8 @@
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmOutputConverter.h"
-#include "cmPolicies.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
-#include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
 #include "cmValue.h"
@@ -50,195 +40,6 @@ void cmExportBuildFileGenerator::Compute(cmLocalGenerator* lg)
   }
 }
 
-bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
-{
-  {
-    std::string expectedTargets;
-    std::string sep;
-    std::vector<TargetExport> targets;
-    bool generatedInterfaceRequired = false;
-    this->GetTargets(targets);
-    for (auto const& tei : targets) {
-      cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name);
-      expectedTargets += sep + this->Namespace + te->GetExportName();
-      sep = " ";
-      if (this->ExportedTargets.insert(te).second) {
-        this->Exports.emplace_back(te, tei.XcFrameworkLocation);
-      } else {
-        std::ostringstream e;
-        e << "given target \"" << te->GetName() << "\" more than once.";
-        this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
-          MessageType::FATAL_ERROR, e.str(),
-          this->LG->GetMakefile()->GetBacktrace());
-        return false;
-      }
-      generatedInterfaceRequired |=
-        this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY;
-    }
-
-    if (generatedInterfaceRequired) {
-      this->SetRequiredCMakeVersion(3, 0, 0);
-    }
-    this->GenerateExpectedTargetsCode(os, expectedTargets);
-  }
-
-  // Create all the imported targets.
-  for (auto const& exp : this->Exports) {
-    cmGeneratorTarget* gte = exp.Target;
-    this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
-
-    gte->Target->AppendBuildInterfaceIncludes();
-
-    ImportPropertyMap properties;
-
-    this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", gte,
-                                    cmGeneratorExpression::BuildInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_SOURCES", gte,
-                                    cmGeneratorExpression::BuildInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gte,
-                                    cmGeneratorExpression::BuildInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gte,
-                                    cmGeneratorExpression::BuildInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gte,
-                                    cmGeneratorExpression::BuildInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
-                                    cmGeneratorExpression::BuildInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", gte,
-                                    cmGeneratorExpression::BuildInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte,
-                                    cmGeneratorExpression::BuildInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte,
-                                    cmGeneratorExpression::BuildInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", gte,
-                                    cmGeneratorExpression::BuildInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte,
-                                    cmGeneratorExpression::BuildInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte,
-                                    properties);
-
-    std::string errorMessage;
-    if (!this->PopulateCxxModuleExportProperties(
-          gte, properties, cmGeneratorExpression::BuildInterface, {},
-          errorMessage)) {
-      this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
-        MessageType::FATAL_ERROR, errorMessage,
-        this->LG->GetMakefile()->GetBacktrace());
-      return false;
-    }
-
-    if (!this->PopulateExportProperties(gte, properties, errorMessage)) {
-      this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
-        MessageType::FATAL_ERROR, errorMessage,
-        this->LG->GetMakefile()->GetBacktrace());
-      return false;
-    }
-
-    const bool newCMP0022Behavior =
-      gte->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
-      gte->GetPolicyStatusCMP0022() != cmPolicies::OLD;
-    if (newCMP0022Behavior) {
-      this->PopulateInterfaceLinkLibrariesProperty(
-        gte, cmGeneratorExpression::BuildInterface, properties);
-    }
-    this->PopulateCompatibleInterfaceProperties(gte, properties);
-    this->PopulateCustomTransitiveInterfaceProperties(
-      gte, cmGeneratorExpression::BuildInterface, properties);
-
-    this->GenerateInterfaceProperties(gte, os, properties);
-
-    this->GenerateTargetFileSets(gte, os);
-  }
-
-  std::string cxx_modules_name;
-  if (this->ExportSet) {
-    cxx_modules_name = this->ExportSet->GetName();
-  } else {
-    cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
-    constexpr std::size_t HASH_TRUNCATION = 12;
-    for (auto const& target : this->Targets) {
-      hasher.Append(target.Name);
-    }
-    cxx_modules_name = hasher.FinalizeHex().substr(0, HASH_TRUNCATION);
-  }
-
-  this->GenerateCxxModuleInformation(cxx_modules_name, os);
-
-  // Generate import file content for each configuration.
-  for (std::string const& c : this->Configurations) {
-    this->GenerateImportConfig(os, c);
-  }
-
-  // Generate import file content for each configuration.
-  for (std::string const& c : this->Configurations) {
-    this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name, c);
-  }
-
-  this->GenerateMissingTargetsCheckCode(os);
-
-  return true;
-}
-
-void cmExportBuildFileGenerator::GenerateImportTargetsConfig(
-  std::ostream& os, const std::string& config, std::string const& suffix)
-{
-  for (auto const& exp : this->Exports) {
-    cmGeneratorTarget* target = exp.Target;
-
-    // Collect import properties for this target.
-    ImportPropertyMap properties;
-
-    if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) {
-      this->SetImportLocationProperty(config, suffix, target, properties);
-    }
-    if (!properties.empty()) {
-      // Get the rest of the target details.
-      if (this->GetExportTargetType(target) !=
-          cmStateEnums::INTERFACE_LIBRARY) {
-        this->SetImportDetailProperties(config, suffix, target, properties);
-        this->SetImportLinkInterface(config, suffix,
-                                     cmGeneratorExpression::BuildInterface,
-                                     target, properties);
-      }
-
-      // TODO: PUBLIC_HEADER_LOCATION
-      // This should wait until the build feature propagation stuff
-      // is done.  Then this can be a propagated include directory.
-      // this->GenerateImportProperty(config, te->HeaderGenerator,
-      //                              properties);
-
-      // Generate code in the export file.
-      std::string importedXcFrameworkLocation = exp.XcFrameworkLocation;
-      if (!importedXcFrameworkLocation.empty()) {
-        importedXcFrameworkLocation = cmGeneratorExpression::Preprocess(
-          importedXcFrameworkLocation,
-          cmGeneratorExpression::PreprocessContext::BuildInterface);
-        importedXcFrameworkLocation = cmGeneratorExpression::Evaluate(
-          importedXcFrameworkLocation, exp.Target->GetLocalGenerator(), config,
-          exp.Target, nullptr, exp.Target);
-        if (!importedXcFrameworkLocation.empty() &&
-            !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation)) {
-          importedXcFrameworkLocation =
-            cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/',
-                     importedXcFrameworkLocation);
-        }
-      }
-      this->GenerateImportPropertyCode(os, config, suffix, target, properties,
-                                       importedXcFrameworkLocation);
-    }
-  }
-}
-
 cmStateEnums::TargetType cmExportBuildFileGenerator::GetExportTargetType(
   cmGeneratorTarget const* target) const
 {
@@ -260,7 +61,7 @@ void cmExportBuildFileGenerator::SetExportSet(cmExportSet* exportSet)
 }
 
 void cmExportBuildFileGenerator::SetImportLocationProperty(
-  const std::string& config, std::string const& suffix,
+  std::string const& config, std::string const& suffix,
   cmGeneratorTarget* target, ImportPropertyMap& properties)
 {
   // Get the makefile in which to lookup target information.
@@ -276,7 +77,7 @@ void cmExportBuildFileGenerator::SetImportLocationProperty(
     std::string const obj_dir = target->GetObjectDirectory(config);
     std::vector<std::string> objects;
     for (cmSourceFile const* sf : objectSources) {
-      const std::string& obj = target->GetObjectName(sf);
+      std::string const& obj = target->GetObjectName(sf);
       objects.push_back(obj_dir + obj);
     }
 
@@ -311,13 +112,33 @@ void cmExportBuildFileGenerator::SetImportLocationProperty(
   }
 }
 
+bool cmExportBuildFileGenerator::CollectExports(
+  std::function<void(cmGeneratorTarget const*)> visitor)
+{
+  auto pred = [&](cmExportBuildFileGenerator::TargetExport& tei) -> bool {
+    cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name);
+    if (this->ExportedTargets.insert(te).second) {
+      this->Exports.emplace_back(te, tei.XcFrameworkLocation);
+      visitor(te);
+      return true;
+    }
+
+    this->ComplainAboutDuplicateTarget(te->GetName());
+    return false;
+  };
+
+  std::vector<TargetExport> targets;
+  this->GetTargets(targets);
+  return std::all_of(targets.begin(), targets.end(), pred);
+}
+
 void cmExportBuildFileGenerator::HandleMissingTarget(
   std::string& link_libs, cmGeneratorTarget const* depender,
   cmGeneratorTarget* dependee)
 {
   // The target is not in the export.
   if (!this->AppendMode) {
-    const std::string name = dependee->GetName();
+    std::string const& name = dependee->GetName();
     cmGlobalGenerator* gg =
       dependee->GetLocalGenerator()->GetGlobalGenerator();
     auto exportInfo = this->FindBuildExportInfo(gg, name);
@@ -359,7 +180,7 @@ void cmExportBuildFileGenerator::GetTargets(
 
 std::pair<std::vector<std::string>, std::string>
 cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg,
-                                                const std::string& name)
+                                                std::string const& name)
 {
   std::vector<std::string> exportFiles;
   std::string ns;
@@ -367,12 +188,12 @@ cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg,
   auto& exportSets = gg->GetBuildExportSets();
 
   for (auto const& exp : exportSets) {
-    const auto& exportSet = exp.second;
+    auto const& exportSet = exp.second;
     std::vector<TargetExport> targets;
     exportSet->GetTargets(targets);
     if (std::any_of(
           targets.begin(), targets.end(),
-          [&name](const TargetExport& te) { return te.Name == name; })) {
+          [&name](TargetExport const& te) { return te.Name == name; })) {
       exportFiles.push_back(exp.first);
       ns = exportSet->GetNamespace();
     }
@@ -383,7 +204,7 @@ cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg,
 
 void cmExportBuildFileGenerator::ComplainAboutMissingTarget(
   cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
-  std::vector<std::string> const& exportFiles)
+  std::vector<std::string> const& exportFiles) const
 {
   std::ostringstream e;
   e << "export called with target \"" << depender->GetName()
@@ -399,13 +220,27 @@ void cmExportBuildFileGenerator::ComplainAboutMissingTarget(
       << dependee->GetName() << "\" target to a single export.";
   }
 
+  this->ReportError(e.str());
+}
+
+void cmExportBuildFileGenerator::ComplainAboutDuplicateTarget(
+  std::string const& targetName) const
+{
+  std::ostringstream e;
+  e << "given target \"" << targetName << "\" more than once.";
+  this->ReportError(e.str());
+}
+
+void cmExportBuildFileGenerator::ReportError(
+  std::string const& errorMessage) const
+{
   this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
-    MessageType::FATAL_ERROR, e.str(),
+    MessageType::FATAL_ERROR, errorMessage,
     this->LG->GetMakefile()->GetBacktrace());
 }
 
 std::string cmExportBuildFileGenerator::InstallNameDir(
-  cmGeneratorTarget const* target, const std::string& config)
+  cmGeneratorTarget const* target, std::string const& config)
 {
   std::string install_name_dir;
 
@@ -417,185 +252,22 @@ std::string cmExportBuildFileGenerator::InstallNameDir(
   return install_name_dir;
 }
 
-namespace {
-bool EntryIsContextSensitive(
-  const std::unique_ptr<cmCompiledGeneratorExpression>& cge)
-{
-  return cge->GetHadContextSensitiveCondition();
-}
-}
-
-std::string cmExportBuildFileGenerator::GetFileSetDirectories(
-  cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* /*te*/)
-{
-  std::vector<std::string> resultVector;
-
-  auto configs =
-    gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
-  auto directoryEntries = fileSet->CompileDirectoryEntries();
-
-  for (auto const& config : configs) {
-    auto directories = fileSet->EvaluateDirectoryEntries(
-      directoryEntries, gte->LocalGenerator, config, gte);
-
-    bool const contextSensitive =
-      std::any_of(directoryEntries.begin(), directoryEntries.end(),
-                  EntryIsContextSensitive);
-
-    auto const& type = fileSet->GetType();
-    // C++ modules do not support interface file sets which are dependent upon
-    // the configuration.
-    if (contextSensitive && type == "CXX_MODULES"_s) {
-      auto* mf = this->LG->GetMakefile();
-      std::ostringstream e;
-      e << "The \"" << gte->GetName() << "\" target's interface file set \""
-        << fileSet->GetName() << "\" of type \"" << type
-        << "\" contains context-sensitive base directory entries which is not "
-           "supported.";
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
-      return std::string{};
-    }
-
-    for (auto const& directory : directories) {
-      auto dest = cmOutputConverter::EscapeForCMake(
-        directory, cmOutputConverter::WrapQuotes::NoWrap);
-
-      if (contextSensitive && configs.size() != 1) {
-        resultVector.push_back(
-          cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
-      } else {
-        resultVector.emplace_back(cmStrCat('"', dest, '"'));
-        break;
-      }
-    }
-  }
-
-  return cmJoin(resultVector, " ");
-}
-
-std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte,
-                                                        cmFileSet* fileSet,
-                                                        cmTargetExport* /*te*/)
+bool cmExportBuildFileGenerator::PopulateInterfaceProperties(
+  cmGeneratorTarget const* target, ImportPropertyMap& properties)
 {
-  std::vector<std::string> resultVector;
-
-  auto configs =
-    gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
-
-  auto fileEntries = fileSet->CompileFileEntries();
-  auto directoryEntries = fileSet->CompileDirectoryEntries();
-
-  for (auto const& config : configs) {
-    auto directories = fileSet->EvaluateDirectoryEntries(
-      directoryEntries, gte->LocalGenerator, config, gte);
-
-    std::map<std::string, std::vector<std::string>> files;
-    for (auto const& entry : fileEntries) {
-      fileSet->EvaluateFileEntry(directories, files, entry,
-                                 gte->LocalGenerator, config, gte);
-    }
-
-    bool const contextSensitive =
-      std::any_of(directoryEntries.begin(), directoryEntries.end(),
-                  EntryIsContextSensitive) ||
-      std::any_of(fileEntries.begin(), fileEntries.end(),
-                  EntryIsContextSensitive);
-
-    auto const& type = fileSet->GetType();
-    // C++ modules do not support interface file sets which are dependent upon
-    // the configuration.
-    if (contextSensitive && type == "CXX_MODULES"_s) {
-      auto* mf = this->LG->GetMakefile();
-      std::ostringstream e;
-      e << "The \"" << gte->GetName() << "\" target's interface file set \""
-        << fileSet->GetName() << "\" of type \"" << type
-        << "\" contains context-sensitive file entries which is not "
-           "supported.";
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
-      return std::string{};
-    }
-
-    for (auto const& it : files) {
-      for (auto const& filename : it.second) {
-        auto escapedFile = cmOutputConverter::EscapeForCMake(
-          filename, cmOutputConverter::WrapQuotes::NoWrap);
-        if (contextSensitive && configs.size() != 1) {
-          resultVector.push_back(
-            cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
-        } else {
-          resultVector.emplace_back(cmStrCat('"', escapedFile, '"'));
-        }
-      }
-    }
-
-    if (!(contextSensitive && configs.size() != 1)) {
-      break;
-    }
-  }
-
-  return cmJoin(resultVector, " ");
-}
-
-std::string cmExportBuildFileGenerator::GetCxxModulesDirectory() const
-{
-  return this->CxxModulesDirectory;
-}
-
-void cmExportBuildFileGenerator::GenerateCxxModuleConfigInformation(
-  std::string const& name, std::ostream& os) const
-{
-  const char* opt = "";
-  if (this->Configurations.size() > 1) {
-    // With more than one configuration, each individual file is optional.
-    opt = " OPTIONAL";
-  }
-
-  // Generate import file content for each configuration.
-  for (std::string c : this->Configurations) {
-    if (c.empty()) {
-      c = "noconfig";
-    }
-    os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << '-'
-       << c << ".cmake\"" << opt << ")\n";
-  }
-}
-
-bool cmExportBuildFileGenerator::GenerateImportCxxModuleConfigTargetInclusion(
-  std::string const& name, std::string config) const
-{
-  auto cxx_modules_dirname = this->GetCxxModulesDirectory();
-  if (cxx_modules_dirname.empty()) {
-    return true;
-  }
-
-  if (config.empty()) {
-    config = "noconfig";
-  }
-
-  std::string fileName =
-    cmStrCat(this->FileDir, '/', cxx_modules_dirname, "/cxx-modules-", name,
-             '-', config, ".cmake");
-
-  cmGeneratedFileStream os(fileName, true);
-  if (!os) {
-    std::string se = cmSystemTools::GetLastSystemError();
-    std::ostringstream e;
-    e << "cannot write to file \"" << fileName << "\": " << se;
-    cmSystemTools::Error(e.str());
-    return false;
-  }
-  os.SetCopyIfDifferent(true);
-
-  for (auto const* tgt : this->ExportedTargets) {
-    // Only targets with C++ module sources will have a
-    // collator-generated install script.
-    if (!tgt->HaveCxx20ModuleSources()) {
-      continue;
-    }
-
-    os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-"
-       << tgt->GetFilesystemExportName() << '-' << config << ".cmake\")\n";
-  }
-
-  return true;
+  this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", target,
+                                  cmGeneratorExpression::BuildInterface,
+                                  properties);
+  this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", target,
+                                  cmGeneratorExpression::BuildInterface,
+                                  properties);
+  this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", target,
+                                  cmGeneratorExpression::BuildInterface,
+                                  properties);
+  this->PopulateInterfaceProperty("INTERFACE_SOURCES", target,
+                                  cmGeneratorExpression::BuildInterface,
+                                  properties);
+
+  return this->PopulateInterfaceProperties(
+    target, {}, cmGeneratorExpression::BuildInterface, properties);
 }

+ 30 - 31
Source/cmExportBuildFileGenerator.h

@@ -4,7 +4,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <iosfwd>
+#include <functional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -15,22 +15,19 @@
 #include "cmStateTypes.h"
 
 class cmExportSet;
-class cmFileSet;
 class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmLocalGenerator;
-class cmTargetExport;
 
-/** \class cmExportBuildFileGenerator
+/** \class cmExportBuildCMakeConfigGenerator
  * \brief Generate a file exporting targets from a build tree.
  *
- * cmExportBuildFileGenerator generates a file exporting targets from
- * a build tree.  A single file exports information for all
- * configurations built.
+ * cmExportBuildCMakeConfigGenerator is the interface class for generating a
+ * file exporting targets from a build tree.
  *
  * This is used to implement the export() command.
  */
-class cmExportBuildFileGenerator : public cmExportFileGenerator
+class cmExportBuildFileGenerator : virtual public cmExportFileGenerator
 {
 public:
   struct TargetExport
@@ -64,54 +61,56 @@ public:
   {
     this->CxxModulesDirectory = std::move(cxx_module_dir);
   }
-  const std::string& GetCxxModuleDirectory() const
+  std::string const& GetCxxModuleDirectory() const
   {
     return this->CxxModulesDirectory;
   }
 
-  /** Set whether to append generated code to the output file.  */
-  void SetAppendMode(bool append) { this->AppendMode = append; }
-
   void Compute(cmLocalGenerator* lg);
 
 protected:
-  // Implement virtual methods from the superclass.
-  bool GenerateMainFile(std::ostream& os) override;
-  void GenerateImportTargetsConfig(std::ostream& os, const std::string& config,
-                                   std::string const& suffix) override;
   cmStateEnums::TargetType GetExportTargetType(
     cmGeneratorTarget const* target) const;
+
+  /** Walk the list of targets to be exported.  Returns true iff no duplicates
+      are found.  */
+  bool CollectExports(std::function<void(cmGeneratorTarget const*)> visitor);
+
   void HandleMissingTarget(std::string& link_libs,
                            cmGeneratorTarget const* depender,
                            cmGeneratorTarget* dependee) override;
 
-  void ComplainAboutMissingTarget(cmGeneratorTarget const* depender,
-                                  cmGeneratorTarget const* dependee,
-                                  std::vector<std::string> const& namespaces);
+  void ComplainAboutMissingTarget(
+    cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
+    std::vector<std::string> const& namespaces) const;
+
+  void ComplainAboutDuplicateTarget(
+    std::string const& targetName) const override;
+
+  void ReportError(std::string const& errorMessage) const override;
 
   /** Fill in properties indicating built file locations.  */
-  void SetImportLocationProperty(const std::string& config,
+  void SetImportLocationProperty(std::string const& config,
                                  std::string const& suffix,
                                  cmGeneratorTarget* target,
                                  ImportPropertyMap& properties);
 
   std::string InstallNameDir(cmGeneratorTarget const* target,
-                             const std::string& config) override;
+                             std::string const& config) override;
 
-  std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet,
-                                    cmTargetExport* te) override;
-  std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet,
-                              cmTargetExport* te) override;
   cmExportSet* GetExportSet() const override { return this->ExportSet; }
 
-  std::string GetCxxModulesDirectory() const override;
-  void GenerateCxxModuleConfigInformation(std::string const&,
-                                          std::ostream&) const override;
-  bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&,
-                                                    std::string) const;
+  std::string GetCxxModulesDirectory() const override
+  {
+    return this->CxxModulesDirectory;
+  }
 
   std::pair<std::vector<std::string>, std::string> FindBuildExportInfo(
-    cmGlobalGenerator* gg, const std::string& name);
+    cmGlobalGenerator* gg, std::string const& name);
+
+  using cmExportFileGenerator::PopulateInterfaceProperties;
+  bool PopulateInterfaceProperties(cmGeneratorTarget const* target,
+                                   ImportPropertyMap& properties);
 
   struct TargetExportPrivate
   {

+ 686 - 0
Source/cmExportCMakeConfigGenerator.cxx

@@ -0,0 +1,686 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmExportCMakeConfigGenerator.h"
+
+#include <algorithm>
+#include <cassert>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+#include <cm/optional>
+#include <cm/string_view>
+#include <cmext/string_view>
+
+#include "cmExportSet.h"
+#include "cmFileSet.h"
+#include "cmFindPackageStack.h"
+#include "cmGeneratedFileStream.h"
+#include "cmGeneratorTarget.h"
+#include "cmLinkItem.h"
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmOutputConverter.h"
+#include "cmPolicies.h"
+#include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmTarget.h"
+#include "cmValue.h"
+#include "cmVersion.h"
+
+static std::string cmExportFileGeneratorEscape(std::string const& str)
+{
+  // Escape a property value for writing into a .cmake file.
+  std::string result = cmOutputConverter::EscapeForCMake(str);
+  // Un-escape variable references generated by our own export code.
+  cmSystemTools::ReplaceString(result, "\\${_IMPORT_PREFIX}",
+                               "${_IMPORT_PREFIX}");
+  cmSystemTools::ReplaceString(result, "\\${CMAKE_IMPORT_LIBRARY_SUFFIX}",
+                               "${CMAKE_IMPORT_LIBRARY_SUFFIX}");
+  return result;
+}
+
+cmExportCMakeConfigGenerator::cmExportCMakeConfigGenerator() = default;
+
+cm::string_view cmExportCMakeConfigGenerator::GetImportPrefixWithSlash() const
+{
+  return "${_IMPORT_PREFIX}/"_s;
+}
+
+bool cmExportCMakeConfigGenerator::GenerateImportFile(std::ostream& os)
+{
+  std::stringstream mainFileWithHeadersAndFootersBuffer;
+
+  // Start with the import file header.
+  this->GenerateImportHeaderCode(mainFileWithHeadersAndFootersBuffer);
+
+  // Create all the imported targets.
+  std::stringstream mainFileBuffer;
+  bool result = this->GenerateMainFile(mainFileBuffer);
+
+  // Export find_dependency() calls. Must be done after GenerateMainFile(),
+  // because that's when target dependencies are gathered, which we need for
+  // the find_dependency() calls.
+  if (!this->AppendMode && this->GetExportSet() &&
+      this->ExportPackageDependencies) {
+    this->SetRequiredCMakeVersion(3, 9, 0);
+    this->GenerateFindDependencyCalls(mainFileWithHeadersAndFootersBuffer);
+  }
+
+  // Write cached import code.
+  mainFileWithHeadersAndFootersBuffer << mainFileBuffer.rdbuf();
+
+  // End with the import file footer.
+  this->GenerateImportFooterCode(mainFileWithHeadersAndFootersBuffer);
+  this->GeneratePolicyFooterCode(mainFileWithHeadersAndFootersBuffer);
+
+  // This has to be done last, after the minimum CMake version has been
+  // determined.
+  this->GeneratePolicyHeaderCode(os);
+  os << mainFileWithHeadersAndFootersBuffer.rdbuf();
+
+  return result;
+}
+
+void cmExportCMakeConfigGenerator::GenerateInterfaceProperties(
+  cmGeneratorTarget const* target, std::ostream& os,
+  ImportPropertyMap const& properties)
+{
+  if (!properties.empty()) {
+    std::string targetName =
+      cmStrCat(this->Namespace, target->GetExportName());
+    os << "set_target_properties(" << targetName << " PROPERTIES\n";
+    for (auto const& property : properties) {
+      os << "  " << property.first << " "
+         << cmExportFileGeneratorEscape(property.second) << "\n";
+    }
+    os << ")\n\n";
+  }
+}
+
+void cmExportCMakeConfigGenerator::SetImportLinkInterface(
+  std::string const& config, std::string const& suffix,
+  cmGeneratorExpression::PreprocessContext preprocessRule,
+  cmGeneratorTarget const* target, ImportPropertyMap& properties)
+{
+  // Add the transitive link dependencies for this configuration.
+  cmLinkInterface const* iface = target->GetLinkInterface(config, target);
+  if (!iface) {
+    return;
+  }
+
+  if (iface->ImplementationIsInterface) {
+    // Policy CMP0022 must not be NEW.
+    this->SetImportLinkProperty(
+      suffix, target, "IMPORTED_LINK_INTERFACE_LIBRARIES", iface->Libraries,
+      properties, ImportLinkPropertyTargetNames::Yes);
+    return;
+  }
+
+  cmValue propContent;
+
+  if (cmValue prop_suffixed =
+        target->GetProperty("LINK_INTERFACE_LIBRARIES" + suffix)) {
+    propContent = prop_suffixed;
+  } else if (cmValue prop = target->GetProperty("LINK_INTERFACE_LIBRARIES")) {
+    propContent = prop;
+  } else {
+    return;
+  }
+
+  bool const newCMP0022Behavior =
+    target->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
+    target->GetPolicyStatusCMP0022() != cmPolicies::OLD;
+
+  if (newCMP0022Behavior && !this->ExportOld) {
+    cmLocalGenerator* lg = target->GetLocalGenerator();
+    std::ostringstream e;
+    e << "Target \"" << target->GetName()
+      << "\" has policy CMP0022 enabled, "
+         "but also has old-style LINK_INTERFACE_LIBRARIES properties "
+         "populated, but it was exported without the "
+         "EXPORT_LINK_INTERFACE_LIBRARIES to export the old-style properties";
+    lg->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    return;
+  }
+
+  if (propContent->empty()) {
+    properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix].clear();
+    return;
+  }
+
+  std::string prepro =
+    cmGeneratorExpression::Preprocess(*propContent, preprocessRule);
+  if (!prepro.empty()) {
+    this->ResolveTargetsInGeneratorExpressions(prepro, target,
+                                               ReplaceFreeTargets);
+    properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro;
+  }
+}
+
+void cmExportCMakeConfigGenerator::GeneratePolicyHeaderCode(std::ostream& os)
+{
+  // Protect that file against use with older CMake versions.
+  /* clang-format off */
+  os << "# Generated by CMake\n\n";
+  os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.8)\n"
+     << "   message(FATAL_ERROR \"CMake >= "
+     << this->RequiredCMakeVersionMajor << '.'
+     << this->RequiredCMakeVersionMinor << '.'
+     << this->RequiredCMakeVersionPatch << " required\")\n"
+     << "endif()\n"
+     << "if(CMAKE_VERSION VERSION_LESS \""
+     << this->RequiredCMakeVersionMajor << '.'
+     << this->RequiredCMakeVersionMinor << '.'
+     << this->RequiredCMakeVersionPatch << "\")\n"
+     << "   message(FATAL_ERROR \"CMake >= "
+     << this->RequiredCMakeVersionMajor << '.'
+     << this->RequiredCMakeVersionMinor << '.'
+     << this->RequiredCMakeVersionPatch << " required\")\n"
+     << "endif()\n";
+  /* clang-format on */
+
+  // Isolate the file policy level.
+  // Support CMake versions as far back as the
+  // RequiredCMakeVersion{Major,Minor,Patch}, but also support using NEW
+  // policy settings for up to CMake 3.29 (this upper limit may be reviewed
+  // and increased from time to time). This reduces the opportunity for CMake
+  // warnings when an older export file is later used with newer CMake
+  // versions.
+  /* clang-format off */
+  os << "cmake_policy(PUSH)\n"
+     << "cmake_policy(VERSION "
+     << this->RequiredCMakeVersionMajor << '.'
+     << this->RequiredCMakeVersionMinor << '.'
+     << this->RequiredCMakeVersionPatch << "...3.29)\n";
+  /* clang-format on */
+}
+
+void cmExportCMakeConfigGenerator::GeneratePolicyFooterCode(std::ostream& os)
+{
+  os << "cmake_policy(POP)\n";
+}
+
+void cmExportCMakeConfigGenerator::GenerateImportHeaderCode(
+  std::ostream& os, std::string const& config)
+{
+  os << "#----------------------------------------------------------------\n"
+     << "# Generated CMake target import file";
+  if (!config.empty()) {
+    os << " for configuration \"" << config << "\".\n";
+  } else {
+    os << ".\n";
+  }
+  os << "#----------------------------------------------------------------\n"
+     << "\n";
+  this->GenerateImportVersionCode(os);
+}
+
+void cmExportCMakeConfigGenerator::GenerateImportFooterCode(std::ostream& os)
+{
+  os << "# Commands beyond this point should not need to know the version.\n"
+     << "set(CMAKE_IMPORT_FILE_VERSION)\n";
+}
+
+void cmExportCMakeConfigGenerator::GenerateImportVersionCode(std::ostream& os)
+{
+  // Store an import file format version.  This will let us change the
+  // format later while still allowing old import files to work.
+  /* clang-format off */
+  os << "# Commands may need to know the format version.\n"
+     << "set(CMAKE_IMPORT_FILE_VERSION 1)\n"
+     << "\n";
+  /* clang-format on */
+}
+
+void cmExportCMakeConfigGenerator::GenerateExpectedTargetsCode(
+  std::ostream& os, std::string const& expectedTargets)
+{
+  /* clang-format off */
+  os << "# Protect against multiple inclusion, which would fail when already "
+        "imported targets are added once more.\n"
+        "set(_cmake_targets_defined \"\")\n"
+        "set(_cmake_targets_not_defined \"\")\n"
+        "set(_cmake_expected_targets \"\")\n"
+        "foreach(_cmake_expected_target IN ITEMS " << expectedTargets << ")\n"
+        "  list(APPEND _cmake_expected_targets \"${_cmake_expected_target}\")\n"
+        "  if(TARGET \"${_cmake_expected_target}\")\n"
+        "    list(APPEND _cmake_targets_defined \"${_cmake_expected_target}\")\n"
+        "  else()\n"
+        "    list(APPEND _cmake_targets_not_defined \"${_cmake_expected_target}\")\n"
+        "  endif()\n"
+        "endforeach()\n"
+        "unset(_cmake_expected_target)\n"
+        "if(_cmake_targets_defined STREQUAL _cmake_expected_targets)\n"
+        "  unset(_cmake_targets_defined)\n"
+        "  unset(_cmake_targets_not_defined)\n"
+        "  unset(_cmake_expected_targets)\n"
+        "  unset(CMAKE_IMPORT_FILE_VERSION)\n"
+        "  cmake_policy(POP)\n"
+        "  return()\n"
+        "endif()\n"
+        "if(NOT _cmake_targets_defined STREQUAL \"\")\n"
+        "  string(REPLACE \";\" \", \" _cmake_targets_defined_text \"${_cmake_targets_defined}\")\n"
+        "  string(REPLACE \";\" \", \" _cmake_targets_not_defined_text \"${_cmake_targets_not_defined}\")\n"
+        "  message(FATAL_ERROR \"Some (but not all) targets in this export "
+        "set were already defined.\\nTargets Defined: ${_cmake_targets_defined_text}\\n"
+        "Targets not yet defined: ${_cmake_targets_not_defined_text}\\n\")\n"
+        "endif()\n"
+        "unset(_cmake_targets_defined)\n"
+        "unset(_cmake_targets_not_defined)\n"
+        "unset(_cmake_expected_targets)\n"
+        "\n\n";
+  /* clang-format on */
+}
+
+void cmExportCMakeConfigGenerator::GenerateImportTargetCode(
+  std::ostream& os, cmGeneratorTarget const* target,
+  cmStateEnums::TargetType targetType)
+{
+  // Construct the imported target name.
+  std::string targetName = this->Namespace;
+
+  targetName += target->GetExportName();
+
+  // Create the imported target.
+  os << "# Create imported target " << targetName << "\n";
+  switch (targetType) {
+    case cmStateEnums::EXECUTABLE:
+      os << "add_executable(" << targetName << " IMPORTED)\n";
+      break;
+    case cmStateEnums::STATIC_LIBRARY:
+      os << "add_library(" << targetName << " STATIC IMPORTED)\n";
+      break;
+    case cmStateEnums::SHARED_LIBRARY:
+      os << "add_library(" << targetName << " SHARED IMPORTED)\n";
+      break;
+    case cmStateEnums::MODULE_LIBRARY:
+      os << "add_library(" << targetName << " MODULE IMPORTED)\n";
+      break;
+    case cmStateEnums::UNKNOWN_LIBRARY:
+      os << "add_library(" << targetName << " UNKNOWN IMPORTED)\n";
+      break;
+    case cmStateEnums::OBJECT_LIBRARY:
+      os << "add_library(" << targetName << " OBJECT IMPORTED)\n";
+      break;
+    case cmStateEnums::INTERFACE_LIBRARY:
+      os << "add_library(" << targetName << " INTERFACE IMPORTED)\n";
+      break;
+    default: // should never happen
+      break;
+  }
+
+  // Mark the imported executable if it has exports.
+  if (target->IsExecutableWithExports() ||
+      (target->IsSharedLibraryWithExports() && target->HasImportLibrary(""))) {
+    os << "set_property(TARGET " << targetName
+       << " PROPERTY ENABLE_EXPORTS 1)\n";
+  }
+
+  // Mark the imported library if it is a framework.
+  if (target->IsFrameworkOnApple()) {
+    os << "set_property(TARGET " << targetName << " PROPERTY FRAMEWORK 1)\n";
+  }
+
+  // Mark the imported executable if it is an application bundle.
+  if (target->IsAppBundleOnApple()) {
+    os << "set_property(TARGET " << targetName
+       << " PROPERTY MACOSX_BUNDLE 1)\n";
+  }
+
+  if (target->IsCFBundleOnApple()) {
+    os << "set_property(TARGET " << targetName << " PROPERTY BUNDLE 1)\n";
+  }
+
+  // generate DEPRECATION
+  if (target->IsDeprecated()) {
+    os << "set_property(TARGET " << targetName << " PROPERTY DEPRECATION "
+       << cmExportFileGeneratorEscape(target->GetDeprecation()) << ")\n";
+  }
+
+  if (target->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) {
+    os << "set_property(TARGET " << targetName
+       << " PROPERTY IMPORTED_NO_SYSTEM 1)\n";
+  }
+
+  if (target->GetPropertyAsBool("EXPORT_NO_SYSTEM")) {
+    os << "set_property(TARGET " << targetName << " PROPERTY SYSTEM 0)\n";
+  }
+
+  os << "\n";
+}
+
+void cmExportCMakeConfigGenerator::GenerateImportPropertyCode(
+  std::ostream& os, std::string const& config, std::string const& suffix,
+  cmGeneratorTarget const* target, ImportPropertyMap const& properties,
+  std::string const& importedXcFrameworkLocation)
+{
+  // Construct the imported target name.
+  std::string targetName = this->Namespace;
+
+  targetName += target->GetExportName();
+
+  // Set the import properties.
+  os << "# Import target \"" << targetName << "\" for configuration \""
+     << config << "\"\n";
+  os << "set_property(TARGET " << targetName
+     << " APPEND PROPERTY IMPORTED_CONFIGURATIONS ";
+  if (!config.empty()) {
+    os << cmSystemTools::UpperCase(config);
+  } else {
+    os << "NOCONFIG";
+  }
+  os << ")\n";
+  os << "set_target_properties(" << targetName << " PROPERTIES\n";
+  std::string importedLocationProp = cmStrCat("IMPORTED_LOCATION", suffix);
+  for (auto const& property : properties) {
+    if (importedXcFrameworkLocation.empty() ||
+        property.first != importedLocationProp) {
+      os << "  " << property.first << " "
+         << cmExportFileGeneratorEscape(property.second) << "\n";
+    }
+  }
+  os << "  )\n";
+  if (!importedXcFrameworkLocation.empty()) {
+    auto importedLocationIt = properties.find(importedLocationProp);
+    if (importedLocationIt != properties.end()) {
+      os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.28\" AND IS_DIRECTORY "
+         << cmExportFileGeneratorEscape(importedXcFrameworkLocation)
+         << ")\n"
+            "  set_property(TARGET "
+         << targetName << " PROPERTY " << importedLocationProp << " "
+         << cmExportFileGeneratorEscape(importedXcFrameworkLocation)
+         << ")\nelse()\n  set_property(TARGET " << targetName << " PROPERTY "
+         << importedLocationProp << " "
+         << cmExportFileGeneratorEscape(importedLocationIt->second)
+         << ")\nendif()\n";
+    }
+  }
+  os << "\n";
+}
+
+void cmExportCMakeConfigGenerator::GenerateFindDependencyCalls(
+  std::ostream& os)
+{
+  os << "include(CMakeFindDependencyMacro)\n";
+  std::map<std::string, cmExportSet::PackageDependency> packageDependencies;
+  auto* exportSet = this->GetExportSet();
+  if (exportSet) {
+    packageDependencies = exportSet->GetPackageDependencies();
+  }
+
+  for (cmGeneratorTarget const* gt : this->ExternalTargets) {
+    std::string findPackageName;
+    auto exportFindPackageName = gt->GetProperty("EXPORT_FIND_PACKAGE_NAME");
+    cmFindPackageStack pkgStack = gt->Target->GetFindPackageStack();
+    if (!exportFindPackageName.IsEmpty()) {
+      findPackageName = *exportFindPackageName;
+    } else {
+      if (!pkgStack.Empty()) {
+        cmFindPackageCall const& fpc = pkgStack.Top();
+        findPackageName = fpc.Name;
+      }
+    }
+    if (!findPackageName.empty()) {
+      auto& dep = packageDependencies[findPackageName];
+      if (!pkgStack.Empty()) {
+        dep.FindPackageIndex = pkgStack.Top().Index;
+      }
+      if (dep.Enabled == cmExportSet::PackageDependencyExportEnabled::Auto) {
+        dep.Enabled = cmExportSet::PackageDependencyExportEnabled::On;
+      }
+    }
+  }
+
+  std::vector<std::pair<std::string, cmExportSet::PackageDependency>>
+    packageDependenciesSorted(packageDependencies.begin(),
+                              packageDependencies.end());
+  std::sort(
+    packageDependenciesSorted.begin(), packageDependenciesSorted.end(),
+    [](std::pair<std::string, cmExportSet::PackageDependency> const& lhs,
+       std::pair<std::string, cmExportSet::PackageDependency> const& rhs)
+      -> bool {
+      if (lhs.second.SpecifiedIndex) {
+        if (rhs.second.SpecifiedIndex) {
+          return lhs.second.SpecifiedIndex < rhs.second.SpecifiedIndex;
+        }
+        assert(rhs.second.FindPackageIndex);
+        return true;
+      }
+      assert(lhs.second.FindPackageIndex);
+      if (rhs.second.SpecifiedIndex) {
+        return false;
+      }
+      assert(rhs.second.FindPackageIndex);
+      return lhs.second.FindPackageIndex < rhs.second.FindPackageIndex;
+    });
+
+  for (auto const& it : packageDependenciesSorted) {
+    if (it.second.Enabled == cmExportSet::PackageDependencyExportEnabled::On) {
+      os << "find_dependency(" << it.first;
+      for (auto const& arg : it.second.ExtraArguments) {
+        os << " " << cmOutputConverter::EscapeForCMake(arg);
+      }
+      os << ")\n";
+    }
+  }
+  os << "\n\n";
+}
+
+void cmExportCMakeConfigGenerator::GenerateMissingTargetsCheckCode(
+  std::ostream& os)
+{
+  if (this->MissingTargets.empty()) {
+    /* clang-format off */
+    os << "# This file does not depend on other imported targets which have\n"
+          "# been exported from the same project but in a separate "
+            "export set.\n\n";
+    /* clang-format on */
+    return;
+  }
+  /* clang-format off */
+  os << "# Make sure the targets which have been exported in some other\n"
+        "# export set exist.\n"
+        "unset(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n"
+        "foreach(_target ";
+  /* clang-format on */
+  std::set<std::string> emitted;
+  for (std::string const& missingTarget : this->MissingTargets) {
+    if (emitted.insert(missingTarget).second) {
+      os << "\"" << missingTarget << "\" ";
+    }
+  }
+  /* clang-format off */
+  os << ")\n"
+        "  if(NOT TARGET \"${_target}\" )\n"
+        "    set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets \""
+        "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets} ${_target}\")"
+        "\n"
+        "  endif()\n"
+        "endforeach()\n"
+        "\n"
+        "if(DEFINED ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n"
+        "  if(CMAKE_FIND_PACKAGE_NAME)\n"
+        "    set( ${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)\n"
+        "    set( ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "
+        "\"The following imported targets are "
+        "referenced, but are missing: "
+                 "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets}\")\n"
+        "  else()\n"
+        "    message(FATAL_ERROR \"The following imported targets are "
+        "referenced, but are missing: "
+                "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets}\")\n"
+        "  endif()\n"
+        "endif()\n"
+        "unset(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n"
+        "\n";
+  /* clang-format on */
+}
+
+void cmExportCMakeConfigGenerator::GenerateImportedFileCheckLoop(
+  std::ostream& os)
+{
+  // Add code which verifies at cmake time that the file which is being
+  // imported actually exists on disk. This should in theory always be theory
+  // case, but still when packages are split into normal and development
+  // packages this might get broken (e.g. the Config.cmake could be part of
+  // the non-development package, something similar happened to me without
+  // on SUSE with a mysql pkg-config file, which claimed everything is fine,
+  // but the development package was not installed.).
+  /* clang-format off */
+  os << "# Loop over all imported files and verify that they actually exist\n"
+        "foreach(_cmake_target IN LISTS _cmake_import_check_targets)\n"
+        "  if(CMAKE_VERSION VERSION_LESS \"3.28\"\n"
+        "      OR NOT DEFINED "
+        "_cmake_import_check_xcframework_for_${_cmake_target}\n"
+        "      OR NOT IS_DIRECTORY "
+        "\"${_cmake_import_check_xcframework_for_${_cmake_target}}\")\n"
+        "    foreach(_cmake_file IN LISTS "
+        "\"_cmake_import_check_files_for_${_cmake_target}\")\n"
+        "      if(NOT EXISTS \"${_cmake_file}\")\n"
+        "        message(FATAL_ERROR \"The imported target "
+        "\\\"${_cmake_target}\\\" references the file\n"
+        "   \\\"${_cmake_file}\\\"\n"
+        "but this file does not exist.  Possible reasons include:\n"
+        "* The file was deleted, renamed, or moved to another location.\n"
+        "* An install or uninstall procedure did not complete successfully.\n"
+        "* The installation package was faulty and contained\n"
+        "   \\\"${CMAKE_CURRENT_LIST_FILE}\\\"\n"
+        "but not all the files it references.\n"
+        "\")\n"
+        "      endif()\n"
+        "    endforeach()\n"
+        "  endif()\n"
+        "  unset(_cmake_file)\n"
+        "  unset(\"_cmake_import_check_files_for_${_cmake_target}\")\n"
+        "endforeach()\n"
+        "unset(_cmake_target)\n"
+        "unset(_cmake_import_check_targets)\n"
+        "\n";
+  /* clang-format on */
+}
+
+void cmExportCMakeConfigGenerator::GenerateImportedFileChecksCode(
+  std::ostream& os, cmGeneratorTarget const* target,
+  ImportPropertyMap const& properties,
+  std::set<std::string> const& importedLocations,
+  std::string const& importedXcFrameworkLocation)
+{
+  // Construct the imported target name.
+  std::string targetName = cmStrCat(this->Namespace, target->GetExportName());
+
+  os << "list(APPEND _cmake_import_check_targets " << targetName << " )\n";
+  if (!importedXcFrameworkLocation.empty()) {
+    os << "set(_cmake_import_check_xcframework_for_" << targetName << ' '
+       << cmExportFileGeneratorEscape(importedXcFrameworkLocation) << ")\n";
+  }
+  os << "list(APPEND _cmake_import_check_files_for_" << targetName << " ";
+
+  for (std::string const& li : importedLocations) {
+    auto pi = properties.find(li);
+    if (pi != properties.end()) {
+      os << cmExportFileGeneratorEscape(pi->second) << " ";
+    }
+  }
+
+  os << ")\n\n";
+}
+
+void cmExportCMakeConfigGenerator::GenerateTargetFileSets(
+  cmGeneratorTarget* gte, std::ostream& os, cmTargetExport const* te)
+{
+  auto interfaceFileSets = gte->Target->GetAllInterfaceFileSets();
+  if (!interfaceFileSets.empty()) {
+    std::string targetName = cmStrCat(this->Namespace, gte->GetExportName());
+    os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.23.0\")\n"
+          "  target_sources("
+       << targetName << "\n";
+
+    for (auto const& name : interfaceFileSets) {
+      auto* fileSet = gte->Target->GetFileSet(name);
+      if (!fileSet) {
+        gte->Makefile->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("File set \"", name,
+                   "\" is listed in interface file sets of ", gte->GetName(),
+                   " but has not been created"));
+        return;
+      }
+
+      os << "    INTERFACE"
+         << "\n      FILE_SET " << cmOutputConverter::EscapeForCMake(name)
+         << "\n      TYPE "
+         << cmOutputConverter::EscapeForCMake(fileSet->GetType())
+         << "\n      BASE_DIRS "
+         << this->GetFileSetDirectories(gte, fileSet, te) << "\n      FILES "
+         << this->GetFileSetFiles(gte, fileSet, te) << "\n";
+    }
+
+    os << "  )\nelse()\n  set_property(TARGET " << targetName
+       << "\n    APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES";
+    for (auto const& name : interfaceFileSets) {
+      auto* fileSet = gte->Target->GetFileSet(name);
+      if (!fileSet) {
+        gte->Makefile->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("File set \"", name,
+                   "\" is listed in interface file sets of ", gte->GetName(),
+                   " but has not been created"));
+        return;
+      }
+
+      if (fileSet->GetType() == "HEADERS"_s) {
+        os << "\n      " << this->GetFileSetDirectories(gte, fileSet, te);
+      }
+    }
+    os << "\n  )\nendif()\n\n";
+  }
+}
+
+std::string cmExportCMakeConfigGenerator::GetCxxModuleFile(
+  std::string const& name) const
+{
+  auto const& cxxModuleDirname = this->GetCxxModulesDirectory();
+  if (cxxModuleDirname.empty()) {
+    return {};
+  }
+
+  return cmStrCat(cmSystemTools::GetFilenamePath(this->MainImportFile), '/',
+                  cxxModuleDirname, "/cxx-modules-", name, ".cmake");
+}
+
+void cmExportCMakeConfigGenerator::GenerateCxxModuleInformation(
+  std::string const& name, std::ostream& os)
+{
+  auto const cxx_module_dirname = this->GetCxxModulesDirectory();
+  if (cxx_module_dirname.empty()) {
+    return;
+  }
+
+  // Write the include.
+  os << "# Include C++ module properties\n"
+     << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << cxx_module_dirname
+     << "/cxx-modules-" << name << ".cmake\")\n\n";
+
+  // Include all configuration-specific include files.
+  cmGeneratedFileStream ap(this->GetCxxModuleFile(name), true);
+  ap.SetCopyIfDifferent(true);
+
+  this->GenerateCxxModuleConfigInformation(name, ap);
+}
+
+void cmExportCMakeConfigGenerator::SetRequiredCMakeVersion(unsigned int major,
+                                                           unsigned int minor,
+                                                           unsigned int patch)
+{
+  if (CMake_VERSION_ENCODE(major, minor, patch) >
+      CMake_VERSION_ENCODE(this->RequiredCMakeVersionMajor,
+                           this->RequiredCMakeVersionMinor,
+                           this->RequiredCMakeVersionPatch)) {
+    this->RequiredCMakeVersionMajor = major;
+    this->RequiredCMakeVersionMinor = minor;
+    this->RequiredCMakeVersionPatch = patch;
+  }
+}

+ 109 - 0
Source/cmExportCMakeConfigGenerator.h

@@ -0,0 +1,109 @@
+/* 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
+
+#include <iosfwd>
+#include <map>
+#include <set>
+#include <string>
+
+#include <cm/string_view>
+
+#include "cmExportFileGenerator.h"
+#include "cmGeneratorExpression.h"
+#include "cmStateTypes.h"
+
+class cmFileSet;
+class cmGeneratorTarget;
+class cmTargetExport;
+
+/** \class cmExportCMakeConfigGenerator
+ * \brief Generate CMake configuration files exporting targets from a build or
+ * install tree.
+ *
+ * cmExportCMakeConfigGenerator is the superclass for
+ * cmExportBuildCMakeConfigGenerator and cmExportInstallCMakeConfigGenerator.
+ * It contains common code generation routines for the two kinds of export
+ * implementations.
+ */
+class cmExportCMakeConfigGenerator : virtual public cmExportFileGenerator
+{
+public:
+  cmExportCMakeConfigGenerator();
+
+  void SetExportOld(bool exportOld) { this->ExportOld = exportOld; }
+
+  void SetExportPackageDependencies(bool exportPackageDependencies)
+  {
+    this->ExportPackageDependencies = exportPackageDependencies;
+  }
+
+  using cmExportFileGenerator::GenerateImportFile;
+
+protected:
+  using ImportPropertyMap = std::map<std::string, std::string>;
+
+  // Methods to implement export file code generation.
+  bool GenerateImportFile(std::ostream& os) override;
+  virtual void GeneratePolicyHeaderCode(std::ostream& os);
+  virtual void GeneratePolicyFooterCode(std::ostream& os);
+  virtual void GenerateImportHeaderCode(std::ostream& os,
+                                        std::string const& config = "");
+  virtual void GenerateImportFooterCode(std::ostream& os);
+  void GenerateImportVersionCode(std::ostream& os);
+  virtual void GenerateImportTargetCode(std::ostream& os,
+                                        cmGeneratorTarget const* target,
+                                        cmStateEnums::TargetType targetType);
+  virtual void GenerateImportPropertyCode(
+    std::ostream& os, std::string const& config, std::string const& suffix,
+    cmGeneratorTarget const* target, ImportPropertyMap const& properties,
+    std::string const& importedXcFrameworkLocation);
+  virtual void GenerateImportedFileChecksCode(
+    std::ostream& os, cmGeneratorTarget const* target,
+    ImportPropertyMap const& properties,
+    std::set<std::string> const& importedLocations,
+    std::string const& importedXcFrameworkLocation);
+  virtual void GenerateImportedFileCheckLoop(std::ostream& os);
+  virtual void GenerateMissingTargetsCheckCode(std::ostream& os);
+  virtual void GenerateFindDependencyCalls(std::ostream& os);
+
+  virtual void GenerateExpectedTargetsCode(std::ostream& os,
+                                           std::string const& expectedTargets);
+
+  cm::string_view GetImportPrefixWithSlash() const override;
+
+  virtual void GenerateInterfaceProperties(
+    cmGeneratorTarget const* target, std::ostream& os,
+    ImportPropertyMap const& properties);
+
+  void SetImportLinkInterface(
+    std::string const& config, std::string const& suffix,
+    cmGeneratorExpression::PreprocessContext preprocessRule,
+    cmGeneratorTarget const* target, ImportPropertyMap& properties);
+
+  void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os,
+                              cmTargetExport const* te = nullptr);
+
+  std::string GetCxxModuleFile(std::string const& name) const override;
+
+  void GenerateCxxModuleInformation(std::string const& name, std::ostream& os);
+
+  virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte,
+                                            cmFileSet* fileSet,
+                                            cmTargetExport const* te) = 0;
+  virtual std::string GetFileSetFiles(cmGeneratorTarget* gte,
+                                      cmFileSet* fileSet,
+                                      cmTargetExport const* te) = 0;
+
+  void SetRequiredCMakeVersion(unsigned int major, unsigned int minor,
+                               unsigned int patch);
+
+  bool ExportOld = false;
+  bool ExportPackageDependencies = false;
+
+  unsigned int RequiredCMakeVersionMajor = 2;
+  unsigned int RequiredCMakeVersionMinor = 8;
+  unsigned int RequiredCMakeVersionPatch = 3;
+};

+ 9 - 5
Source/cmExportCommand.cxx

@@ -19,6 +19,7 @@
 #include "cmExecutionStatus.h"
 #include "cmExperimental.h"
 #include "cmExportBuildAndroidMKGenerator.h"
+#include "cmExportBuildCMakeConfigGenerator.h"
 #include "cmExportBuildFileGenerator.h"
 #include "cmExportSet.h"
 #include "cmGeneratedFileStream.h"
@@ -318,21 +319,24 @@ bool cmExportCommand(std::vector<std::string> const& args,
   // Setup export file generation.
   std::unique_ptr<cmExportBuildFileGenerator> ebfg = nullptr;
   if (android) {
-    ebfg = cm::make_unique<cmExportBuildAndroidMKGenerator>();
+    auto ebag = cm::make_unique<cmExportBuildAndroidMKGenerator>();
+    ebag->SetAppendMode(arguments.Append);
+    ebfg = std::move(ebag);
   } else {
-    ebfg = cm::make_unique<cmExportBuildFileGenerator>();
+    auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>();
+    ebcg->SetAppendMode(arguments.Append);
+    ebcg->SetExportOld(arguments.ExportOld);
+    ebcg->SetExportPackageDependencies(arguments.ExportPackageDependencies);
+    ebfg = std::move(ebcg);
   }
   ebfg->SetExportFile(fname.c_str());
   ebfg->SetNamespace(arguments.Namespace);
   ebfg->SetCxxModuleDirectory(arguments.CxxModulesDirectory);
-  ebfg->SetAppendMode(arguments.Append);
   if (exportSet != nullptr) {
     ebfg->SetExportSet(exportSet);
   } else {
     ebfg->SetTargets(targets);
   }
-  ebfg->SetExportOld(arguments.ExportOld);
-  ebfg->SetExportPackageDependencies(arguments.ExportPackageDependencies);
 
   // Compute the set of configurations exported.
   std::vector<std::string> configurationTypes =

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 93 - 873
Source/cmExportFileGenerator.cxx


+ 76 - 128
Source/cmExportFileGenerator.h

@@ -10,16 +10,13 @@
 #include <string>
 #include <vector>
 
+#include <cm/string_view>
+
 #include "cmGeneratorExpression.h"
-#include "cmStateTypes.h"
-#include "cmVersion.h"
-#include "cmVersionConfig.h"
 
 class cmExportSet;
-class cmFileSet;
 class cmGeneratorTarget;
 class cmLocalGenerator;
-class cmTargetExport;
 
 #define STRINGIFY_HELPER(X) #X
 #define STRINGIFY(X) STRINGIFY_HELPER(X)
@@ -32,12 +29,9 @@ class cmTargetExport;
      : #major "." #minor ".0")
 
 /** \class cmExportFileGenerator
- * \brief Generate a file exporting targets from a build or install tree.
+ * \brief Generate files exporting targets from a build or install tree.
  *
- * cmExportFileGenerator is the superclass for
- * cmExportBuildFileGenerator and cmExportInstallFileGenerator.  It
- * contains common code generation routines for the two kinds of
- * export implementations.
+ * cmExportFileGenerator is the interface class for generating export files.
  */
 class cmExportFileGenerator
 {
@@ -46,65 +40,28 @@ public:
   virtual ~cmExportFileGenerator() = default;
 
   /** Set the full path to the export file to generate.  */
-  void SetExportFile(const char* mainFile);
-  const std::string& GetMainExportFileName() const;
+  void SetExportFile(char const* mainFile);
+  std::string const& GetMainExportFileName() const;
 
   /** Set the namespace in which to place exported target names.  */
-  void SetNamespace(const std::string& ns) { this->Namespace = ns; }
+  void SetNamespace(std::string const& ns) { this->Namespace = ns; }
   std::string GetNamespace() const { return this->Namespace; }
 
-  void SetExportOld(bool exportOld) { this->ExportOld = exportOld; }
-
   /** Add a configuration to be exported.  */
-  void AddConfiguration(const std::string& config);
+  void AddConfiguration(std::string const& config);
 
-  /** Actually generate the export file.  Returns whether there was an
-      error.  */
+  /** Create and actually generate the export file.  Returns whether there was
+      an error.  */
   bool GenerateImportFile();
 
-  void SetExportPackageDependencies(bool exportPackageDependencies)
-  {
-    this->ExportPackageDependencies = exportPackageDependencies;
-  }
-
 protected:
   using ImportPropertyMap = std::map<std::string, std::string>;
 
-  // Generate per-configuration target information to the given output
-  // stream.
-  void GenerateImportConfig(std::ostream& os, const std::string& config);
-
-  // Methods to implement export file code generation.
-  virtual void GeneratePolicyHeaderCode(std::ostream& os);
-  virtual void GeneratePolicyFooterCode(std::ostream& os);
-  virtual void GenerateImportHeaderCode(std::ostream& os,
-                                        const std::string& config = "");
-  virtual void GenerateImportFooterCode(std::ostream& os);
-  void GenerateImportVersionCode(std::ostream& os);
-  virtual void GenerateImportTargetCode(std::ostream& os,
-                                        cmGeneratorTarget const* target,
-                                        cmStateEnums::TargetType targetType);
-  virtual void GenerateImportPropertyCode(
-    std::ostream& os, const std::string& config, const std::string& suffix,
-    cmGeneratorTarget const* target, ImportPropertyMap const& properties,
-    const std::string& importedXcFrameworkLocation);
-  virtual void GenerateImportedFileChecksCode(
-    std::ostream& os, cmGeneratorTarget* target,
-    ImportPropertyMap const& properties,
-    const std::set<std::string>& importedLocations,
-    const std::string& importedXcFrameworkLocation);
-  virtual void GenerateImportedFileCheckLoop(std::ostream& os);
-  virtual void GenerateMissingTargetsCheckCode(std::ostream& os);
-  virtual void GenerateFindDependencyCalls(std::ostream& os);
-
-  virtual void GenerateExpectedTargetsCode(std::ostream& os,
-                                           const std::string& expectedTargets);
-
   // Collect properties with detailed information about targets beyond
   // their location on disk.
-  void SetImportDetailProperties(const std::string& config,
+  void SetImportDetailProperties(std::string const& config,
                                  std::string const& suffix,
-                                 cmGeneratorTarget* target,
+                                 cmGeneratorTarget const* target,
                                  ImportPropertyMap& properties);
 
   enum class ImportLinkPropertyTargetNames
@@ -115,17 +72,25 @@ protected:
   template <typename T>
   void SetImportLinkProperty(std::string const& suffix,
                              cmGeneratorTarget const* target,
-                             const std::string& propName,
+                             std::string const& propName,
                              std::vector<T> const& entries,
                              ImportPropertyMap& properties,
                              ImportLinkPropertyTargetNames targetNames);
 
+  /** Generate the export file to the given output stream.  Returns whether
+      there was an error.  */
+  virtual bool GenerateImportFile(std::ostream& os) = 0;
+
   /** Each subclass knows how to generate its kind of export file.  */
   virtual bool GenerateMainFile(std::ostream& os) = 0;
 
+  /** Generate per-configuration target information to the given output
+      stream.  */
+  void GenerateImportConfig(std::ostream& os, std::string const& config);
+
   /** Each subclass knows where the target files are located.  */
   virtual void GenerateImportTargetsConfig(std::ostream& os,
-                                           const std::string& config,
+                                           std::string const& config,
                                            std::string const& suffix) = 0;
 
   /** Each subclass knows how to deal with a target that is  missing from an
@@ -133,47 +98,33 @@ protected:
   virtual void HandleMissingTarget(std::string& link_libs,
                                    cmGeneratorTarget const* depender,
                                    cmGeneratorTarget* dependee) = 0;
-  void PopulateInterfaceProperty(const std::string&,
+
+  /** Complain when a duplicate target is encountered.  */
+  virtual void ComplainAboutDuplicateTarget(
+    std::string const& targetName) const = 0;
+
+  virtual cm::string_view GetImportPrefixWithSlash() const = 0;
+
+  void AddImportPrefix(std::string& exportDirs) const;
+
+  void PopulateInterfaceProperty(std::string const& propName,
+                                 cmGeneratorTarget const* target,
+                                 ImportPropertyMap& properties) const;
+  void PopulateInterfaceProperty(std::string const& propName,
                                  cmGeneratorTarget const* target,
                                  cmGeneratorExpression::PreprocessContext,
                                  ImportPropertyMap& properties);
   bool PopulateInterfaceLinkLibrariesProperty(
     cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext,
     ImportPropertyMap& properties);
-  void PopulateInterfaceProperty(const std::string& propName,
-                                 cmGeneratorTarget const* target,
-                                 ImportPropertyMap& properties);
-  void PopulateCompatibleInterfaceProperties(cmGeneratorTarget const* target,
-                                             ImportPropertyMap& properties);
-  void PopulateCustomTransitiveInterfaceProperties(
-    cmGeneratorTarget const* target,
-    cmGeneratorExpression::PreprocessContext preprocessRule,
-    ImportPropertyMap& properties);
-  virtual void GenerateInterfaceProperties(
-    cmGeneratorTarget const* target, std::ostream& os,
-    const ImportPropertyMap& properties);
-  void PopulateIncludeDirectoriesInterface(
-    cmGeneratorTarget const* target,
-    cmGeneratorExpression::PreprocessContext preprocessRule,
-    ImportPropertyMap& properties, cmTargetExport const& te,
-    std::string& includesDestinationDirs);
-  void PopulateSourcesInterface(
-    cmGeneratorTarget const* target,
-    cmGeneratorExpression::PreprocessContext preprocessRule,
-    ImportPropertyMap& properties);
-  void PopulateLinkDirectoriesInterface(
-    cmGeneratorTarget const* target,
-    cmGeneratorExpression::PreprocessContext preprocessRule,
-    ImportPropertyMap& properties);
-  void PopulateLinkDependsInterface(
+
+  bool PopulateInterfaceProperties(
     cmGeneratorTarget const* target,
+    std::string const& includesDestinationDirs,
     cmGeneratorExpression::PreprocessContext preprocessRule,
     ImportPropertyMap& properties);
 
-  void SetImportLinkInterface(
-    const std::string& config, std::string const& suffix,
-    cmGeneratorExpression::PreprocessContext preprocessRule,
-    cmGeneratorTarget const* target, ImportPropertyMap& properties);
+  virtual void ReportError(std::string const& errorMessage) const = 0;
 
   enum FreeTargetsReplace
   {
@@ -185,36 +136,26 @@ protected:
     std::string& input, cmGeneratorTarget const* target,
     FreeTargetsReplace replace = NoReplaceFreeTargets);
 
-  bool PopulateCxxModuleExportProperties(
-    cmGeneratorTarget const* gte, ImportPropertyMap& properties,
-    cmGeneratorExpression::PreprocessContext ctx,
-    std::string const& includesDestinationDirs, std::string& errorMessage);
-  bool PopulateExportProperties(cmGeneratorTarget const* gte,
-                                ImportPropertyMap& properties,
-                                std::string& errorMessage);
+  virtual cmExportSet* GetExportSet() const { return nullptr; }
 
-  void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os,
-                              cmTargetExport* te = nullptr);
+  virtual void ReplaceInstallPrefix(std::string& input) const;
 
-  void GenerateCxxModuleInformation(std::string const& name, std::ostream& os);
+  virtual std::string InstallNameDir(cmGeneratorTarget const* target,
+                                     std::string const& config) = 0;
 
-  virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte,
-                                            cmFileSet* fileSet,
-                                            cmTargetExport* te) = 0;
-  virtual std::string GetFileSetFiles(cmGeneratorTarget* gte,
-                                      cmFileSet* fileSet,
-                                      cmTargetExport* te) = 0;
+  /** Get the temporary location of the config-agnostic C++ module file.  */
+  virtual std::string GetCxxModuleFile(std::string const& name) const = 0;
 
-  virtual cmExportSet* GetExportSet() const { return nullptr; }
+  virtual std::string GetCxxModulesDirectory() const = 0;
+  virtual void GenerateCxxModuleConfigInformation(std::string const&,
+                                                  std::ostream& os) const = 0;
 
-  void SetRequiredCMakeVersion(unsigned int major, unsigned int minor,
-                               unsigned int patch);
+  bool AddTargetNamespace(std::string& input, cmGeneratorTarget const* target,
+                          cmLocalGenerator const* lg);
 
   // The namespace in which the exports are placed in the generated file.
   std::string Namespace;
 
-  bool ExportOld;
-
   // The set of configurations to export.
   std::vector<std::string> Configurations;
 
@@ -223,40 +164,47 @@ protected:
   std::string FileDir;
   std::string FileBase;
   std::string FileExt;
-  bool AppendMode;
+  bool AppendMode = false;
 
   // The set of targets included in the export.
-  std::set<cmGeneratorTarget*> ExportedTargets;
+  std::set<cmGeneratorTarget const*> ExportedTargets;
 
   std::vector<std::string> MissingTargets;
 
   std::set<cmGeneratorTarget const*> ExternalTargets;
 
-  unsigned int RequiredCMakeVersionMajor = 2;
-  unsigned int RequiredCMakeVersionMinor = 8;
-  unsigned int RequiredCMakeVersionPatch = 3;
-
-  bool ExportPackageDependencies = false;
-
 private:
-  void PopulateInterfaceProperty(const std::string&, const std::string&,
+  void PopulateInterfaceProperty(std::string const& propName,
+                                 std::string const& outputName,
                                  cmGeneratorTarget const* target,
                                  cmGeneratorExpression::PreprocessContext,
                                  ImportPropertyMap& properties);
 
-  bool AddTargetNamespace(std::string& input, cmGeneratorTarget const* target,
-                          cmLocalGenerator const* lg);
+  void PopulateCompatibleInterfaceProperties(
+    cmGeneratorTarget const* target, ImportPropertyMap& properties) const;
+  void PopulateCustomTransitiveInterfaceProperties(
+    cmGeneratorTarget const* target,
+    cmGeneratorExpression::PreprocessContext preprocessRule,
+    ImportPropertyMap& properties);
+  bool PopulateCxxModuleExportProperties(
+    cmGeneratorTarget const* gte, ImportPropertyMap& properties,
+    cmGeneratorExpression::PreprocessContext ctx,
+    std::string const& includesDestinationDirs, std::string& errorMessage);
+  bool PopulateExportProperties(cmGeneratorTarget const* gte,
+                                ImportPropertyMap& properties,
+                                std::string& errorMessage) const;
 
   void ResolveTargetsInGeneratorExpression(std::string& input,
                                            cmGeneratorTarget const* target,
                                            cmLocalGenerator const* lg);
+};
 
-  virtual void ReplaceInstallPrefix(std::string& input);
-
-  virtual std::string InstallNameDir(cmGeneratorTarget const* target,
-                                     const std::string& config) = 0;
+extern template void cmExportFileGenerator::SetImportLinkProperty<std::string>(
+  std::string const&, cmGeneratorTarget const*, std::string const&,
+  std::vector<std::string> const&, ImportPropertyMap& properties,
+  ImportLinkPropertyTargetNames);
 
-  virtual std::string GetCxxModulesDirectory() const = 0;
-  virtual void GenerateCxxModuleConfigInformation(std::string const&,
-                                                  std::ostream& os) const = 0;
-};
+extern template void cmExportFileGenerator::SetImportLinkProperty<cmLinkItem>(
+  std::string const&, cmGeneratorTarget const*, std::string const&,
+  std::vector<cmLinkItem> const&, ImportPropertyMap& properties,
+  ImportLinkPropertyTargetNames);

+ 51 - 65
Source/cmExportInstallAndroidMKGenerator.cxx

@@ -4,14 +4,15 @@
 
 #include <cstddef>
 #include <memory>
-#include <ostream>
+#include <sstream>
 #include <vector>
 
-#include "cmExportBuildAndroidMKGenerator.h"
 #include "cmExportSet.h"
+#include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmInstallExportGenerator.h"
 #include "cmInstallTargetGenerator.h"
+#include "cmPolicies.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
@@ -24,8 +25,55 @@ cmExportInstallAndroidMKGenerator::cmExportInstallAndroidMKGenerator(
 {
 }
 
+void cmExportInstallAndroidMKGenerator::ReportDuplicateTarget(
+  std::string const& targetName) const
+{
+  std::ostringstream e;
+  e << "install(EXPORT_ANDROID_MK \"" << this->GetExportSet()->GetName()
+    << "\" ...) "
+    << "includes target \"" << targetName
+    << "\" more than once in the export set.";
+  this->ReportError(e.str());
+}
+
+bool cmExportInstallAndroidMKGenerator::GenerateMainFile(std::ostream& os)
+{
+  std::vector<cmTargetExport const*> allTargets;
+  {
+    auto visitor = [&](cmTargetExport const* te) { allTargets.push_back(te); };
+
+    if (!this->CollectExports(visitor)) {
+      return false;
+    }
+  }
+
+  // Create all the imported targets.
+  for (cmTargetExport const* te : allTargets) {
+    cmGeneratorTarget const* gt = te->Target;
+
+    this->GenerateImportTargetCode(os, gt, this->GetExportTargetType(te));
+
+    ImportPropertyMap properties;
+    if (!this->PopulateInterfaceProperties(te, properties)) {
+      return false;
+    }
+
+    bool const newCMP0022Behavior =
+      gt->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
+      gt->GetPolicyStatusCMP0022() != cmPolicies::OLD;
+    if (newCMP0022Behavior) {
+      this->PopulateInterfaceLinkLibrariesProperty(
+        gt, cmGeneratorExpression::InstallInterface, properties);
+    }
+
+    this->GenerateInterfaceProperties(gt, os, properties);
+  }
+
+  return true;
+}
+
 void cmExportInstallAndroidMKGenerator::GenerateImportHeaderCode(
-  std::ostream& os, const std::string&)
+  std::ostream& os, std::string const&)
 {
   std::string installDir = this->IEGen->GetDestination();
   os << "LOCAL_PATH := $(call my-dir)\n";
@@ -53,10 +101,6 @@ void cmExportInstallAndroidMKGenerator::GenerateImportHeaderCode(
   }
 }
 
-void cmExportInstallAndroidMKGenerator::GenerateImportFooterCode(std::ostream&)
-{
-}
-
 void cmExportInstallAndroidMKGenerator::GenerateImportTargetCode(
   std::ostream& os, cmGeneratorTarget const* target,
   cmStateEnums::TargetType /*targetType*/)
@@ -73,61 +117,3 @@ void cmExportInstallAndroidMKGenerator::GenerateImportTargetCode(
   }
   os << target->GetFullName(config) << "\n";
 }
-
-void cmExportInstallAndroidMKGenerator::GenerateExpectedTargetsCode(
-  std::ostream&, const std::string&)
-{
-}
-
-void cmExportInstallAndroidMKGenerator::GenerateImportPropertyCode(
-  std::ostream&, const std::string&, const std::string&,
-  cmGeneratorTarget const*, ImportPropertyMap const&, const std::string&)
-{
-}
-
-void cmExportInstallAndroidMKGenerator::GenerateMissingTargetsCheckCode(
-  std::ostream&)
-{
-}
-
-void cmExportInstallAndroidMKGenerator::GenerateInterfaceProperties(
-  cmGeneratorTarget const* target, std::ostream& os,
-  const ImportPropertyMap& properties)
-{
-  std::string config;
-  if (!this->Configurations.empty()) {
-    config = this->Configurations[0];
-  }
-  cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties(
-    target, os, properties, cmExportBuildAndroidMKGenerator::INSTALL, config);
-}
-
-void cmExportInstallAndroidMKGenerator::LoadConfigFiles(std::ostream&)
-{
-}
-
-void cmExportInstallAndroidMKGenerator::GenerateImportPrefix(std::ostream&)
-{
-}
-
-void cmExportInstallAndroidMKGenerator::CleanupTemporaryVariables(
-  std::ostream&)
-{
-}
-
-void cmExportInstallAndroidMKGenerator::GenerateImportedFileCheckLoop(
-  std::ostream&)
-{
-}
-
-void cmExportInstallAndroidMKGenerator::GenerateImportedFileChecksCode(
-  std::ostream&, cmGeneratorTarget*, ImportPropertyMap const&,
-  const std::set<std::string>&, const std::string&)
-{
-}
-
-bool cmExportInstallAndroidMKGenerator::GenerateImportFileConfig(
-  const std::string&)
-{
-  return true;
-}

+ 30 - 32
Source/cmExportInstallAndroidMKGenerator.h

@@ -5,9 +5,10 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <iosfwd>
-#include <set>
 #include <string>
+#include <vector>
 
+#include "cmExportAndroidMKGenerator.h"
 #include "cmExportInstallFileGenerator.h"
 #include "cmStateTypes.h"
 
@@ -15,52 +16,49 @@ class cmGeneratorTarget;
 class cmInstallExportGenerator;
 
 /** \class cmExportInstallAndroidMKGenerator
- * \brief Generate a file exporting targets from an install tree.
+ * \brief Generate files exporting targets from an install tree.
  *
  * cmExportInstallAndroidMKGenerator generates files exporting targets from
- * install an installation tree.  The files are placed in a temporary
- * location for installation by cmInstallExportGenerator.  The file format
- * is for the ndk build system and is a makefile fragment specifying prebuilt
- * libraries to the ndk build system.
+ * an installation tree.  The files are placed in a temporary location for
+ * installation by cmInstallExportGenerator.  The file format is for the ndk
+ * build system and is a makefile fragment specifying prebuilt libraries to the
+ * ndk build system.
  *
  * This is used to implement the INSTALL(EXPORT_ANDROID_MK) command.
  */
-class cmExportInstallAndroidMKGenerator : public cmExportInstallFileGenerator
+class cmExportInstallAndroidMKGenerator
+  : public cmExportAndroidMKGenerator
+  , public cmExportInstallFileGenerator
 {
 public:
   /** Construct with the export installer that will install the
       files.  */
   cmExportInstallAndroidMKGenerator(cmInstallExportGenerator* iegen);
 
+  std::string GetConfigImportFileGlob() const override { return {}; }
+
 protected:
+  GenerateType GetGenerateType() const override { return INSTALL; }
+
   // Implement virtual methods from the superclass.
-  void GeneratePolicyHeaderCode(std::ostream&) override {}
-  void GeneratePolicyFooterCode(std::ostream&) override {}
+  void ReportDuplicateTarget(std::string const& targetName) const;
+  bool GenerateMainFile(std::ostream& os) override;
   void GenerateImportHeaderCode(std::ostream& os,
-                                const std::string& config = "") override;
-  void GenerateImportFooterCode(std::ostream& os) override;
+                                std::string const& config = "") override;
   void GenerateImportTargetCode(
     std::ostream& os, cmGeneratorTarget const* target,
     cmStateEnums::TargetType /*targetType*/) override;
-  void GenerateExpectedTargetsCode(
-    std::ostream& os, const std::string& expectedTargets) override;
-  void GenerateImportPropertyCode(
-    std::ostream& os, const std::string& config, const std::string& suffix,
-    cmGeneratorTarget const* target, ImportPropertyMap const& properties,
-    const std::string& importedXcFrameworkLocation) override;
-  void GenerateMissingTargetsCheckCode(std::ostream& os) override;
-  void GenerateFindDependencyCalls(std::ostream&) override {}
-  void GenerateInterfaceProperties(
-    cmGeneratorTarget const* target, std::ostream& os,
-    const ImportPropertyMap& properties) override;
-  void GenerateImportPrefix(std::ostream& os) override;
-  void LoadConfigFiles(std::ostream&) override;
-  void CleanupTemporaryVariables(std::ostream&) override;
-  void GenerateImportedFileCheckLoop(std::ostream& os) override;
-  void GenerateImportedFileChecksCode(
-    std::ostream& os, cmGeneratorTarget* target,
-    ImportPropertyMap const& properties,
-    const std::set<std::string>& importedLocations,
-    const std::string& importedXcFrameworkLocation) override;
-  bool GenerateImportFileConfig(const std::string& config) override;
+
+  void ComplainAboutMissingTarget(cmGeneratorTarget const* depender,
+                                  cmGeneratorTarget const* dependee,
+                                  std::vector<std::string> const& namespaces);
+
+  void GenerateImportTargetsConfig(std::ostream& os, std::string const& config,
+                                   std::string const& suffix) override
+  {
+    this->cmExportAndroidMKGenerator::GenerateImportTargetsConfig(os, config,
+                                                                  suffix);
+  }
+
+  std::string GetCxxModulesDirectory() const override { return {}; }
 };

+ 507 - 0
Source/cmExportInstallCMakeConfigGenerator.cxx

@@ -0,0 +1,507 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmExportInstallCMakeConfigGenerator.h"
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+#include <cm/string_view>
+#include <cmext/string_view>
+
+#include "cmExportSet.h"
+#include "cmFileSet.h"
+#include "cmGeneratedFileStream.h"
+#include "cmGeneratorExpression.h"
+#include "cmGeneratorTarget.h"
+#include "cmInstallExportGenerator.h"
+#include "cmInstallFileSetGenerator.h"
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmOutputConverter.h"
+#include "cmPolicies.h"
+#include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmTargetExport.h"
+#include "cmValue.h"
+
+cmExportInstallCMakeConfigGenerator::cmExportInstallCMakeConfigGenerator(
+  cmInstallExportGenerator* iegen)
+  : cmExportInstallFileGenerator(iegen)
+{
+}
+
+std::string cmExportInstallCMakeConfigGenerator::GetConfigImportFileGlob()
+  const
+{
+  std::string glob = cmStrCat(this->FileBase, "-*", this->FileExt);
+  return glob;
+}
+
+bool cmExportInstallCMakeConfigGenerator::GenerateMainFile(std::ostream& os)
+{
+  std::vector<cmTargetExport const*> allTargets;
+  {
+    std::string expectedTargets;
+    std::string sep;
+    auto visitor = [&](cmTargetExport const* te) {
+      allTargets.push_back(te);
+      expectedTargets += sep + this->Namespace + te->Target->GetExportName();
+      sep = " ";
+    };
+
+    if (!this->CollectExports(visitor)) {
+      return false;
+    }
+
+    this->GenerateExpectedTargetsCode(os, expectedTargets);
+  }
+
+  // Compute the relative import prefix for the file
+  this->GenerateImportPrefix(os);
+
+  bool requiresConfigFiles = false;
+  // Create all the imported targets.
+  for (cmTargetExport const* te : allTargets) {
+    cmGeneratorTarget* gt = te->Target;
+    cmStateEnums::TargetType targetType = this->GetExportTargetType(te);
+
+    requiresConfigFiles =
+      requiresConfigFiles || targetType != cmStateEnums::INTERFACE_LIBRARY;
+
+    this->GenerateImportTargetCode(os, gt, targetType);
+
+    ImportPropertyMap properties;
+    if (!this->PopulateInterfaceProperties(te, properties)) {
+      return false;
+    }
+
+    bool const newCMP0022Behavior =
+      gt->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
+      gt->GetPolicyStatusCMP0022() != cmPolicies::OLD;
+    if (newCMP0022Behavior) {
+      if (this->PopulateInterfaceLinkLibrariesProperty(
+            gt, cmGeneratorExpression::InstallInterface, properties) &&
+          !this->ExportOld) {
+        this->SetRequiredCMakeVersion(2, 8, 12);
+      }
+    }
+    if (targetType == cmStateEnums::INTERFACE_LIBRARY) {
+      this->SetRequiredCMakeVersion(3, 0, 0);
+    }
+    if (gt->GetProperty("INTERFACE_SOURCES")) {
+      // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1
+      // can consume them.
+      this->SetRequiredCMakeVersion(3, 1, 0);
+    }
+
+    this->GenerateInterfaceProperties(gt, os, properties);
+
+    this->GenerateTargetFileSets(gt, os, te);
+  }
+
+  this->LoadConfigFiles(os);
+
+  bool result = true;
+
+  std::string cxx_modules_name = this->GetExportSet()->GetName();
+  this->GenerateCxxModuleInformation(cxx_modules_name, os);
+  if (requiresConfigFiles) {
+    for (std::string const& c : this->Configurations) {
+      if (!this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name,
+                                                              c)) {
+        result = false;
+      }
+    }
+  }
+
+  this->CleanupTemporaryVariables(os);
+  this->GenerateImportedFileCheckLoop(os);
+
+  // Generate an import file for each configuration.
+  // Don't do this if we only export INTERFACE_LIBRARY targets.
+  if (requiresConfigFiles) {
+    for (std::string const& c : this->Configurations) {
+      if (!this->GenerateImportFileConfig(c)) {
+        result = false;
+      }
+    }
+  }
+
+  this->GenerateMissingTargetsCheckCode(os);
+
+  return result;
+}
+
+void cmExportInstallCMakeConfigGenerator::GenerateImportPrefix(
+  std::ostream& os)
+{
+  // Set an _IMPORT_PREFIX variable for import location properties
+  // to reference if they are relative to the install prefix.
+  std::string installPrefix =
+    this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition(
+      "CMAKE_INSTALL_PREFIX");
+  std::string const& expDest = this->IEGen->GetDestination();
+  if (cmSystemTools::FileIsFullPath(expDest)) {
+    // The export file is being installed to an absolute path so the
+    // package is not relocatable.  Use the configured install prefix.
+    /* clang-format off */
+    os <<
+      "# The installation prefix configured by this project.\n"
+      "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n"
+      "\n";
+    /* clang-format on */
+  } else {
+    // Add code to compute the installation prefix relative to the
+    // import file location.
+    std::string absDest = installPrefix + "/" + expDest;
+    std::string absDestS = absDest + "/";
+    os << "# Compute the installation prefix relative to this file.\n"
+       << "get_filename_component(_IMPORT_PREFIX"
+       << " \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n";
+    if (cmHasLiteralPrefix(absDestS, "/lib/") ||
+        cmHasLiteralPrefix(absDestS, "/lib64/") ||
+        cmHasLiteralPrefix(absDestS, "/libx32/") ||
+        cmHasLiteralPrefix(absDestS, "/usr/lib/") ||
+        cmHasLiteralPrefix(absDestS, "/usr/lib64/") ||
+        cmHasLiteralPrefix(absDestS, "/usr/libx32/")) {
+      // Handle "/usr move" symlinks created by some Linux distros.
+      /* clang-format off */
+      os <<
+        "# Use original install prefix when loaded through a\n"
+        "# cross-prefix symbolic link such as /lib -> /usr/lib.\n"
+        "get_filename_component(_realCurr \"${_IMPORT_PREFIX}\" REALPATH)\n"
+        "get_filename_component(_realOrig \"" << absDest << "\" REALPATH)\n"
+        "if(_realCurr STREQUAL _realOrig)\n"
+        "  set(_IMPORT_PREFIX \"" << absDest << "\")\n"
+        "endif()\n"
+        "unset(_realOrig)\n"
+        "unset(_realCurr)\n";
+      /* clang-format on */
+    }
+    std::string dest = expDest;
+    while (!dest.empty()) {
+      os << "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" "
+            "PATH)\n";
+      dest = cmSystemTools::GetFilenamePath(dest);
+    }
+    os << "if(_IMPORT_PREFIX STREQUAL \"/\")\n"
+       << "  set(_IMPORT_PREFIX \"\")\n"
+       << "endif()\n"
+       << "\n";
+  }
+}
+
+void cmExportInstallCMakeConfigGenerator::CleanupTemporaryVariables(
+  std::ostream& os)
+{
+  /* clang-format off */
+  os << "# Cleanup temporary variables.\n"
+     << "set(_IMPORT_PREFIX)\n"
+     << "\n";
+  /* clang-format on */
+}
+
+void cmExportInstallCMakeConfigGenerator::LoadConfigFiles(std::ostream& os)
+{
+  // Now load per-configuration properties for them.
+  /* clang-format off */
+  os << "# Load information for each installed configuration.\n"
+     << "file(GLOB _cmake_config_files \"${CMAKE_CURRENT_LIST_DIR}/"
+     << this->GetConfigImportFileGlob() << "\")\n"
+     << "foreach(_cmake_config_file IN LISTS _cmake_config_files)\n"
+     << "  include(\"${_cmake_config_file}\")\n"
+     << "endforeach()\n"
+     << "unset(_cmake_config_file)\n"
+     << "unset(_cmake_config_files)\n"
+     << "\n";
+  /* clang-format on */
+}
+
+bool cmExportInstallCMakeConfigGenerator::GenerateImportFileConfig(
+  std::string const& config)
+{
+  // Skip configurations not enabled for this export.
+  if (!this->IEGen->InstallsForConfig(config)) {
+    return true;
+  }
+
+  // Construct the name of the file to generate.
+  std::string fileName = cmStrCat(this->FileDir, '/', this->FileBase, '-');
+  if (!config.empty()) {
+    fileName += cmSystemTools::LowerCase(config);
+  } else {
+    fileName += "noconfig";
+  }
+  fileName += this->FileExt;
+
+  // Open the output file to generate it.
+  cmGeneratedFileStream exportFileStream(fileName, true);
+  if (!exportFileStream) {
+    std::string se = cmSystemTools::GetLastSystemError();
+    std::ostringstream e;
+    e << "cannot write to file \"" << fileName << "\": " << se;
+    cmSystemTools::Error(e.str());
+    return false;
+  }
+  exportFileStream.SetCopyIfDifferent(true);
+  std::ostream& os = exportFileStream;
+
+  // Start with the import file header.
+  this->GenerateImportHeaderCode(os, config);
+
+  // Generate the per-config target information.
+  this->GenerateImportConfig(os, config);
+
+  // End with the import file footer.
+  this->GenerateImportFooterCode(os);
+
+  // Record this per-config import file.
+  this->ConfigImportFiles[config] = fileName;
+
+  return true;
+}
+
+void cmExportInstallCMakeConfigGenerator::GenerateImportTargetsConfig(
+  std::ostream& os, std::string const& config, std::string const& suffix)
+{
+  // Add each target in the set to the export.
+  for (std::unique_ptr<cmTargetExport> const& te :
+       this->GetExportSet()->GetTargetExports()) {
+    // Collect import properties for this target.
+    if (this->GetExportTargetType(te.get()) ==
+        cmStateEnums::INTERFACE_LIBRARY) {
+      continue;
+    }
+
+    ImportPropertyMap properties;
+    std::set<std::string> importedLocations;
+
+    this->PopulateImportProperties(config, suffix, te.get(), properties,
+                                   importedLocations);
+
+    // If any file location was set for the target add it to the
+    // import file.
+    if (!properties.empty()) {
+      cmGeneratorTarget const* const gtgt = te->Target;
+      std::string const importedXcFrameworkLocation =
+        this->GetImportXcFrameworkLocation(config, te.get());
+
+      this->SetImportLinkInterface(config, suffix,
+                                   cmGeneratorExpression::InstallInterface,
+                                   gtgt, properties);
+
+      this->GenerateImportPropertyCode(os, config, suffix, gtgt, properties,
+                                       importedXcFrameworkLocation);
+      this->GenerateImportedFileChecksCode(
+        os, gtgt, properties, importedLocations, importedXcFrameworkLocation);
+    }
+  }
+}
+
+namespace {
+bool EntryIsContextSensitive(
+  std::unique_ptr<cmCompiledGeneratorExpression> const& cge)
+{
+  return cge->GetHadContextSensitiveCondition();
+}
+}
+
+std::string cmExportInstallCMakeConfigGenerator::GetFileSetDirectories(
+  cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* te)
+{
+  std::vector<std::string> resultVector;
+
+  auto configs =
+    gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
+
+  cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
+  auto cge = ge.Parse(te->FileSetGenerators.at(fileSet)->GetDestination());
+
+  for (auto const& config : configs) {
+    auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte);
+    auto dest = cmOutputConverter::EscapeForCMake(
+      unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
+    if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
+      dest = cmStrCat("${_IMPORT_PREFIX}/", dest);
+    }
+
+    auto const& type = fileSet->GetType();
+    // C++ modules do not support interface file sets which are dependent upon
+    // the configuration.
+    if (cge->GetHadContextSensitiveCondition() && type == "CXX_MODULES"_s) {
+      auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
+      std::ostringstream e;
+      e << "The \"" << gte->GetName() << "\" target's interface file set \""
+        << fileSet->GetName() << "\" of type \"" << type
+        << "\" contains context-sensitive base file entries which is not "
+           "supported.";
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return std::string{};
+    }
+
+    if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) {
+      resultVector.push_back(
+        cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
+    } else {
+      resultVector.emplace_back(cmStrCat('"', dest, '"'));
+      break;
+    }
+  }
+
+  return cmJoin(resultVector, " ");
+}
+
+std::string cmExportInstallCMakeConfigGenerator::GetFileSetFiles(
+  cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* te)
+{
+  std::vector<std::string> resultVector;
+
+  auto configs =
+    gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
+
+  auto fileEntries = fileSet->CompileFileEntries();
+  auto directoryEntries = fileSet->CompileDirectoryEntries();
+
+  cmGeneratorExpression destGe(*gte->Makefile->GetCMakeInstance());
+  auto destCge =
+    destGe.Parse(te->FileSetGenerators.at(fileSet)->GetDestination());
+
+  for (auto const& config : configs) {
+    auto directories = fileSet->EvaluateDirectoryEntries(
+      directoryEntries, gte->LocalGenerator, config, gte);
+
+    std::map<std::string, std::vector<std::string>> files;
+    for (auto const& entry : fileEntries) {
+      fileSet->EvaluateFileEntry(directories, files, entry,
+                                 gte->LocalGenerator, config, gte);
+    }
+    auto unescapedDest = destCge->Evaluate(gte->LocalGenerator, config, gte);
+    auto dest =
+      cmStrCat(cmOutputConverter::EscapeForCMake(
+                 unescapedDest, cmOutputConverter::WrapQuotes::NoWrap),
+               '/');
+    if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
+      dest = cmStrCat("${_IMPORT_PREFIX}/", dest);
+    }
+
+    bool const contextSensitive = destCge->GetHadContextSensitiveCondition() ||
+      std::any_of(directoryEntries.begin(), directoryEntries.end(),
+                  EntryIsContextSensitive) ||
+      std::any_of(fileEntries.begin(), fileEntries.end(),
+                  EntryIsContextSensitive);
+
+    auto const& type = fileSet->GetType();
+    // C++ modules do not support interface file sets which are dependent upon
+    // the configuration.
+    if (contextSensitive && type == "CXX_MODULES"_s) {
+      auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
+      std::ostringstream e;
+      e << "The \"" << gte->GetName() << "\" target's interface file set \""
+        << fileSet->GetName() << "\" of type \"" << type
+        << "\" contains context-sensitive base file entries which is not "
+           "supported.";
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return std::string{};
+    }
+
+    for (auto const& it : files) {
+      auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/');
+      for (auto const& filename : it.second) {
+        auto relFile =
+          cmStrCat(prefix, cmSystemTools::GetFilenameName(filename));
+        auto escapedFile =
+          cmStrCat(dest,
+                   cmOutputConverter::EscapeForCMake(
+                     relFile, cmOutputConverter::WrapQuotes::NoWrap));
+        if (contextSensitive && configs.size() != 1) {
+          resultVector.push_back(
+            cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
+        } else {
+          resultVector.emplace_back(cmStrCat('"', escapedFile, '"'));
+        }
+      }
+    }
+
+    if (!(contextSensitive && configs.size() != 1)) {
+      break;
+    }
+  }
+
+  return cmJoin(resultVector, " ");
+}
+
+std::string cmExportInstallCMakeConfigGenerator::GetCxxModulesDirectory() const
+{
+  return IEGen->GetCxxModuleDirectory();
+}
+
+void cmExportInstallCMakeConfigGenerator::GenerateCxxModuleConfigInformation(
+  std::string const& name, std::ostream& os) const
+{
+  // Now load per-configuration properties for them.
+  /* clang-format off */
+  os << "# Load information for each installed configuration.\n"
+        "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << "-*.cmake\")\n"
+        "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n"
+        "  include(\"${_cmake_cxx_module_include}\")\n"
+        "endforeach()\n"
+        "unset(_cmake_cxx_module_include)\n"
+        "unset(_cmake_cxx_module_includes)\n";
+  /* clang-format on */
+}
+
+bool cmExportInstallCMakeConfigGenerator::
+  GenerateImportCxxModuleConfigTargetInclusion(std::string const& name,
+                                               std::string const& config)
+{
+  auto cxx_modules_dirname = this->GetCxxModulesDirectory();
+  if (cxx_modules_dirname.empty()) {
+    return true;
+  }
+
+  std::string filename_config = config;
+  if (filename_config.empty()) {
+    filename_config = "noconfig";
+  }
+
+  std::string const dest =
+    cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/');
+  std::string fileName =
+    cmStrCat(dest, "cxx-modules-", name, '-', filename_config, ".cmake");
+
+  cmGeneratedFileStream os(fileName, true);
+  if (!os) {
+    std::string se = cmSystemTools::GetLastSystemError();
+    std::ostringstream e;
+    e << "cannot write to file \"" << fileName << "\": " << se;
+    cmSystemTools::Error(e.str());
+    return false;
+  }
+  os.SetCopyIfDifferent(true);
+
+  // Record this per-config import file.
+  this->ConfigCxxModuleFiles[config] = fileName;
+
+  auto& prop_files = this->ConfigCxxModuleTargetFiles[config];
+  for (auto const* tgt : this->ExportedTargets) {
+    // Only targets with C++ module sources will have a
+    // collator-generated install script.
+    if (!tgt->HaveCxx20ModuleSources()) {
+      continue;
+    }
+
+    auto prop_filename = cmStrCat("target-", tgt->GetFilesystemExportName(),
+                                  '-', filename_config, ".cmake");
+    prop_files.emplace_back(cmStrCat(dest, prop_filename));
+    os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n";
+  }
+
+  return true;
+}

+ 74 - 0
Source/cmExportInstallCMakeConfigGenerator.h

@@ -0,0 +1,74 @@
+/* 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
+
+#include <iosfwd>
+#include <string>
+
+#include "cmExportCMakeConfigGenerator.h"
+#include "cmExportInstallFileGenerator.h"
+
+class cmFileSet;
+class cmGeneratorTarget;
+class cmInstallExportGenerator;
+class cmTargetExport;
+
+/** \class cmExportInstallCMakeConfigGenerator
+ * \brief Generate files exporting targets from an install tree.
+ *
+ * cmExportInstallCMakeConfigGenerator generates files exporting targets from
+ * an installation tree.  The files are placed in a temporary location for
+ * installation by cmInstallExportGenerator.  The file format is CMake's native
+ * package configuration format.
+ *
+ * One main file is generated that creates the imported targets and loads
+ * per-configuration files.  Target locations and settings for each
+ * configuration are written to these per-configuration files.  After
+ * installation the main file loads the configurations that have been
+ * installed.
+ *
+ * This is used to implement the INSTALL(EXPORT) command.
+ */
+class cmExportInstallCMakeConfigGenerator
+  : public cmExportCMakeConfigGenerator
+  , public cmExportInstallFileGenerator
+{
+public:
+  /** Construct with the export installer that will install the
+      files.  */
+  cmExportInstallCMakeConfigGenerator(cmInstallExportGenerator* iegen);
+
+  /** Compute the globbing expression used to load per-config import
+      files from the main file.  */
+  std::string GetConfigImportFileGlob() const override;
+
+protected:
+  // Implement virtual methods from the superclass.
+  bool GenerateMainFile(std::ostream& os) override;
+  void GenerateImportTargetsConfig(std::ostream& os, std::string const& config,
+                                   std::string const& suffix) override;
+
+  /** Generate the relative import prefix.  */
+  virtual void GenerateImportPrefix(std::ostream&);
+
+  /** Generate the relative import prefix.  */
+  virtual void LoadConfigFiles(std::ostream&);
+
+  virtual void CleanupTemporaryVariables(std::ostream&);
+
+  /** Generate a per-configuration file for the targets.  */
+  virtual bool GenerateImportFileConfig(std::string const& config);
+
+  std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet,
+                                    cmTargetExport const* te) override;
+  std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet,
+                              cmTargetExport const* te) override;
+
+  std::string GetCxxModulesDirectory() const override;
+  void GenerateCxxModuleConfigInformation(std::string const&,
+                                          std::ostream&) const override;
+  bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&,
+                                                    std::string const&);
+};

+ 380 - 530
Source/cmExportInstallFileGenerator.cxx

@@ -3,29 +3,21 @@
 #include "cmExportInstallFileGenerator.h"
 
 #include <algorithm>
+#include <cassert>
+#include <cstddef>
 #include <memory>
+#include <set>
 #include <sstream>
-#include <utility>
-
-#include <cm/string_view>
-#include <cmext/string_view>
 
 #include "cmExportSet.h"
-#include "cmFileSet.h"
-#include "cmGeneratedFileStream.h"
-#include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
-#include "cmInstallExportGenerator.h"
-#include "cmInstallFileSetGenerator.h"
 #include "cmInstallTargetGenerator.h"
 #include "cmList.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
@@ -38,373 +30,75 @@ cmExportInstallFileGenerator::cmExportInstallFileGenerator(
 {
 }
 
-std::string cmExportInstallFileGenerator::GetConfigImportFileGlob()
+void cmExportInstallFileGenerator::ReplaceInstallPrefix(
+  std::string& input) const
 {
-  std::string glob = cmStrCat(this->FileBase, "-*", this->FileExt);
-  return glob;
+  cmGeneratorExpression::ReplaceInstallPrefix(input, this->GetInstallPrefix());
 }
 
-bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
+void cmExportInstallFileGenerator::PopulateImportProperties(
+  std::string const& config, std::string const& suffix,
+  cmTargetExport const* targetExport, ImportPropertyMap& properties,
+  std::set<std::string>& importedLocations)
 {
-  std::vector<cmTargetExport*> allTargets;
-  {
-    std::string expectedTargets;
-    std::string sep;
-    for (std::unique_ptr<cmTargetExport> const& te :
-         this->IEGen->GetExportSet()->GetTargetExports()) {
-      if (te->NamelinkOnly) {
-        continue;
-      }
-      expectedTargets += sep + this->Namespace + te->Target->GetExportName();
-      sep = " ";
-      if (this->ExportedTargets.insert(te->Target).second) {
-        allTargets.push_back(te.get());
-      } else {
-        std::ostringstream e;
-        e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
-          << "\" ...) "
-          << "includes target \"" << te->Target->GetName()
-          << "\" more than once in the export set.";
-        cmSystemTools::Error(e.str());
-        return false;
-      }
-    }
-
-    this->GenerateExpectedTargetsCode(os, expectedTargets);
-  }
-
-  // Compute the relative import prefix for the file
-  this->GenerateImportPrefix(os);
-
-  bool requiresConfigFiles = false;
-  // Create all the imported targets.
-  for (cmTargetExport* te : allTargets) {
-    cmGeneratorTarget* gt = te->Target;
-    cmStateEnums::TargetType targetType = this->GetExportTargetType(te);
-
-    requiresConfigFiles =
-      requiresConfigFiles || targetType != cmStateEnums::INTERFACE_LIBRARY;
-
-    this->GenerateImportTargetCode(os, gt, targetType);
-
-    ImportPropertyMap properties;
-
-    std::string includesDestinationDirs;
-    this->PopulateIncludeDirectoriesInterface(
-      gt, cmGeneratorExpression::InstallInterface, properties, *te,
-      includesDestinationDirs);
-    this->PopulateSourcesInterface(gt, cmGeneratorExpression::InstallInterface,
-                                   properties);
-    this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", gt,
-                                    cmGeneratorExpression::InstallInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gt,
-                                    cmGeneratorExpression::InstallInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gt,
-                                    cmGeneratorExpression::InstallInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gt,
-                                    cmGeneratorExpression::InstallInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gt,
-                                    cmGeneratorExpression::InstallInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", gt,
-                                    cmGeneratorExpression::InstallInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gt,
-                                    cmGeneratorExpression::InstallInterface,
-                                    properties);
-    this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gt,
-                                    cmGeneratorExpression::InstallInterface,
-                                    properties);
-    this->PopulateLinkDirectoriesInterface(
-      gt, cmGeneratorExpression::InstallInterface, properties);
-    this->PopulateLinkDependsInterface(
-      gt, cmGeneratorExpression::InstallInterface, properties);
-
-    std::string errorMessage;
-    if (!this->PopulateCxxModuleExportProperties(
-          gt, properties, cmGeneratorExpression::InstallInterface,
-          includesDestinationDirs, errorMessage)) {
-      cmSystemTools::Error(errorMessage);
-      return false;
-    }
-
-    if (!this->PopulateExportProperties(gt, properties, errorMessage)) {
-      cmSystemTools::Error(errorMessage);
-      return false;
-    }
-
-    const bool newCMP0022Behavior =
-      gt->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
-      gt->GetPolicyStatusCMP0022() != cmPolicies::OLD;
-    if (newCMP0022Behavior) {
-      if (this->PopulateInterfaceLinkLibrariesProperty(
-            gt, cmGeneratorExpression::InstallInterface, properties) &&
-          !this->ExportOld) {
-        this->SetRequiredCMakeVersion(2, 8, 12);
-      }
-    }
-    if (targetType == cmStateEnums::INTERFACE_LIBRARY) {
-      this->SetRequiredCMakeVersion(3, 0, 0);
-    }
-    if (gt->GetProperty("INTERFACE_SOURCES")) {
-      // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1
-      // can consume them.
-      this->SetRequiredCMakeVersion(3, 1, 0);
-    }
-
-    this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gt,
-                                    properties);
-
-    this->PopulateCompatibleInterfaceProperties(gt, properties);
-    this->PopulateCustomTransitiveInterfaceProperties(
-      gt, cmGeneratorExpression::InstallInterface, properties);
-
-    this->GenerateInterfaceProperties(gt, os, properties);
-
-    this->GenerateTargetFileSets(gt, os, te);
+  this->SetImportLocationProperty(config, suffix,
+                                  targetExport->ArchiveGenerator, properties,
+                                  importedLocations);
+  this->SetImportLocationProperty(config, suffix,
+                                  targetExport->LibraryGenerator, properties,
+                                  importedLocations);
+  this->SetImportLocationProperty(config, suffix,
+                                  targetExport->RuntimeGenerator, properties,
+                                  importedLocations);
+  this->SetImportLocationProperty(config, suffix,
+                                  targetExport->ObjectsGenerator, properties,
+                                  importedLocations);
+  this->SetImportLocationProperty(config, suffix,
+                                  targetExport->FrameworkGenerator, properties,
+                                  importedLocations);
+  this->SetImportLocationProperty(config, suffix,
+                                  targetExport->BundleGenerator, properties,
+                                  importedLocations);
+
+  // If any file location was set for the target add it to the
+  // import file.
+  if (!properties.empty()) {
+    // Get the rest of the target details.
+    cmGeneratorTarget const* const gtgt = targetExport->Target;
+    this->SetImportDetailProperties(config, suffix, gtgt, properties);
+
+    // TODO: PUBLIC_HEADER_LOCATION
+    // This should wait until the build feature propagation stuff is done.
+    // Then this can be a propagated include directory.
+    // this->GenerateImportProperty(config, te->HeaderGenerator, properties);
   }
-
-  this->LoadConfigFiles(os);
-
-  bool result = true;
-
-  std::string cxx_modules_name = this->IEGen->GetExportSet()->GetName();
-  this->GenerateCxxModuleInformation(cxx_modules_name, os);
-  if (requiresConfigFiles) {
-    for (std::string const& c : this->Configurations) {
-      if (!this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name,
-                                                              c)) {
-        result = false;
-      }
-    }
-  }
-
-  this->CleanupTemporaryVariables(os);
-  this->GenerateImportedFileCheckLoop(os);
-
-  // Generate an import file for each configuration.
-  // Don't do this if we only export INTERFACE_LIBRARY targets.
-  if (requiresConfigFiles) {
-    for (std::string const& c : this->Configurations) {
-      if (!this->GenerateImportFileConfig(c)) {
-        result = false;
-      }
-    }
-  }
-
-  this->GenerateMissingTargetsCheckCode(os);
-
-  return result;
 }
 
-void cmExportInstallFileGenerator::GenerateImportPrefix(std::ostream& os)
+std::string cmExportInstallFileGenerator::GetImportXcFrameworkLocation(
+  std::string const& config, cmTargetExport const* targetExport) const
 {
-  // Set an _IMPORT_PREFIX variable for import location properties
-  // to reference if they are relative to the install prefix.
-  std::string installPrefix =
-    this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition(
-      "CMAKE_INSTALL_PREFIX");
-  std::string const& expDest = this->IEGen->GetDestination();
-  if (cmSystemTools::FileIsFullPath(expDest)) {
-    // The export file is being installed to an absolute path so the
-    // package is not relocatable.  Use the configured install prefix.
-    /* clang-format off */
-    os <<
-      "# The installation prefix configured by this project.\n"
-      "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n"
-      "\n";
-    /* clang-format on */
-  } else {
-    // Add code to compute the installation prefix relative to the
-    // import file location.
-    std::string absDest = installPrefix + "/" + expDest;
-    std::string absDestS = absDest + "/";
-    os << "# Compute the installation prefix relative to this file.\n"
-       << "get_filename_component(_IMPORT_PREFIX"
-       << " \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n";
-    if (cmHasLiteralPrefix(absDestS, "/lib/") ||
-        cmHasLiteralPrefix(absDestS, "/lib64/") ||
-        cmHasLiteralPrefix(absDestS, "/libx32/") ||
-        cmHasLiteralPrefix(absDestS, "/usr/lib/") ||
-        cmHasLiteralPrefix(absDestS, "/usr/lib64/") ||
-        cmHasLiteralPrefix(absDestS, "/usr/libx32/")) {
-      // Handle "/usr move" symlinks created by some Linux distros.
-      /* clang-format off */
-      os <<
-        "# Use original install prefix when loaded through a\n"
-        "# cross-prefix symbolic link such as /lib -> /usr/lib.\n"
-        "get_filename_component(_realCurr \"${_IMPORT_PREFIX}\" REALPATH)\n"
-        "get_filename_component(_realOrig \"" << absDest << "\" REALPATH)\n"
-        "if(_realCurr STREQUAL _realOrig)\n"
-        "  set(_IMPORT_PREFIX \"" << absDest << "\")\n"
-        "endif()\n"
-        "unset(_realOrig)\n"
-        "unset(_realCurr)\n";
-      /* clang-format on */
-    }
-    std::string dest = expDest;
-    while (!dest.empty()) {
-      os << "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" "
-            "PATH)\n";
-      dest = cmSystemTools::GetFilenamePath(dest);
+  std::string importedXcFrameworkLocation = targetExport->XcFrameworkLocation;
+  if (!importedXcFrameworkLocation.empty()) {
+    importedXcFrameworkLocation = cmGeneratorExpression::Preprocess(
+      importedXcFrameworkLocation,
+      cmGeneratorExpression::PreprocessContext::InstallInterface, true);
+    importedXcFrameworkLocation = cmGeneratorExpression::Evaluate(
+      importedXcFrameworkLocation, targetExport->Target->GetLocalGenerator(),
+      config, targetExport->Target, nullptr, targetExport->Target);
+    if (!importedXcFrameworkLocation.empty() &&
+        !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation) &&
+        !cmHasPrefix(importedXcFrameworkLocation,
+                     this->GetImportPrefixWithSlash())) {
+      return cmStrCat(this->GetImportPrefixWithSlash(),
+                      importedXcFrameworkLocation);
     }
-    os << "if(_IMPORT_PREFIX STREQUAL \"/\")\n"
-       << "  set(_IMPORT_PREFIX \"\")\n"
-       << "endif()\n"
-       << "\n";
   }
-}
 
-void cmExportInstallFileGenerator::CleanupTemporaryVariables(std::ostream& os)
-{
-  /* clang-format off */
-  os << "# Cleanup temporary variables.\n"
-     << "set(_IMPORT_PREFIX)\n"
-     << "\n";
-  /* clang-format on */
-}
-
-void cmExportInstallFileGenerator::LoadConfigFiles(std::ostream& os)
-{
-  // Now load per-configuration properties for them.
-  /* clang-format off */
-  os << "# Load information for each installed configuration.\n"
-     << "file(GLOB _cmake_config_files \"${CMAKE_CURRENT_LIST_DIR}/"
-     << this->GetConfigImportFileGlob() << "\")\n"
-     << "foreach(_cmake_config_file IN LISTS _cmake_config_files)\n"
-     << "  include(\"${_cmake_config_file}\")\n"
-     << "endforeach()\n"
-     << "unset(_cmake_config_file)\n"
-     << "unset(_cmake_config_files)\n"
-     << "\n";
-  /* clang-format on */
-}
-
-void cmExportInstallFileGenerator::ReplaceInstallPrefix(std::string& input)
-{
-  cmGeneratorExpression::ReplaceInstallPrefix(input, "${_IMPORT_PREFIX}");
-}
-
-bool cmExportInstallFileGenerator::GenerateImportFileConfig(
-  const std::string& config)
-{
-  // Skip configurations not enabled for this export.
-  if (!this->IEGen->InstallsForConfig(config)) {
-    return true;
-  }
-
-  // Construct the name of the file to generate.
-  std::string fileName = cmStrCat(this->FileDir, '/', this->FileBase, '-');
-  if (!config.empty()) {
-    fileName += cmSystemTools::LowerCase(config);
-  } else {
-    fileName += "noconfig";
-  }
-  fileName += this->FileExt;
-
-  // Open the output file to generate it.
-  cmGeneratedFileStream exportFileStream(fileName, true);
-  if (!exportFileStream) {
-    std::string se = cmSystemTools::GetLastSystemError();
-    std::ostringstream e;
-    e << "cannot write to file \"" << fileName << "\": " << se;
-    cmSystemTools::Error(e.str());
-    return false;
-  }
-  exportFileStream.SetCopyIfDifferent(true);
-  std::ostream& os = exportFileStream;
-
-  // Start with the import file header.
-  this->GenerateImportHeaderCode(os, config);
-
-  // Generate the per-config target information.
-  this->GenerateImportConfig(os, config);
-
-  // End with the import file footer.
-  this->GenerateImportFooterCode(os);
-
-  // Record this per-config import file.
-  this->ConfigImportFiles[config] = fileName;
-
-  return true;
-}
-
-void cmExportInstallFileGenerator::GenerateImportTargetsConfig(
-  std::ostream& os, const std::string& config, std::string const& suffix)
-{
-  // Add each target in the set to the export.
-  for (std::unique_ptr<cmTargetExport> const& te :
-       this->IEGen->GetExportSet()->GetTargetExports()) {
-    // Collect import properties for this target.
-    if (this->GetExportTargetType(te.get()) ==
-        cmStateEnums::INTERFACE_LIBRARY) {
-      continue;
-    }
-
-    ImportPropertyMap properties;
-    std::set<std::string> importedLocations;
-
-    this->SetImportLocationProperty(config, suffix, te->ArchiveGenerator,
-                                    properties, importedLocations);
-    this->SetImportLocationProperty(config, suffix, te->LibraryGenerator,
-                                    properties, importedLocations);
-    this->SetImportLocationProperty(config, suffix, te->RuntimeGenerator,
-                                    properties, importedLocations);
-    this->SetImportLocationProperty(config, suffix, te->ObjectsGenerator,
-                                    properties, importedLocations);
-    this->SetImportLocationProperty(config, suffix, te->FrameworkGenerator,
-                                    properties, importedLocations);
-    this->SetImportLocationProperty(config, suffix, te->BundleGenerator,
-                                    properties, importedLocations);
-
-    // If any file location was set for the target add it to the
-    // import file.
-    if (!properties.empty()) {
-      // Get the rest of the target details.
-      cmGeneratorTarget* gtgt = te->Target;
-      this->SetImportDetailProperties(config, suffix, gtgt, properties);
-
-      this->SetImportLinkInterface(config, suffix,
-                                   cmGeneratorExpression::InstallInterface,
-                                   gtgt, properties);
-
-      // TODO: PUBLIC_HEADER_LOCATION
-      // This should wait until the build feature propagation stuff
-      // is done.  Then this can be a propagated include directory.
-      // this->GenerateImportProperty(config, te->HeaderGenerator,
-      //                              properties);
-
-      // Generate code in the export file.
-      std::string importedXcFrameworkLocation = te->XcFrameworkLocation;
-      if (!importedXcFrameworkLocation.empty()) {
-        importedXcFrameworkLocation = cmGeneratorExpression::Preprocess(
-          importedXcFrameworkLocation,
-          cmGeneratorExpression::PreprocessContext::InstallInterface, true);
-        importedXcFrameworkLocation = cmGeneratorExpression::Evaluate(
-          importedXcFrameworkLocation, te->Target->GetLocalGenerator(), config,
-          te->Target, nullptr, te->Target);
-        if (!importedXcFrameworkLocation.empty() &&
-            !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation) &&
-            !cmHasLiteralPrefix(importedXcFrameworkLocation,
-                                "${_IMPORT_PREFIX}/")) {
-          importedXcFrameworkLocation =
-            cmStrCat("${_IMPORT_PREFIX}/", importedXcFrameworkLocation);
-        }
-      }
-      this->GenerateImportPropertyCode(os, config, suffix, gtgt, properties,
-                                       importedXcFrameworkLocation);
-      this->GenerateImportedFileChecksCode(
-        os, gtgt, properties, importedLocations, importedXcFrameworkLocation);
-    }
-  }
+  return importedXcFrameworkLocation;
 }
 
 void cmExportInstallFileGenerator::SetImportLocationProperty(
-  const std::string& config, std::string const& suffix,
+  std::string const& config, std::string const& suffix,
   cmInstallTargetGenerator* itgen, ImportPropertyMap& properties,
   std::set<std::string>& importedLocations)
 {
@@ -501,11 +195,16 @@ cmStateEnums::TargetType cmExportInstallFileGenerator::GetExportTargetType(
   return targetType;
 }
 
+std::string const& cmExportInstallFileGenerator::GetExportName() const
+{
+  return this->GetExportSet()->GetName();
+}
+
 void cmExportInstallFileGenerator::HandleMissingTarget(
   std::string& link_libs, cmGeneratorTarget const* depender,
   cmGeneratorTarget* dependee)
 {
-  const std::string name = dependee->GetName();
+  std::string const& name = dependee->GetName();
   cmGlobalGenerator* gg = dependee->GetLocalGenerator()->GetGlobalGenerator();
   auto exportInfo = this->FindNamespaces(gg, name);
   std::vector<std::string> const& exportFiles = exportInfo.first;
@@ -524,14 +223,14 @@ void cmExportInstallFileGenerator::HandleMissingTarget(
 
 std::pair<std::vector<std::string>, std::string>
 cmExportInstallFileGenerator::FindNamespaces(cmGlobalGenerator* gg,
-                                             const std::string& name)
+                                             std::string const& name) const
 {
   std::vector<std::string> exportFiles;
   std::string ns;
-  const cmExportSetMap& exportSets = gg->GetExportSets();
+  cmExportSetMap const& exportSets = gg->GetExportSets();
 
   for (auto const& expIt : exportSets) {
-    const cmExportSet& exportSet = expIt.second;
+    cmExportSet const& exportSet = expIt.second;
 
     bool containsTarget = false;
     for (auto const& target : exportSet.GetTargetExports()) {
@@ -556,11 +255,11 @@ cmExportInstallFileGenerator::FindNamespaces(cmGlobalGenerator* gg,
 
 void cmExportInstallFileGenerator::ComplainAboutMissingTarget(
   cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
-  std::vector<std::string> const& exportFiles)
+  std::vector<std::string> const& exportFiles) const
 {
   std::ostringstream e;
-  e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName()
-    << "\" ...) "
+  e << "install(" << this->IEGen->InstallSubcommand() << " \""
+    << this->GetExportName() << "\" ...) "
     << "includes target \"" << depender->GetName()
     << "\" which requires target \"" << dependee->GetName() << "\" ";
   if (exportFiles.empty()) {
@@ -573,220 +272,371 @@ void cmExportInstallFileGenerator::ComplainAboutMissingTarget(
          "\""
       << dependee->GetName() << "\" target to a single export.";
   }
-  cmSystemTools::Error(e.str());
+  this->ReportError(e.str());
+}
+
+void cmExportInstallFileGenerator::ComplainAboutDuplicateTarget(
+  std::string const& targetName) const
+{
+  std::ostringstream e;
+  e << "install(" << this->IEGen->InstallSubcommand() << " \""
+    << this->GetExportName() << "\" ...) "
+    << "includes target \"" << targetName
+    << "\" more than once in the export set.";
+  this->ReportError(e.str());
+}
+
+void cmExportInstallFileGenerator::ReportError(
+  std::string const& errorMessage) const
+{
+  cmSystemTools::Error(errorMessage);
 }
 
 std::string cmExportInstallFileGenerator::InstallNameDir(
-  cmGeneratorTarget const* target, const std::string& config)
+  cmGeneratorTarget const* target, std::string const& config)
 {
   std::string install_name_dir;
 
   cmMakefile* mf = target->Target->GetMakefile();
   if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
-    install_name_dir =
-      target->GetInstallNameDirForInstallTree(config, "${_IMPORT_PREFIX}");
+    auto const& prefix = this->GetInstallPrefix();
+    install_name_dir = target->GetInstallNameDirForInstallTree(config, prefix);
   }
 
   return install_name_dir;
 }
 
-namespace {
-bool EntryIsContextSensitive(
-  const std::unique_ptr<cmCompiledGeneratorExpression>& cge)
+std::string cmExportInstallFileGenerator::GetCxxModuleFile() const
 {
-  return cge->GetHadContextSensitiveCondition();
-}
+  return this->GetCxxModuleFile(this->GetExportSet()->GetName());
 }
 
-std::string cmExportInstallFileGenerator::GetFileSetDirectories(
-  cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te)
+bool cmExportInstallFileGenerator::CollectExports(
+  std::function<void(cmTargetExport const*)> const& visitor)
 {
-  std::vector<std::string> resultVector;
-
-  auto configs =
-    gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
-
-  cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
-  auto cge = ge.Parse(te->FileSetGenerators.at(fileSet)->GetDestination());
-
-  for (auto const& config : configs) {
-    auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte);
-    auto dest = cmOutputConverter::EscapeForCMake(
-      unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
-    if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
-      dest = cmStrCat("${_IMPORT_PREFIX}/", dest);
+  auto pred = [&](std::unique_ptr<cmTargetExport> const& te) -> bool {
+    if (te->NamelinkOnly) {
+      return true;
     }
-
-    auto const& type = fileSet->GetType();
-    // C++ modules do not support interface file sets which are dependent upon
-    // the configuration.
-    if (cge->GetHadContextSensitiveCondition() && type == "CXX_MODULES"_s) {
-      auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
-      std::ostringstream e;
-      e << "The \"" << gte->GetName() << "\" target's interface file set \""
-        << fileSet->GetName() << "\" of type \"" << type
-        << "\" contains context-sensitive base file entries which is not "
-           "supported.";
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
-      return std::string{};
+    if (this->ExportedTargets.insert(te->Target).second) {
+      visitor(te.get());
+      return true;
     }
 
-    if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) {
-      resultVector.push_back(
-        cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
-    } else {
-      resultVector.emplace_back(cmStrCat('"', dest, '"'));
-      break;
-    }
-  }
+    this->ComplainAboutDuplicateTarget(te->Target->GetName());
+    return false;
+  };
 
-  return cmJoin(resultVector, " ");
+  auto const& targets = this->GetExportSet()->GetTargetExports();
+  return std::all_of(targets.begin(), targets.end(), pred);
 }
 
-std::string cmExportInstallFileGenerator::GetFileSetFiles(
-  cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te)
+bool cmExportInstallFileGenerator::PopulateInterfaceProperties(
+  cmTargetExport const* targetExport, ImportPropertyMap& properties)
 {
-  std::vector<std::string> resultVector;
+  cmGeneratorTarget const* const gt = targetExport->Target;
+
+  std::string includesDestinationDirs;
+  this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", gt,
+                                  cmGeneratorExpression::InstallInterface,
+                                  properties);
+  this->PopulateIncludeDirectoriesInterface(
+    gt, cmGeneratorExpression::InstallInterface, properties, *targetExport,
+    includesDestinationDirs);
+  this->PopulateLinkDirectoriesInterface(
+    gt, cmGeneratorExpression::InstallInterface, properties);
+  this->PopulateLinkDependsInterface(
+    gt, cmGeneratorExpression::InstallInterface, properties);
+  this->PopulateSourcesInterface(gt, cmGeneratorExpression::InstallInterface,
+                                 properties);
+
+  return this->PopulateInterfaceProperties(
+    gt, includesDestinationDirs, cmGeneratorExpression::InstallInterface,
+    properties);
+}
 
-  auto configs =
-    gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
+namespace {
+bool isSubDirectory(std::string const& a, std::string const& b)
+{
+  return (cmSystemTools::ComparePath(a, b) ||
+          cmSystemTools::IsSubDirectory(a, b));
+}
+
+bool checkInterfaceDirs(std::string const& prepro,
+                        cmGeneratorTarget const* target,
+                        std::string const& prop)
+{
+  std::string const& installDir =
+    target->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
+  std::string const& topSourceDir =
+    target->GetLocalGenerator()->GetSourceDirectory();
+  std::string const& topBinaryDir =
+    target->GetLocalGenerator()->GetBinaryDirectory();
 
-  auto fileEntries = fileSet->CompileFileEntries();
-  auto directoryEntries = fileSet->CompileDirectoryEntries();
+  std::vector<std::string> parts;
+  cmGeneratorExpression::Split(prepro, parts);
 
-  cmGeneratorExpression destGe(*gte->Makefile->GetCMakeInstance());
-  auto destCge =
-    destGe.Parse(te->FileSetGenerators.at(fileSet)->GetDestination());
+  bool const inSourceBuild = topSourceDir == topBinaryDir;
 
-  for (auto const& config : configs) {
-    auto directories = fileSet->EvaluateDirectoryEntries(
-      directoryEntries, gte->LocalGenerator, config, gte);
+  bool hadFatalError = false;
 
-    std::map<std::string, std::vector<std::string>> files;
-    for (auto const& entry : fileEntries) {
-      fileSet->EvaluateFileEntry(directories, files, entry,
-                                 gte->LocalGenerator, config, gte);
+  for (std::string const& li : parts) {
+    size_t genexPos = cmGeneratorExpression::Find(li);
+    if (genexPos == 0) {
+      continue;
     }
-    auto unescapedDest = destCge->Evaluate(gte->LocalGenerator, config, gte);
-    auto dest =
-      cmStrCat(cmOutputConverter::EscapeForCMake(
-                 unescapedDest, cmOutputConverter::WrapQuotes::NoWrap),
-               '/');
-    if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
-      dest = cmStrCat("${_IMPORT_PREFIX}/", dest);
+    if (cmHasLiteralPrefix(li, "${_IMPORT_PREFIX}")) {
+      continue;
     }
-
-    bool const contextSensitive = destCge->GetHadContextSensitiveCondition() ||
-      std::any_of(directoryEntries.begin(), directoryEntries.end(),
-                  EntryIsContextSensitive) ||
-      std::any_of(fileEntries.begin(), fileEntries.end(),
-                  EntryIsContextSensitive);
-
-    auto const& type = fileSet->GetType();
-    // C++ modules do not support interface file sets which are dependent upon
-    // the configuration.
-    if (contextSensitive && type == "CXX_MODULES"_s) {
-      auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
-      std::ostringstream e;
-      e << "The \"" << gte->GetName() << "\" target's interface file set \""
-        << fileSet->GetName() << "\" of type \"" << type
-        << "\" contains context-sensitive base file entries which is not "
-           "supported.";
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
-      return std::string{};
+    MessageType messageType = MessageType::FATAL_ERROR;
+    std::ostringstream e;
+    if (genexPos != std::string::npos) {
+      if (prop == "INTERFACE_INCLUDE_DIRECTORIES") {
+        switch (target->GetPolicyStatusCMP0041()) {
+          case cmPolicies::WARN:
+            messageType = MessageType::WARNING;
+            e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0041) << "\n";
+            break;
+          case cmPolicies::OLD:
+            continue;
+          case cmPolicies::REQUIRED_IF_USED:
+          case cmPolicies::REQUIRED_ALWAYS:
+          case cmPolicies::NEW:
+            hadFatalError = true;
+            break; // Issue fatal message.
+        }
+      } else {
+        hadFatalError = true;
+      }
     }
-
-    for (auto const& it : files) {
-      auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/');
-      for (auto const& filename : it.second) {
-        auto relFile =
-          cmStrCat(prefix, cmSystemTools::GetFilenameName(filename));
-        auto escapedFile =
-          cmStrCat(dest,
-                   cmOutputConverter::EscapeForCMake(
-                     relFile, cmOutputConverter::WrapQuotes::NoWrap));
-        if (contextSensitive && configs.size() != 1) {
-          resultVector.push_back(
-            cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
-        } else {
-          resultVector.emplace_back(cmStrCat('"', escapedFile, '"'));
+    if (!cmSystemTools::FileIsFullPath(li)) {
+      /* clang-format off */
+      e << "Target \"" << target->GetName() << "\" " << prop <<
+           " property contains relative path:\n"
+           "  \"" << li << "\"";
+      /* clang-format on */
+      target->GetLocalGenerator()->IssueMessage(messageType, e.str());
+    }
+    bool inBinary = isSubDirectory(li, topBinaryDir);
+    bool inSource = isSubDirectory(li, topSourceDir);
+    if (isSubDirectory(li, installDir)) {
+      // The include directory is inside the install tree.  If the
+      // install tree is not inside the source tree or build tree then
+      // fall through to the checks below that the include directory is not
+      // also inside the source tree or build tree.
+      bool shouldContinue =
+        (!inBinary || isSubDirectory(installDir, topBinaryDir)) &&
+        (!inSource || isSubDirectory(installDir, topSourceDir));
+
+      if (prop == "INTERFACE_INCLUDE_DIRECTORIES") {
+        if (!shouldContinue) {
+          switch (target->GetPolicyStatusCMP0052()) {
+            case cmPolicies::WARN: {
+              std::ostringstream s;
+              s << cmPolicies::GetPolicyWarning(cmPolicies::CMP0052) << "\n";
+              s << "Directory:\n    \"" << li
+                << "\"\nin "
+                   "INTERFACE_INCLUDE_DIRECTORIES of target \""
+                << target->GetName()
+                << "\" is a subdirectory of the install "
+                   "directory:\n    \""
+                << installDir
+                << "\"\nhowever it is also "
+                   "a subdirectory of the "
+                << (inBinary ? "build" : "source") << " tree:\n    \""
+                << (inBinary ? topBinaryDir : topSourceDir) << "\"\n";
+              target->GetLocalGenerator()->IssueMessage(
+                MessageType::AUTHOR_WARNING, s.str());
+              CM_FALLTHROUGH;
+            }
+            case cmPolicies::OLD:
+              shouldContinue = true;
+              break;
+            case cmPolicies::REQUIRED_ALWAYS:
+            case cmPolicies::REQUIRED_IF_USED:
+            case cmPolicies::NEW:
+              break;
+          }
         }
       }
+      if (shouldContinue) {
+        continue;
+      }
     }
-
-    if (!(contextSensitive && configs.size() != 1)) {
-      break;
+    if (inBinary) {
+      /* clang-format off */
+      e << "Target \"" << target->GetName() << "\" " << prop <<
+           " property contains path:\n"
+           "  \"" << li << "\"\nwhich is prefixed in the build directory.";
+      /* clang-format on */
+      target->GetLocalGenerator()->IssueMessage(messageType, e.str());
+    }
+    if (!inSourceBuild) {
+      if (inSource) {
+        e << "Target \"" << target->GetName() << "\" " << prop
+          << " property contains path:\n"
+             "  \""
+          << li << "\"\nwhich is prefixed in the source directory.";
+        target->GetLocalGenerator()->IssueMessage(messageType, e.str());
+      }
     }
   }
-
-  return cmJoin(resultVector, " ");
+  return !hadFatalError;
+}
 }
 
-std::string cmExportInstallFileGenerator::GetCxxModulesDirectory() const
+void cmExportInstallFileGenerator::PopulateSourcesInterface(
+  cmGeneratorTarget const* gt,
+  cmGeneratorExpression::PreprocessContext preprocessRule,
+  ImportPropertyMap& properties)
 {
-  return IEGen->GetCxxModuleDirectory();
+  assert(preprocessRule == cmGeneratorExpression::InstallInterface);
+
+  char const* const propName = "INTERFACE_SOURCES";
+  cmValue input = gt->GetProperty(propName);
+
+  if (!input) {
+    return;
+  }
+
+  if (input->empty()) {
+    properties[propName].clear();
+    return;
+  }
+
+  std::string prepro =
+    cmGeneratorExpression::Preprocess(*input, preprocessRule, true);
+  if (!prepro.empty()) {
+    this->ResolveTargetsInGeneratorExpressions(prepro, gt);
+
+    if (!checkInterfaceDirs(prepro, gt, propName)) {
+      return;
+    }
+    properties[propName] = prepro;
+  }
 }
 
-void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation(
-  std::string const& name, std::ostream& os) const
+void cmExportInstallFileGenerator::PopulateIncludeDirectoriesInterface(
+  cmGeneratorTarget const* target,
+  cmGeneratorExpression::PreprocessContext preprocessRule,
+  ImportPropertyMap& properties, cmTargetExport const& te,
+  std::string& includesDestinationDirs)
 {
-  // Now load per-configuration properties for them.
-  /* clang-format off */
-  os << "# Load information for each installed configuration.\n"
-        "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << "-*.cmake\")\n"
-        "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n"
-        "  include(\"${_cmake_cxx_module_include}\")\n"
-        "endforeach()\n"
-        "unset(_cmake_cxx_module_include)\n"
-        "unset(_cmake_cxx_module_includes)\n";
-  /* clang-format on */
+  assert(preprocessRule == cmGeneratorExpression::InstallInterface);
+
+  includesDestinationDirs.clear();
+
+  char const* const propName = "INTERFACE_INCLUDE_DIRECTORIES";
+  cmValue input = target->GetProperty(propName);
+
+  cmGeneratorExpression ge(*target->Makefile->GetCMakeInstance());
+
+  std::string dirs = cmGeneratorExpression::Preprocess(
+    cmList::to_string(target->Target->GetInstallIncludeDirectoriesEntries(te)),
+    preprocessRule, true);
+  this->ReplaceInstallPrefix(dirs);
+  std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(dirs);
+  std::string exportDirs =
+    cge->Evaluate(target->GetLocalGenerator(), "", target);
+
+  if (cge->GetHadContextSensitiveCondition()) {
+    cmLocalGenerator* lg = target->GetLocalGenerator();
+    std::ostringstream e;
+    e << "Target \"" << target->GetName()
+      << "\" is installed with "
+         "INCLUDES DESTINATION set to a context sensitive path.  Paths which "
+         "depend on the configuration, policy values or the link interface "
+         "are "
+         "not supported.  Consider using target_include_directories instead.";
+    lg->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    return;
+  }
+
+  if (!input && exportDirs.empty()) {
+    return;
+  }
+  if ((input && input->empty()) && exportDirs.empty()) {
+    // Set to empty
+    properties[propName].clear();
+    return;
+  }
+
+  this->AddImportPrefix(exportDirs);
+  includesDestinationDirs = exportDirs;
+
+  std::string includes = (input ? *input : "");
+  char const* const sep = input ? ";" : "";
+  includes += sep + exportDirs;
+  std::string prepro =
+    cmGeneratorExpression::Preprocess(includes, preprocessRule, true);
+  if (!prepro.empty()) {
+    this->ResolveTargetsInGeneratorExpressions(prepro, target);
+
+    if (!checkInterfaceDirs(prepro, target, propName)) {
+      return;
+    }
+    properties[propName] = prepro;
+  }
 }
 
-bool cmExportInstallFileGenerator::
-  GenerateImportCxxModuleConfigTargetInclusion(std::string const& name,
-                                               std::string const& config)
+void cmExportInstallFileGenerator::PopulateLinkDependsInterface(
+  cmGeneratorTarget const* gt,
+  cmGeneratorExpression::PreprocessContext preprocessRule,
+  ImportPropertyMap& properties)
 {
-  auto cxx_modules_dirname = this->GetCxxModulesDirectory();
-  if (cxx_modules_dirname.empty()) {
-    return true;
+  assert(preprocessRule == cmGeneratorExpression::InstallInterface);
+
+  char const* const propName = "INTERFACE_LINK_DEPENDS";
+  cmValue input = gt->GetProperty(propName);
+
+  if (!input) {
+    return;
   }
 
-  std::string filename_config = config;
-  if (filename_config.empty()) {
-    filename_config = "noconfig";
+  if (input->empty()) {
+    properties[propName].clear();
+    return;
   }
 
-  std::string const dest =
-    cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/');
-  std::string fileName =
-    cmStrCat(dest, "cxx-modules-", name, '-', filename_config, ".cmake");
+  std::string prepro =
+    cmGeneratorExpression::Preprocess(*input, preprocessRule, true);
+  if (!prepro.empty()) {
+    this->ResolveTargetsInGeneratorExpressions(prepro, gt);
 
-  cmGeneratedFileStream os(fileName, true);
-  if (!os) {
-    std::string se = cmSystemTools::GetLastSystemError();
-    std::ostringstream e;
-    e << "cannot write to file \"" << fileName << "\": " << se;
-    cmSystemTools::Error(e.str());
-    return false;
+    if (!checkInterfaceDirs(prepro, gt, propName)) {
+      return;
+    }
+    properties[propName] = prepro;
   }
-  os.SetCopyIfDifferent(true);
+}
 
-  // Record this per-config import file.
-  this->ConfigCxxModuleFiles[config] = fileName;
+void cmExportInstallFileGenerator::PopulateLinkDirectoriesInterface(
+  cmGeneratorTarget const* gt,
+  cmGeneratorExpression::PreprocessContext preprocessRule,
+  ImportPropertyMap& properties)
+{
+  assert(preprocessRule == cmGeneratorExpression::InstallInterface);
 
-  auto& prop_files = this->ConfigCxxModuleTargetFiles[config];
-  for (auto const* tgt : this->ExportedTargets) {
-    // Only targets with C++ module sources will have a
-    // collator-generated install script.
-    if (!tgt->HaveCxx20ModuleSources()) {
-      continue;
-    }
+  char const* const propName = "INTERFACE_LINK_DIRECTORIES";
+  cmValue input = gt->GetProperty(propName);
+
+  if (!input) {
+    return;
+  }
 
-    auto prop_filename = cmStrCat("target-", tgt->GetFilesystemExportName(),
-                                  '-', filename_config, ".cmake");
-    prop_files.emplace_back(cmStrCat(dest, prop_filename));
-    os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n";
+  if (input->empty()) {
+    properties[propName].clear();
+    return;
   }
 
-  return true;
+  std::string prepro =
+    cmGeneratorExpression::Preprocess(*input, preprocessRule, true);
+  if (!prepro.empty()) {
+    this->ResolveTargetsInGeneratorExpressions(prepro, gt);
+
+    if (!checkInterfaceDirs(prepro, gt, propName)) {
+      return;
+    }
+    properties[propName] = prepro;
+  }
 }

+ 74 - 44
Source/cmExportInstallFileGenerator.h

@@ -4,19 +4,21 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <iosfwd>
+#include <functional>
 #include <map>
 #include <set>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include <cm/string_view>
+
 #include "cmExportFileGenerator.h"
+#include "cmGeneratorExpression.h"
 #include "cmInstallExportGenerator.h"
 #include "cmStateTypes.h"
 
 class cmExportSet;
-class cmFileSet;
 class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmInstallTargetGenerator;
@@ -25,18 +27,10 @@ class cmTargetExport;
 /** \class cmExportInstallFileGenerator
  * \brief Generate a file exporting targets from an install tree.
  *
- * cmExportInstallFileGenerator generates files exporting targets from
- * install an installation tree.  The files are placed in a temporary
- * location for installation by cmInstallExportGenerator.  One main
- * file is generated that creates the imported targets and loads
- * per-configuration files.  Target locations and settings for each
- * configuration are written to these per-configuration files.  After
- * installation the main file loads the configurations that have been
- * installed.
- *
- * This is used to implement the INSTALL(EXPORT) command.
+ * cmExportInstallFileGenerator is the generic interface class for generating
+ * export files for an install tree.
  */
-class cmExportInstallFileGenerator : public cmExportFileGenerator
+class cmExportInstallFileGenerator : virtual public cmExportFileGenerator
 {
 public:
   /** Construct with the export installer that will install the
@@ -51,6 +45,9 @@ public:
     return this->ConfigImportFiles;
   }
 
+  /** Get the temporary location of the config-agnostic C++ module file.  */
+  std::string GetCxxModuleFile() const;
+
   /** Get the per-config C++ module file generated for each configuration.
       This maps from the configuration name to the file temporary location
       for installation.  */
@@ -70,65 +67,73 @@ public:
 
   /** Compute the globbing expression used to load per-config import
       files from the main file.  */
-  std::string GetConfigImportFileGlob();
+  virtual std::string GetConfigImportFileGlob() const = 0;
 
 protected:
-  // Implement virtual methods from the superclass.
-  bool GenerateMainFile(std::ostream& os) override;
-  void GenerateImportTargetsConfig(std::ostream& os, const std::string& config,
-                                   std::string const& suffix) override;
   cmStateEnums::TargetType GetExportTargetType(
     cmTargetExport const* targetExport) const;
+
+  virtual std::string const& GetExportName() const;
+
+  std::string GetInstallPrefix() const
+  {
+    cm::string_view const& prefixWithSlash = this->GetImportPrefixWithSlash();
+    return std::string(prefixWithSlash.data(), prefixWithSlash.length() - 1);
+  }
+
   void HandleMissingTarget(std::string& link_libs,
                            cmGeneratorTarget const* depender,
                            cmGeneratorTarget* dependee) override;
 
-  void ReplaceInstallPrefix(std::string& input) override;
+  void ReplaceInstallPrefix(std::string& input) const override;
 
-  void ComplainAboutMissingTarget(cmGeneratorTarget const* depender,
-                                  cmGeneratorTarget const* dependee,
-                                  std::vector<std::string> const& exportFiles);
+  void ComplainAboutMissingTarget(
+    cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
+    std::vector<std::string> const& exportFiles) const;
 
-  std::pair<std::vector<std::string>, std::string> FindNamespaces(
-    cmGlobalGenerator* gg, const std::string& name);
-
-  /** Generate the relative import prefix.  */
-  virtual void GenerateImportPrefix(std::ostream&);
+  void ComplainAboutDuplicateTarget(
+    std::string const& targetName) const override;
 
-  /** Generate the relative import prefix.  */
-  virtual void LoadConfigFiles(std::ostream&);
-
-  virtual void CleanupTemporaryVariables(std::ostream&);
+  std::pair<std::vector<std::string>, std::string> FindNamespaces(
+    cmGlobalGenerator* gg, std::string const& name) const;
 
-  /** Generate a per-configuration file for the targets.  */
-  virtual bool GenerateImportFileConfig(const std::string& config);
+  void ReportError(std::string const& errorMessage) const override;
 
   /** Fill in properties indicating installed file locations.  */
-  void SetImportLocationProperty(const std::string& config,
+  void SetImportLocationProperty(std::string const& config,
                                  std::string const& suffix,
                                  cmInstallTargetGenerator* itgen,
                                  ImportPropertyMap& properties,
                                  std::set<std::string>& importedLocations);
 
   std::string InstallNameDir(cmGeneratorTarget const* target,
-                             const std::string& config) override;
+                             std::string const& config) override;
 
-  std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet,
-                                    cmTargetExport* te) override;
-  std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet,
-                              cmTargetExport* te) override;
+  using cmExportFileGenerator::GetCxxModuleFile;
 
-  std::string GetCxxModulesDirectory() const override;
-  void GenerateCxxModuleConfigInformation(std::string const&,
-                                          std::ostream&) const override;
-  bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&,
-                                                    std::string const&);
+  /** Walk the list of targets to be exported.  Returns true iff no duplicates
+      are found.  */
+  bool CollectExports(
+    std::function<void(cmTargetExport const*)> const& visitor);
 
   cmExportSet* GetExportSet() const override
   {
     return this->IEGen->GetExportSet();
   }
 
+  std::string GetImportXcFrameworkLocation(
+    std::string const& config, cmTargetExport const* targetExport) const;
+
+  using cmExportFileGenerator::PopulateInterfaceProperties;
+  bool PopulateInterfaceProperties(cmTargetExport const* targetExport,
+                                   ImportPropertyMap& properties);
+
+  void PopulateImportProperties(std::string const& config,
+                                std::string const& suffix,
+                                cmTargetExport const* targetExport,
+                                ImportPropertyMap& properties,
+                                std::set<std::string>& importedLocations);
+
   cmInstallExportGenerator* IEGen;
 
   // The import file generated for each configuration.
@@ -137,4 +142,29 @@ protected:
   std::map<std::string, std::string> ConfigCxxModuleFiles;
   // The C++ module property target files generated for each configuration.
   std::map<std::string, std::vector<std::string>> ConfigCxxModuleTargetFiles;
+
+private:
+  void PopulateCompatibleInterfaceProperties(cmGeneratorTarget const* target,
+                                             ImportPropertyMap& properties);
+  void PopulateCustomTransitiveInterfaceProperties(
+    cmGeneratorTarget const* target,
+    cmGeneratorExpression::PreprocessContext preprocessRule,
+    ImportPropertyMap& properties);
+  void PopulateIncludeDirectoriesInterface(
+    cmGeneratorTarget const* target,
+    cmGeneratorExpression::PreprocessContext preprocessRule,
+    ImportPropertyMap& properties, cmTargetExport const& te,
+    std::string& includesDestinationDirs);
+  void PopulateSourcesInterface(
+    cmGeneratorTarget const* target,
+    cmGeneratorExpression::PreprocessContext preprocessRule,
+    ImportPropertyMap& properties);
+  void PopulateLinkDirectoriesInterface(
+    cmGeneratorTarget const* target,
+    cmGeneratorExpression::PreprocessContext preprocessRule,
+    ImportPropertyMap& properties);
+  void PopulateLinkDependsInterface(
+    cmGeneratorTarget const* target,
+    cmGeneratorExpression::PreprocessContext preprocessRule,
+    ImportPropertyMap& properties);
 };

+ 14 - 7
Source/cmExportTryCompileFileGenerator.cxx

@@ -19,19 +19,26 @@
 #include "cmOutputConverter.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmValue.h"
 
 class cmTargetExport;
 
 cmExportTryCompileFileGenerator::cmExportTryCompileFileGenerator(
-  cmGlobalGenerator* gg, const std::vector<std::string>& targets,
+  cmGlobalGenerator* gg, std::vector<std::string> const& targets,
   cmMakefile* mf, std::set<std::string> const& langs)
   : Languages(langs.begin(), langs.end())
 {
   gg->CreateImportedGenerationObjects(mf, targets, this->Exports);
 }
 
+void cmExportTryCompileFileGenerator::ReportError(
+  std::string const& errorMessage) const
+{
+  cmSystemTools::Error(errorMessage);
+}
+
 bool cmExportTryCompileFileGenerator::GenerateMainFile(std::ostream& os)
 {
   std::set<cmGeneratorTarget const*> emitted;
@@ -61,7 +68,7 @@ bool cmExportTryCompileFileGenerator::GenerateMainFile(std::ostream& os)
 }
 
 std::string cmExportTryCompileFileGenerator::FindTargets(
-  const std::string& propName, cmGeneratorTarget const* tgt,
+  std::string const& propName, cmGeneratorTarget const* tgt,
   std::string const& language, std::set<cmGeneratorTarget const*>& emitted)
 {
   cmValue prop = tgt->GetProperty(propName);
@@ -94,7 +101,7 @@ std::string cmExportTryCompileFileGenerator::FindTargets(
   std::string result = cge->Evaluate(tgt->GetLocalGenerator(), this->Config,
                                      &gDummyHead, &dagChecker, tgt, language);
 
-  const std::set<cmGeneratorTarget const*>& allTargets =
+  std::set<cmGeneratorTarget const*> const& allTargets =
     cge->GetAllTargetsSeen();
   for (cmGeneratorTarget const* target : allTargets) {
     if (emitted.insert(target).second) {
@@ -105,7 +112,7 @@ std::string cmExportTryCompileFileGenerator::FindTargets(
 }
 
 void cmExportTryCompileFileGenerator::PopulateProperties(
-  const cmGeneratorTarget* target, ImportPropertyMap& properties,
+  cmGeneratorTarget const* target, ImportPropertyMap& properties,
   std::set<cmGeneratorTarget const*>& emitted)
 {
   // Look through all non-special properties.
@@ -140,7 +147,7 @@ void cmExportTryCompileFileGenerator::PopulateProperties(
 }
 
 std::string cmExportTryCompileFileGenerator::InstallNameDir(
-  cmGeneratorTarget const* target, const std::string& config)
+  cmGeneratorTarget const* target, std::string const& config)
 {
   std::string install_name_dir;
 
@@ -153,14 +160,14 @@ std::string cmExportTryCompileFileGenerator::InstallNameDir(
 }
 
 std::string cmExportTryCompileFileGenerator::GetFileSetDirectories(
-  cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport* /*te*/)
+  cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport const* /*te*/)
 {
   return cmOutputConverter::EscapeForCMake(
     cmList::to_string(fileSet->GetDirectoryEntries()));
 }
 
 std::string cmExportTryCompileFileGenerator::GetFileSetFiles(
-  cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport* /*te*/)
+  cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport const* /*te*/)
 {
   return cmOutputConverter::EscapeForCMake(
     cmList::to_string(fileSet->GetFileEntries()));

+ 15 - 11
Source/cmExportTryCompileFileGenerator.h

@@ -9,7 +9,7 @@
 #include <string>
 #include <vector>
 
-#include "cmExportFileGenerator.h"
+#include "cmExportCMakeConfigGenerator.h"
 
 class cmFileSet;
 class cmGeneratorTarget;
@@ -17,7 +17,7 @@ class cmGlobalGenerator;
 class cmMakefile;
 class cmTargetExport;
 
-class cmExportTryCompileFileGenerator : public cmExportFileGenerator
+class cmExportTryCompileFileGenerator : public cmExportCMakeConfigGenerator
 {
 public:
   cmExportTryCompileFileGenerator(cmGlobalGenerator* gg,
@@ -26,13 +26,17 @@ public:
                                   std::set<std::string> const& langs);
 
   /** Set the list of targets to export.  */
-  void SetConfig(const std::string& config) { this->Config = config; }
+  void SetConfig(std::string const& config) { this->Config = config; }
 
 protected:
   // Implement virtual methods from the superclass.
+  void ComplainAboutDuplicateTarget(
+    std::string const& /*targetName*/) const override{};
+  void ReportError(std::string const& errorMessage) const override;
+
   bool GenerateMainFile(std::ostream& os) override;
 
-  void GenerateImportTargetsConfig(std::ostream&, const std::string&,
+  void GenerateImportTargetsConfig(std::ostream&, std::string const&,
                                    std::string const&) override
   {
   }
@@ -43,17 +47,17 @@ protected:
 
   void PopulateProperties(cmGeneratorTarget const* target,
                           ImportPropertyMap& properties,
-                          std::set<const cmGeneratorTarget*>& emitted);
+                          std::set<cmGeneratorTarget const*>& emitted);
 
   std::string InstallNameDir(cmGeneratorTarget const* target,
-                             const std::string& config) override;
+                             std::string const& config) override;
 
   std::string GetFileSetDirectories(cmGeneratorTarget* target,
                                     cmFileSet* fileSet,
-                                    cmTargetExport* te) override;
+                                    cmTargetExport const* te) override;
 
   std::string GetFileSetFiles(cmGeneratorTarget* target, cmFileSet* fileSet,
-                              cmTargetExport* te) override;
+                              cmTargetExport const* te) override;
 
   std::string GetCxxModulesDirectory() const override { return {}; }
   void GenerateCxxModuleConfigInformation(std::string const&,
@@ -62,10 +66,10 @@ protected:
   }
 
 private:
-  std::string FindTargets(const std::string& prop,
-                          const cmGeneratorTarget* tgt,
+  std::string FindTargets(std::string const& prop,
+                          cmGeneratorTarget const* tgt,
                           std::string const& language,
-                          std::set<const cmGeneratorTarget*>& emitted);
+                          std::set<cmGeneratorTarget const*>& emitted);
 
   std::vector<cmGeneratorTarget const*> Exports;
   std::string Config;

+ 30 - 0
Source/cmInstallAndroidMKExportGenerator.cxx

@@ -0,0 +1,30 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmInstallAndroidMKExportGenerator.h"
+
+#include <utility>
+
+#include <cm/memory>
+
+#include "cmExportInstallAndroidMKGenerator.h"
+#include "cmExportInstallFileGenerator.h"
+#include "cmListFileCache.h"
+
+class cmExportSet;
+
+cmInstallAndroidMKExportGenerator::cmInstallAndroidMKExportGenerator(
+  cmExportSet* exportSet, std::string destination, std::string filePermissions,
+  std::vector<std::string> const& configurations, std::string component,
+  MessageLevel message, bool excludeFromAll, std::string filename,
+  std::string targetNamespace, cmListFileBacktrace backtrace)
+  : cmInstallExportGenerator(exportSet, std::move(destination),
+                             std::move(filePermissions), configurations,
+                             std::move(component), message, excludeFromAll,
+                             std::move(filename), std::move(targetNamespace),
+                             std::string{}, std::move(backtrace))
+{
+  this->EFGen = cm::make_unique<cmExportInstallAndroidMKGenerator>(this);
+}
+
+cmInstallAndroidMKExportGenerator::~cmInstallAndroidMKExportGenerator() =
+  default;

+ 36 - 0
Source/cmInstallAndroidMKExportGenerator.h

@@ -0,0 +1,36 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "cmInstallExportGenerator.h"
+
+class cmExportSet;
+class cmListFileBacktrace;
+
+/** \class cmInstallAndroidMKExportGenerator
+ * \brief Generate rules for creating Android .mk export files.
+ */
+class cmInstallAndroidMKExportGenerator : public cmInstallExportGenerator
+{
+public:
+  cmInstallAndroidMKExportGenerator(
+    cmExportSet* exportSet, std::string destination,
+    std::string filePermissions,
+    std::vector<std::string> const& configurations, std::string component,
+    MessageLevel message, bool excludeFromAll, std::string filename,
+    std::string targetNamespace, cmListFileBacktrace backtrace);
+  cmInstallAndroidMKExportGenerator(cmInstallAndroidMKExportGenerator const&) =
+    delete;
+  ~cmInstallAndroidMKExportGenerator() override;
+
+  cmInstallAndroidMKExportGenerator& operator=(
+    cmInstallAndroidMKExportGenerator const&) = delete;
+
+  char const* InstallSubcommand() const override
+  {
+    return "EXPORT_ANDROID_MK";
+  }
+};

+ 43 - 0
Source/cmInstallCMakeConfigExportGenerator.cxx

@@ -0,0 +1,43 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmInstallCMakeConfigExportGenerator.h"
+
+#include <utility>
+
+#include <cm/memory>
+
+#include "cmExportInstallCMakeConfigGenerator.h"
+#include "cmExportInstallFileGenerator.h"
+#include "cmListFileCache.h"
+
+class cmExportSet;
+
+cmInstallCMakeConfigExportGenerator::cmInstallCMakeConfigExportGenerator(
+  cmExportSet* exportSet, std::string destination, std::string filePermissions,
+  std::vector<std::string> const& configurations, std::string component,
+  MessageLevel message, bool excludeFromAll, std::string filename,
+  std::string targetNamespace, std::string cxxModulesDirectory, bool exportOld,
+  bool exportPackageDependencies, cmListFileBacktrace backtrace)
+  : cmInstallExportGenerator(
+      exportSet, std::move(destination), std::move(filePermissions),
+      configurations, std::move(component), message, excludeFromAll,
+      std::move(filename), std::move(targetNamespace),
+      std::move(cxxModulesDirectory), std::move(backtrace))
+  , ExportOld(exportOld)
+  , ExportPackageDependencies(exportPackageDependencies)
+{
+  this->EFGen = cm::make_unique<cmExportInstallCMakeConfigGenerator>(this);
+}
+
+cmInstallCMakeConfigExportGenerator::~cmInstallCMakeConfigExportGenerator() =
+  default;
+
+void cmInstallCMakeConfigExportGenerator::GenerateScript(std::ostream& os)
+{
+  auto* const efgen =
+    static_cast<cmExportInstallCMakeConfigGenerator*>(this->EFGen.get());
+  efgen->SetExportOld(this->ExportOld);
+  efgen->SetExportPackageDependencies(this->ExportPackageDependencies);
+
+  this->cmInstallExportGenerator::GenerateScript(os);
+}

+ 42 - 0
Source/cmInstallCMakeConfigExportGenerator.h

@@ -0,0 +1,42 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "cmInstallExportGenerator.h"
+
+class cmExportSet;
+class cmListFileBacktrace;
+
+/** \class cmInstallCMakeConfigExportGenerator
+ * \brief Generate rules for creating CMake export files.
+ */
+class cmInstallCMakeConfigExportGenerator : public cmInstallExportGenerator
+{
+public:
+  cmInstallCMakeConfigExportGenerator(
+    cmExportSet* exportSet, std::string destination,
+    std::string filePermissions,
+    std::vector<std::string> const& configurations, std::string component,
+    MessageLevel message, bool excludeFromAll, std::string filename,
+    std::string targetNamespace, std::string cxxModulesDirectory,
+    bool exportOld, bool exportPackageDependencies,
+    cmListFileBacktrace backtrace);
+  cmInstallCMakeConfigExportGenerator(
+    cmInstallCMakeConfigExportGenerator const&) = delete;
+  ~cmInstallCMakeConfigExportGenerator() override;
+
+  cmInstallCMakeConfigExportGenerator& operator=(
+    cmInstallCMakeConfigExportGenerator const&) = delete;
+
+  char const* InstallSubcommand() const override { return "EXPORT"; }
+
+protected:
+  void GenerateScript(std::ostream& os) override;
+
+  bool const ExportOld;
+  bool const ExportPackageDependencies;
+};

+ 7 - 6
Source/cmInstallCommand.cxx

@@ -26,10 +26,11 @@
 #include "cmFileSet.h"
 #include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
+#include "cmInstallAndroidMKExportGenerator.h"
+#include "cmInstallCMakeConfigExportGenerator.h"
 #include "cmInstallCommandArguments.h"
 #include "cmInstallCxxModuleBmiGenerator.h"
 #include "cmInstallDirectoryGenerator.h"
-#include "cmInstallExportGenerator.h"
 #include "cmInstallFileSetGenerator.h"
 #include "cmInstallFilesGenerator.h"
 #include "cmInstallGenerator.h"
@@ -2028,10 +2029,10 @@ bool HandleExportAndroidMKMode(std::vector<std::string> const& args,
 
   // Create the export install generator.
   helper.Makefile->AddInstallGenerator(
-    cm::make_unique<cmInstallExportGenerator>(
+    cm::make_unique<cmInstallAndroidMKExportGenerator>(
       &exportSet, ica.GetDestination(), ica.GetPermissions(),
       ica.GetConfigurations(), ica.GetComponent(), message,
-      ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true, false,
+      ica.GetExcludeFromAll(), std::move(fname), std::move(name_space),
       helper.Makefile->GetBacktrace()));
 
   return true;
@@ -2151,11 +2152,11 @@ bool HandleExportMode(std::vector<std::string> const& args,
 
   // Create the export install generator.
   helper.Makefile->AddInstallGenerator(
-    cm::make_unique<cmInstallExportGenerator>(
+    cm::make_unique<cmInstallCMakeConfigExportGenerator>(
       &exportSet, ica.GetDestination(), ica.GetPermissions(),
       ica.GetConfigurations(), ica.GetComponent(), message,
-      ica.GetExcludeFromAll(), fname, name_space, cxx_modules_directory,
-      exportOld, false, exportPackageDependencies,
+      ica.GetExcludeFromAll(), std::move(fname), std::move(name_space),
+      std::move(cxx_modules_directory), exportOld, exportPackageDependencies,
       helper.Makefile->GetBacktrace()));
 
   return true;

+ 69 - 82
Source/cmInstallExportGenerator.cxx

@@ -6,12 +6,7 @@
 #include <sstream>
 #include <utility>
 
-#include <cm/memory>
-
 #include "cmCryptoHash.h"
-#ifndef CMAKE_BOOTSTRAP
-#  include "cmExportInstallAndroidMKGenerator.h"
-#endif
 #include "cmExportInstallFileGenerator.h"
 #include "cmExportSet.h"
 #include "cmInstallType.h"
@@ -22,29 +17,20 @@
 #include "cmSystemTools.h"
 
 cmInstallExportGenerator::cmInstallExportGenerator(
-  cmExportSet* exportSet, std::string const& destination,
-  std::string file_permissions, std::vector<std::string> const& configurations,
-  std::string const& component, MessageLevel message, bool exclude_from_all,
-  std::string filename, std::string name_space,
-  std::string cxx_modules_directory, bool exportOld, bool android,
-  bool exportPackageDependencies, cmListFileBacktrace backtrace)
-  : cmInstallGenerator(destination, configurations, component, message,
-                       exclude_from_all, false, std::move(backtrace))
+  cmExportSet* exportSet, std::string destination, std::string filePermissions,
+  std::vector<std::string> const& configurations, std::string component,
+  MessageLevel message, bool excludeFromAll, std::string filename,
+  std::string targetNamespace, std::string cxxModulesDirectory,
+  cmListFileBacktrace backtrace)
+  : cmInstallGenerator(std::move(destination), configurations,
+                       std::move(component), message, excludeFromAll, false,
+                       std::move(backtrace))
   , ExportSet(exportSet)
-  , FilePermissions(std::move(file_permissions))
+  , FilePermissions(std::move(filePermissions))
   , FileName(std::move(filename))
-  , Namespace(std::move(name_space))
-  , CxxModulesDirectory(std::move(cxx_modules_directory))
-  , ExportOld(exportOld)
-  , ExportPackageDependencies(exportPackageDependencies)
+  , Namespace(std::move(targetNamespace))
+  , CxxModulesDirectory(std::move(cxxModulesDirectory))
 {
-  if (android) {
-#ifndef CMAKE_BOOTSTRAP
-    this->EFGen = cm::make_unique<cmExportInstallAndroidMKGenerator>(this);
-#endif
-  } else {
-    this->EFGen = cm::make_unique<cmExportInstallFileGenerator>(this);
-  }
   exportSet->AddInstallation(this);
 }
 
@@ -92,7 +78,7 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os)
   // Skip empty sets.
   if (this->ExportSet->GetTargetExports().empty()) {
     std::ostringstream e;
-    e << "INSTALL(EXPORT) given unknown export \""
+    e << "INSTALL(" << this->InstallSubcommand() << ") given unknown export \""
       << this->ExportSet->GetName() << "\"";
     cmSystemTools::Error(e.str());
     return;
@@ -108,7 +94,6 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os)
   // Generate the import file for this export set.
   this->EFGen->SetExportFile(this->MainImportFile.c_str());
   this->EFGen->SetNamespace(this->Namespace);
-  this->EFGen->SetExportOld(this->ExportOld);
   if (this->ConfigurationTypes->empty()) {
     if (!this->ConfigurationName.empty()) {
       this->EFGen->AddConfiguration(this->ConfigurationName);
@@ -120,7 +105,6 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os)
       this->EFGen->AddConfiguration(c);
     }
   }
-  this->EFGen->SetExportPackageDependencies(this->ExportPackageDependencies);
   this->EFGen->GenerateImportFile();
 
   // Perform the main install script generation.
@@ -149,37 +133,37 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os,
 
   // Now create a configuration-specific install rule for the C++ module import
   // property file of each configuration.
-  auto cxx_module_dest =
+  auto const cxxModuleDestination =
     cmStrCat(this->Destination, '/', this->CxxModulesDirectory);
-  std::string config_file_example;
-  for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) {
-    config_file_example = i.second;
-    break;
-  }
-  if (!config_file_example.empty()) {
+  auto const cxxModuleInstallFilePath = this->EFGen->GetCxxModuleFile();
+  auto const configImportFilesGlob = this->EFGen->GetConfigImportFileGlob();
+  if (!cxxModuleInstallFilePath.empty() && !configImportFilesGlob.empty()) {
+    auto const cxxModuleFilename =
+      cmSystemTools::GetFilenameName(cxxModuleInstallFilePath);
+
     // Remove old per-configuration export files if the main changes.
-    std::string installedDir = cmStrCat(
-      "$ENV{DESTDIR}", ConvertToAbsoluteDestination(cxx_module_dest), '/');
-    std::string installedFile = cmStrCat(installedDir, "/cxx-modules-",
-                                         this->ExportSet->GetName(), ".cmake");
-    std::string toInstallFile =
-      cmStrCat(cmSystemTools::GetFilenamePath(config_file_example),
-               "/cxx-modules-", this->ExportSet->GetName(), ".cmake");
+    std::string installedDir =
+      cmStrCat("$ENV{DESTDIR}",
+               ConvertToAbsoluteDestination(cxxModuleDestination), '/');
+    std::string installedFile = cmStrCat(installedDir, cxxModuleFilename);
     os << indent << "if(EXISTS \"" << installedFile << "\")\n";
     Indent indentN = indent.Next();
     Indent indentNN = indentN.Next();
     Indent indentNNN = indentNN.Next();
-    /* clang-format off */
     os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n"
        << indentN << "     \"" << installedFile << "\"\n"
-       << indentN << "     \"" << toInstallFile << "\")\n";
+       << indentN << "     \"" << cxxModuleInstallFilePath << "\")\n";
     os << indentN << "if(_cmake_export_file_changed)\n";
     os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir
-       << this->EFGen->GetConfigImportFileGlob() << "\")\n";
+       << configImportFilesGlob << "\")\n";
     os << indentNN << "if(_cmake_old_config_files)\n";
-    os << indentNNN << "string(REPLACE \";\" \", \" _cmake_old_config_files_text \"${_cmake_old_config_files}\")\n";
-    os << indentNNN << R"(message(STATUS "Old C++ module export file \")" << installedFile
-       << "\\\" will be replaced.  Removing files [${_cmake_old_config_files_text}].\")\n";
+    os << indentNNN
+       << "string(REPLACE \";\" \", \" _cmake_old_config_files_text "
+          "\"${_cmake_old_config_files}\")\n";
+    os << indentNNN << R"(message(STATUS "Old C++ module export file \")"
+       << installedFile
+       << "\\\" will be replaced.  "
+          "Removing files [${_cmake_old_config_files_text}].\")\n";
     os << indentNNN << "unset(_cmake_old_config_files_text)\n";
     os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n";
     os << indentNN << "endif()\n";
@@ -187,12 +171,11 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os,
     os << indentN << "endif()\n";
     os << indentN << "unset(_cmake_export_file_changed)\n";
     os << indent << "endif()\n";
-    /* clang-format on */
 
     // All of these files are siblings; get its location to know where the
     // "anchor" file is.
-    files.push_back(toInstallFile);
-    this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, files,
+    files.push_back(cxxModuleInstallFilePath);
+    this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, files,
                          false, this->FilePermissions.c_str(), nullptr,
                          nullptr, nullptr, indent);
     files.clear();
@@ -201,7 +184,7 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os,
     files.push_back(i.second);
     std::string config_test = this->CreateConfigTest(i.first);
     os << indent << "if(" << config_test << ")\n";
-    this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, files,
+    this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, files,
                          false, this->FilePermissions.c_str(), nullptr,
                          nullptr, nullptr, indent.Next());
     os << indent << "endif()\n";
@@ -210,9 +193,9 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os,
   for (auto const& i : this->EFGen->GetConfigCxxModuleTargetFiles()) {
     std::string config_test = this->CreateConfigTest(i.first);
     os << indent << "if(" << config_test << ")\n";
-    this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, i.second,
-                         false, this->FilePermissions.c_str(), nullptr,
-                         nullptr, nullptr, indent.Next());
+    this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES,
+                         i.second, false, this->FilePermissions.c_str(),
+                         nullptr, nullptr, nullptr, indent.Next());
     os << indent << "endif()\n";
     files.clear();
   }
@@ -221,33 +204,37 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os,
 void cmInstallExportGenerator::GenerateScriptActions(std::ostream& os,
                                                      Indent indent)
 {
-  // Remove old per-configuration export files if the main changes.
-  std::string installedDir = cmStrCat(
-    "$ENV{DESTDIR}", ConvertToAbsoluteDestination(this->Destination), '/');
-  std::string installedFile = cmStrCat(installedDir, this->FileName);
-  os << indent << "if(EXISTS \"" << installedFile << "\")\n";
-  Indent indentN = indent.Next();
-  Indent indentNN = indentN.Next();
-  Indent indentNNN = indentNN.Next();
-  /* clang-format off */
-  os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n"
-     << indentN << "     \"" << installedFile << "\"\n"
-     << indentN << "     \"" << this->MainImportFile << "\")\n";
-  os << indentN << "if(_cmake_export_file_changed)\n";
-  os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir
-     << this->EFGen->GetConfigImportFileGlob() << "\")\n";
-  os << indentNN << "if(_cmake_old_config_files)\n";
-  os << indentNNN << "string(REPLACE \";\" \", \" _cmake_old_config_files_text \"${_cmake_old_config_files}\")\n";
-  os << indentNNN << R"(message(STATUS "Old export file \")" << installedFile
-     << "\\\" will be replaced.  Removing files [${_cmake_old_config_files_text}].\")\n";
-  os << indentNNN << "unset(_cmake_old_config_files_text)\n";
-  os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n";
-  os << indentNN << "endif()\n";
-  os << indentNN << "unset(_cmake_old_config_files)\n";
-  os << indentN << "endif()\n";
-  os << indentN << "unset(_cmake_export_file_changed)\n";
-  os << indent << "endif()\n";
-  /* clang-format on */
+  auto const configImportFilesGlob = this->EFGen->GetConfigImportFileGlob();
+  if (!configImportFilesGlob.empty()) {
+    // Remove old per-configuration export files if the main changes.
+    std::string installedDir = cmStrCat(
+      "$ENV{DESTDIR}", ConvertToAbsoluteDestination(this->Destination), '/');
+    std::string installedFile = cmStrCat(installedDir, this->FileName);
+    os << indent << "if(EXISTS \"" << installedFile << "\")\n";
+    Indent indentN = indent.Next();
+    Indent indentNN = indentN.Next();
+    Indent indentNNN = indentNN.Next();
+    os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n"
+       << indentN << "     \"" << installedFile << "\"\n"
+       << indentN << "     \"" << this->MainImportFile << "\")\n";
+    os << indentN << "if(_cmake_export_file_changed)\n";
+    os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir
+       << configImportFilesGlob << "\")\n";
+    os << indentNN << "if(_cmake_old_config_files)\n";
+    os << indentNNN
+       << "string(REPLACE \";\" \", \" _cmake_old_config_files_text "
+          "\"${_cmake_old_config_files}\")\n";
+    os << indentNNN << R"(message(STATUS "Old export file \")" << installedFile
+       << "\\\" will be replaced.  "
+          "Removing files [${_cmake_old_config_files_text}].\")\n";
+    os << indentNNN << "unset(_cmake_old_config_files_text)\n";
+    os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n";
+    os << indentNN << "endif()\n";
+    os << indentNN << "unset(_cmake_old_config_files)\n";
+    os << indentN << "endif()\n";
+    os << indentN << "unset(_cmake_export_file_changed)\n";
+    os << indent << "endif()\n";
+  }
 
   // Install the main export file.
   std::vector<std::string> files;

+ 10 - 13
Source/cmInstallExportGenerator.h

@@ -17,19 +17,18 @@ class cmListFileBacktrace;
 class cmLocalGenerator;
 
 /** \class cmInstallExportGenerator
- * \brief Generate rules for creating an export files.
+ * \brief Support class for generating rules for creating export files.
  */
 class cmInstallExportGenerator : public cmInstallGenerator
 {
 public:
-  cmInstallExportGenerator(cmExportSet* exportSet, std::string const& dest,
-                           std::string file_permissions,
-                           const std::vector<std::string>& configurations,
-                           std::string const& component, MessageLevel message,
-                           bool exclude_from_all, std::string filename,
-                           std::string name_space,
-                           std::string cxx_modules_directory, bool exportOld,
-                           bool android, bool exportPackageDependencies,
+  cmInstallExportGenerator(cmExportSet* exportSet, std::string destination,
+                           std::string filePermissions,
+                           std::vector<std::string> const& configurations,
+                           std::string component, MessageLevel message,
+                           bool excludeFromAll, std::string filename,
+                           std::string targetNamespace,
+                           std::string cxxModulesDirectory,
                            cmListFileBacktrace backtrace);
   cmInstallExportGenerator(const cmInstallExportGenerator&) = delete;
   ~cmInstallExportGenerator() override;
@@ -37,6 +36,8 @@ public:
   cmInstallExportGenerator& operator=(const cmInstallExportGenerator&) =
     delete;
 
+  virtual char const* InstallSubcommand() const = 0;
+
   cmExportSet* GetExportSet() { return this->ExportSet; }
 
   bool Compute(cmLocalGenerator* lg) override;
@@ -60,8 +61,6 @@ protected:
   void GenerateScript(std::ostream& os) override;
   void GenerateScriptConfigs(std::ostream& os, Indent indent) override;
   void GenerateScriptActions(std::ostream& os, Indent indent) override;
-  void GenerateImportFile(cmExportSet const* exportSet);
-  void GenerateImportFile(const char* config, cmExportSet const* exportSet);
   std::string TempDirCalculate() const;
   void ComputeTempDir();
 
@@ -70,8 +69,6 @@ protected:
   std::string const FileName;
   std::string const Namespace;
   std::string const CxxModulesDirectory;
-  bool const ExportOld;
-  bool const ExportPackageDependencies;
   cmLocalGenerator* LocalGenerator = nullptr;
 
   std::string TempDir;

+ 4 - 0
bootstrap

@@ -345,8 +345,11 @@ CMAKE_CXX_SOURCES="\
   cmExecuteProcessCommand \
   cmExpandedCommandArgument \
   cmExperimental \
+  cmExportBuildCMakeConfigGenerator \
   cmExportBuildFileGenerator \
+  cmExportCMakeConfigGenerator \
   cmExportFileGenerator \
+  cmExportInstallCMakeConfigGenerator \
   cmExportInstallFileGenerator \
   cmExportSet \
   cmExportTryCompileFileGenerator \
@@ -408,6 +411,7 @@ CMAKE_CXX_SOURCES="\
   cmIncludeGuardCommand \
   cmIncludeDirectoryCommand \
   cmIncludeRegularExpressionCommand \
+  cmInstallCMakeConfigExportGenerator \
   cmInstallCommand \
   cmInstallCommandArguments \
   cmInstallCxxModuleBmiGenerator \

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно