Przeglądaj źródła

export: Add build-tree CPS support

Add Common Package Specification support to export(EXPORT).
Matthew Woehlke 7 miesięcy temu
rodzic
commit
c8ccd5a9e3
91 zmienionych plików z 1031 dodań i 27 usunięć
  1. 35 2
      Help/command/export.rst
  2. 2 0
      Source/CMakeLists.txt
  3. 119 0
      Source/cmExportBuildPackageInfoGenerator.cxx
  4. 53 0
      Source/cmExportBuildPackageInfoGenerator.h
  5. 118 6
      Source/cmExportCommand.cxx
  6. 10 8
      Source/cmExportFileGenerator.cxx
  7. 2 0
      Source/cmExportFileGenerator.h
  8. 6 3
      Source/cmExportInstallPackageInfoGenerator.cxx
  9. 4 6
      Source/cmExportPackageInfoGenerator.cxx
  10. 1 2
      Source/cmExportPackageInfoGenerator.h
  11. 2 0
      Tests/RunCMake/CMakeLists.txt
  12. 3 0
      Tests/RunCMake/CpsExportImportBuild/CMakeLists.txt
  13. 23 0
      Tests/RunCMake/CpsExportImportBuild/RunCMakeTest.cmake
  14. 12 0
      Tests/RunCMake/CpsExportImportBuild/TestExecutable.cmake
  15. 12 0
      Tests/RunCMake/CpsExportImportBuild/TestLibrary.cmake
  16. 13 0
      Tests/RunCMake/CpsExportImportBuild/app.c
  17. 7 0
      Tests/RunCMake/CpsExportImportBuild/liba.c
  18. 13 0
      Tests/RunCMake/CpsExportImportBuild/libb.c
  19. 16 0
      Tests/RunCMake/ExportPackageInfo/Appendix-check.cmake
  20. 9 0
      Tests/RunCMake/ExportPackageInfo/Appendix.cmake
  21. 34 0
      Tests/RunCMake/ExportPackageInfo/Assertions.cmake
  22. 1 0
      Tests/RunCMake/ExportPackageInfo/BadArgs1-result.txt
  23. 10 0
      Tests/RunCMake/ExportPackageInfo/BadArgs1-stderr.txt
  24. 4 0
      Tests/RunCMake/ExportPackageInfo/BadArgs1.cmake
  25. 1 0
      Tests/RunCMake/ExportPackageInfo/BadArgs2-result.txt
  26. 16 0
      Tests/RunCMake/ExportPackageInfo/BadArgs2-stderr.txt
  27. 5 0
      Tests/RunCMake/ExportPackageInfo/BadArgs2.cmake
  28. 1 0
      Tests/RunCMake/ExportPackageInfo/BadArgs3-result.txt
  29. 10 0
      Tests/RunCMake/ExportPackageInfo/BadArgs3-stderr.txt
  30. 4 0
      Tests/RunCMake/ExportPackageInfo/BadArgs3.cmake
  31. 1 0
      Tests/RunCMake/ExportPackageInfo/BadArgs4-result.txt
  32. 28 0
      Tests/RunCMake/ExportPackageInfo/BadArgs4-stderr.txt
  33. 7 0
      Tests/RunCMake/ExportPackageInfo/BadArgs4.cmake
  34. 1 0
      Tests/RunCMake/ExportPackageInfo/BadDefaultTarget-result.txt
  35. 8 0
      Tests/RunCMake/ExportPackageInfo/BadDefaultTarget-stderr.txt
  36. 5 0
      Tests/RunCMake/ExportPackageInfo/BadDefaultTarget.cmake
  37. 1 0
      Tests/RunCMake/ExportPackageInfo/BadName-result.txt
  38. 4 0
      Tests/RunCMake/ExportPackageInfo/BadName-stderr.txt
  39. 3 0
      Tests/RunCMake/ExportPackageInfo/BadName.cmake
  40. 3 0
      Tests/RunCMake/ExportPackageInfo/CMakeLists.txt
  41. 3 0
      Tests/RunCMake/ExportPackageInfo/DependsMultiple.cmake
  42. 11 0
      Tests/RunCMake/ExportPackageInfo/DependsMultipleCommon.cmake
  43. 1 0
      Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentNamespace-result.txt
  44. 22 0
      Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentNamespace-stderr.txt
  45. 3 0
      Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentNamespace.cmake
  46. 1 0
      Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentSets-result.txt
  47. 22 0
      Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentSets-stderr.txt
  48. 5 0
      Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentSets.cmake
  49. 1 0
      Tests/RunCMake/ExportPackageInfo/DuplicateOutput-result.txt
  50. 4 0
      Tests/RunCMake/ExportPackageInfo/DuplicateOutput-stderr.txt
  51. 4 0
      Tests/RunCMake/ExportPackageInfo/DuplicateOutput.cmake
  52. 1 0
      Tests/RunCMake/ExportPackageInfo/ExperimentalGate-result.txt
  53. 2 0
      Tests/RunCMake/ExportPackageInfo/ExperimentalGate-stderr.txt
  54. 5 0
      Tests/RunCMake/ExportPackageInfo/ExperimentalGate.cmake
  55. 7 0
      Tests/RunCMake/ExportPackageInfo/ExperimentalWarning-stderr.txt
  56. 8 0
      Tests/RunCMake/ExportPackageInfo/ExperimentalWarning.cmake
  57. 24 0
      Tests/RunCMake/ExportPackageInfo/InterfaceProperties-check.cmake
  58. 15 0
      Tests/RunCMake/ExportPackageInfo/InterfaceProperties.cmake
  59. 9 0
      Tests/RunCMake/ExportPackageInfo/LowerCaseFile-check.cmake
  60. 4 0
      Tests/RunCMake/ExportPackageInfo/LowerCaseFile.cmake
  61. 16 0
      Tests/RunCMake/ExportPackageInfo/Metadata-check.cmake
  62. 11 0
      Tests/RunCMake/ExportPackageInfo/Metadata.cmake
  63. 18 0
      Tests/RunCMake/ExportPackageInfo/Minimal-check.cmake
  64. 3 0
      Tests/RunCMake/ExportPackageInfo/Minimal.cmake
  65. 21 0
      Tests/RunCMake/ExportPackageInfo/MinimalVersion-check.cmake
  66. 16 0
      Tests/RunCMake/ExportPackageInfo/MinimalVersion.cmake
  67. 1 0
      Tests/RunCMake/ExportPackageInfo/ReferencesNonExportedTarget-result.txt
  68. 3 0
      Tests/RunCMake/ExportPackageInfo/ReferencesNonExportedTarget-stderr.txt
  69. 6 0
      Tests/RunCMake/ExportPackageInfo/ReferencesNonExportedTarget.cmake
  70. 1 0
      Tests/RunCMake/ExportPackageInfo/ReferencesWronglyExportedTarget-result.txt
  71. 7 0
      Tests/RunCMake/ExportPackageInfo/ReferencesWronglyExportedTarget-stderr.txt
  72. 14 0
      Tests/RunCMake/ExportPackageInfo/ReferencesWronglyExportedTarget.cmake
  73. 1 0
      Tests/RunCMake/ExportPackageInfo/ReferencesWronglyImportedTarget-result.txt
  74. 3 0
      Tests/RunCMake/ExportPackageInfo/ReferencesWronglyImportedTarget-stderr.txt
  75. 7 0
      Tests/RunCMake/ExportPackageInfo/ReferencesWronglyImportedTarget.cmake
  76. 1 0
      Tests/RunCMake/ExportPackageInfo/ReferencesWronglyNamespacedTarget-result.txt
  77. 4 0
      Tests/RunCMake/ExportPackageInfo/ReferencesWronglyNamespacedTarget-stderr.txt
  78. 11 0
      Tests/RunCMake/ExportPackageInfo/ReferencesWronglyNamespacedTarget.cmake
  79. 22 0
      Tests/RunCMake/ExportPackageInfo/Requirements-check.cmake
  80. 20 0
      Tests/RunCMake/ExportPackageInfo/Requirements.cmake
  81. 37 0
      Tests/RunCMake/ExportPackageInfo/RunCMakeTest.cmake
  82. 11 0
      Tests/RunCMake/ExportPackageInfo/TargetTypes-check.cmake
  83. 20 0
      Tests/RunCMake/ExportPackageInfo/TargetTypes.cmake
  84. 1 0
      Tests/RunCMake/ExportPackageInfo/broken-config.cmake
  85. 3 0
      Tests/RunCMake/ExportPackageInfo/foo.cxx
  86. 1 0
      Tests/RunCMake/ExportPackageInfo/test-config.cmake
  87. 4 0
      Tests/RunCMake/ExportPackageInfo/test.cxx
  88. 1 0
      Tests/RunCMake/InstallPackageInfo/BadName-result.txt
  89. 4 0
      Tests/RunCMake/InstallPackageInfo/BadName-stderr.txt
  90. 3 0
      Tests/RunCMake/InstallPackageInfo/BadName.cmake
  91. 1 0
      Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake

+ 35 - 2
Help/command/export.rst

@@ -89,10 +89,10 @@ Exporting Targets to Android.mk
 
 
 .. versionadded:: 3.7
 .. versionadded:: 3.7
 
 
-This signature exports cmake built targets to the android ndk build system
+This signature exports CMake built targets to the android ndk build system
 by creating an ``Android.mk`` file that references the prebuilt targets. The
 by creating an ``Android.mk`` file that references the prebuilt targets. The
 Android NDK supports the use of prebuilt libraries, both static and shared.
 Android NDK supports the use of prebuilt libraries, both static and shared.
-This allows cmake to build the libraries of a project and make them available
+This allows CMake to build the libraries of a project and make them available
 to an ndk build system complete with transitive dependencies, include flags
 to an ndk build system complete with transitive dependencies, include flags
 and defines required to use the libraries. The signature takes a list of
 and defines required to use the libraries. The signature takes a list of
 targets and puts them in the ``Android.mk`` file specified by the
 targets and puts them in the ``Android.mk`` file specified by the
@@ -127,6 +127,36 @@ of the :command:`install(TARGETS)` command.
   Specify that :command:`find_dependency` calls should be exported. See
   Specify that :command:`find_dependency` calls should be exported. See
   :command:`install(EXPORT)` for details on how this works.
   :command:`install(EXPORT)` for details on how this works.
 
 
+Exporting Targets to the |CPS|
+""""""""""""""""""""""""""""""
+
+.. code-block:: cmake
+
+  export(EXPORT <export-name> PACKAGE_INFO <package-name>
+         [APPENDIX <appendix-name>]
+         [LOWER_CASE_FILE]
+         [VERSION <version>
+          [COMPAT_VERSION <version>]
+          [VERSION_SCHEMA <string>]]
+         [DEFAULT_TARGETS <target>...]
+         [DEFAULT_CONFIGURATIONS <config>...])
+
+.. versionadded:: 4.1
+.. note::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO``.
+
+Creates a file in the |CPS|_ that may be included by outside projects to import
+targets named by ``<target>...`` from the current project's build tree.  See
+the :command:`install(PACKAGE_INFO)` command to export targets from an install
+tree.  The imported targets are implicitly in the namespace ``<package-name>``.
+
+The default file name is ``<package-name>[-<appendix-name>].cps``. If the
+``LOWER_CASE_FILE`` option is given, the file name will use the package name
+converted to lower case.
+
+See :command:`install(PACKAGE_INFO)` for a description of the other options.
+
 Exporting Packages
 Exporting Packages
 ^^^^^^^^^^^^^^^^^^
 ^^^^^^^^^^^^^^^^^^
 
 
@@ -221,3 +251,6 @@ Configure the parameters of an export. The arguments are as follows:
     this target. If specified, the generated code will check to see if the
     this target. If specified, the generated code will check to see if the
     ``.xcframework`` exists, and if it does, it will use the ``.xcframework``
     ``.xcframework`` exists, and if it does, it will use the ``.xcframework``
     as its imported location instead of the installed library.
     as its imported location instead of the installed library.
+
+.. _CPS: https://cps-org.github.io/cps/
+.. |CPS| replace:: Common Package Specification

+ 2 - 0
Source/CMakeLists.txt

@@ -203,6 +203,8 @@ add_library(
   cmExportBuildCMakeConfigGenerator.cxx
   cmExportBuildCMakeConfigGenerator.cxx
   cmExportBuildFileGenerator.h
   cmExportBuildFileGenerator.h
   cmExportBuildFileGenerator.cxx
   cmExportBuildFileGenerator.cxx
+  cmExportBuildPackageInfoGenerator.h
+  cmExportBuildPackageInfoGenerator.cxx
   cmExportCMakeConfigGenerator.h
   cmExportCMakeConfigGenerator.h
   cmExportCMakeConfigGenerator.cxx
   cmExportCMakeConfigGenerator.cxx
   cmExportFileGenerator.h
   cmExportFileGenerator.h

+ 119 - 0
Source/cmExportBuildPackageInfoGenerator.cxx

@@ -0,0 +1,119 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmExportBuildPackageInfoGenerator.h"
+
+#include <cassert>
+#include <utility>
+#include <vector>
+
+#include <cmext/string_view>
+
+#include <cm3p/json/value.h>
+
+#include "cmGeneratorExpression.h"
+#include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
+
+cmExportBuildPackageInfoGenerator::cmExportBuildPackageInfoGenerator(
+  std::string packageName, std::string version, std::string versionCompat,
+  std::string versionSchema, std::vector<std::string> defaultTargets,
+  std::vector<std::string> defaultConfigurations)
+  : cmExportPackageInfoGenerator(
+      std::move(packageName), std::move(version), std::move(versionCompat),
+      std::move(versionSchema), std::move(defaultTargets),
+      std::move(defaultConfigurations))
+{
+  this->SetNamespace(cmStrCat(this->GetPackageName(), "::"_s));
+}
+
+bool cmExportBuildPackageInfoGenerator::GenerateMainFile(std::ostream& os)
+{
+  if (!this->CollectExports([&](cmGeneratorTarget const*) {})) {
+    return false;
+  }
+
+  if (!this->CheckDefaultTargets()) {
+    return false;
+  }
+
+  Json::Value root = this->GeneratePackageInfo();
+  root["cps_path"] = "@prefix@";
+
+  Json::Value& components = root["components"];
+
+  // Create all the imported targets.
+  for (auto const& exp : this->Exports) {
+    cmGeneratorTarget* const target = exp.Target;
+    cmStateEnums::TargetType targetType = this->GetExportTargetType(target);
+
+    Json::Value* const component =
+      this->GenerateImportTarget(components, target, targetType);
+    if (!component) {
+      return false;
+    }
+
+    ImportPropertyMap properties;
+    if (!this->PopulateInterfaceProperties(target, properties)) {
+      return false;
+    }
+    this->PopulateInterfaceLinkLibrariesProperty(
+      target, cmGeneratorExpression::InstallInterface, properties);
+
+    if (targetType != cmStateEnums::INTERFACE_LIBRARY) {
+      auto configurations = Json::Value{ Json::objectValue };
+
+      // Add per-configuration properties.
+      for (std::string const& c : this->Configurations) {
+        this->GenerateInterfacePropertiesConfig(configurations, target, c);
+      }
+
+      if (!configurations.empty()) {
+        (*component)["configurations"] = configurations;
+      }
+    }
+
+    // Set configuration-agnostic properties for component.
+    this->GenerateInterfaceProperties(*component, target, properties);
+  }
+
+  this->GeneratePackageRequires(root);
+
+  // Write the primary packing information file.
+  this->WritePackageInfo(root, os);
+
+  bool result = true;
+
+  return result;
+}
+
+void cmExportBuildPackageInfoGenerator::GenerateInterfacePropertiesConfig(
+  Json::Value& configurations, cmGeneratorTarget* target,
+  std::string const& config)
+{
+  std::string const& suffix = PropertyConfigSuffix(config);
+
+  ImportPropertyMap properties;
+
+  assert(this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY);
+  this->SetImportLocationProperty(config, suffix, target, properties);
+  if (properties.empty()) {
+    return;
+  }
+
+  this->SetImportDetailProperties(config, suffix, target, properties);
+
+  // TODO: PUBLIC_HEADER_LOCATION
+
+  Json::Value component =
+    this->GenerateInterfaceConfigProperties(suffix, properties);
+  if (!component.empty()) {
+    configurations[config] = std::move(component);
+  }
+}
+
+std::string cmExportBuildPackageInfoGenerator::GetCxxModulesDirectory() const
+{
+  // TODO: Implement a not-CMake-specific mechanism for providing module
+  // information.
+  return {};
+}

+ 53 - 0
Source/cmExportBuildPackageInfoGenerator.h

@@ -0,0 +1,53 @@
+/* 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 <vector>
+
+#include "cmExportBuildFileGenerator.h"
+#include "cmExportPackageInfoGenerator.h"
+
+namespace Json {
+class Value;
+}
+
+class cmGeneratorTarget;
+
+/** \class cmExportBuildPackageInfoGenerator
+ * \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 the Common Package Specification
+ * (https://cps-org.github.io/cps/).
+ *
+ * This is used to implement the export() command.
+ */
+class cmExportBuildPackageInfoGenerator
+  : public cmExportBuildFileGenerator
+  , public cmExportPackageInfoGenerator
+{
+public:
+  cmExportBuildPackageInfoGenerator(
+    std::string packageName, std::string version, std::string versionCompat,
+    std::string versionSchema, std::vector<std::string> defaultTargets,
+    std::vector<std::string> defaultConfigurations);
+
+protected:
+  // Implement virtual methods from the superclass.
+  bool GenerateMainFile(std::ostream& os) override;
+  void GenerateImportTargetsConfig(std::ostream&, std::string const&,
+                                   std::string const&) override
+  {
+  }
+
+  void GenerateInterfacePropertiesConfig(Json::Value& configurations,
+                                         cmGeneratorTarget* target,
+                                         std::string const& config);
+
+  std::string GetCxxModulesDirectory() const override;
+  // TODO: Generate C++ module info in a not-CMake-specific format.
+};

+ 118 - 6
Source/cmExportCommand.cxx

@@ -21,8 +21,10 @@
 #include "cmExportBuildAndroidMKGenerator.h"
 #include "cmExportBuildAndroidMKGenerator.h"
 #include "cmExportBuildCMakeConfigGenerator.h"
 #include "cmExportBuildCMakeConfigGenerator.h"
 #include "cmExportBuildFileGenerator.h"
 #include "cmExportBuildFileGenerator.h"
+#include "cmExportBuildPackageInfoGenerator.h"
 #include "cmExportSet.h"
 #include "cmExportSet.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratedFileStream.h"
+#include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmMessageType.h"
@@ -68,9 +70,17 @@ bool cmExportCommand(std::vector<std::string> const& args,
     ArgumentParser::NonEmpty<std::string> Namespace;
     ArgumentParser::NonEmpty<std::string> Namespace;
     ArgumentParser::NonEmpty<std::string> Filename;
     ArgumentParser::NonEmpty<std::string> Filename;
     ArgumentParser::NonEmpty<std::string> AndroidMKFile;
     ArgumentParser::NonEmpty<std::string> AndroidMKFile;
+    ArgumentParser::NonEmpty<std::string> PackageName;
+    ArgumentParser::NonEmpty<std::string> Appendix;
+    ArgumentParser::NonEmpty<std::string> Version;
+    ArgumentParser::NonEmpty<std::string> VersionCompat;
+    ArgumentParser::NonEmpty<std::string> VersionSchema;
     ArgumentParser::NonEmpty<std::string> CxxModulesDirectory;
     ArgumentParser::NonEmpty<std::string> CxxModulesDirectory;
+    ArgumentParser::NonEmpty<std::vector<std::string>> DefaultTargets;
+    ArgumentParser::NonEmpty<std::vector<std::string>> DefaultConfigs;
     bool Append = false;
     bool Append = false;
     bool ExportOld = false;
     bool ExportOld = false;
+    bool LowerCase = false;
 
 
     std::vector<std::vector<std::string>> PackageDependencyArgs;
     std::vector<std::vector<std::string>> PackageDependencyArgs;
     bool ExportPackageDependencies = false;
     bool ExportPackageDependencies = false;
@@ -92,6 +102,17 @@ bool cmExportCommand(std::vector<std::string> const& args,
       parser.Bind("EXPORT_PACKAGE_DEPENDENCIES"_s,
       parser.Bind("EXPORT_PACKAGE_DEPENDENCIES"_s,
                   &Arguments::ExportPackageDependencies);
                   &Arguments::ExportPackageDependencies);
     }
     }
+    if (cmExperimental::HasSupportEnabled(
+          status.GetMakefile(), cmExperimental::Feature::ExportPackageInfo)) {
+      parser.Bind("PACKAGE_INFO"_s, &Arguments::PackageName);
+      parser.Bind("LOWER_CASE_FILE"_s, &Arguments::LowerCase);
+      parser.Bind("APPENDIX"_s, &Arguments::Appendix);
+      parser.Bind("VERSION"_s, &Arguments::Version);
+      parser.Bind("COMPAT_VERSION"_s, &Arguments::VersionCompat);
+      parser.Bind("VERSION_SCHEMA"_s, &Arguments::VersionSchema);
+      parser.Bind("DEFAULT_TARGETS"_s, &Arguments::DefaultTargets);
+      parser.Bind("DEFAULT_CONFIGURATIONS"_s, &Arguments::DefaultConfigs);
+    }
   } else if (args[0] == "SETUP") {
   } else if (args[0] == "SETUP") {
     parser.Bind("SETUP"_s, &Arguments::ExportSetName);
     parser.Bind("SETUP"_s, &Arguments::ExportSetName);
     if (cmExperimental::HasSupportEnabled(
     if (cmExperimental::HasSupportEnabled(
@@ -200,19 +221,98 @@ bool cmExportCommand(std::vector<std::string> const& args,
     return true;
     return true;
   }
   }
 
 
+  if (arguments.PackageName.empty()) {
+    if (arguments.LowerCase) {
+      status.SetError("LOWER_CASE_FILE requires PACKAGE_INFO.");
+      return false;
+    }
+    if (!arguments.Appendix.empty()) {
+      status.SetError("APPENDIX requires PACKAGE_INFO.");
+      return false;
+    }
+    if (!arguments.Version.empty()) {
+      status.SetError("VERSION requires PACKAGE_INFO.");
+      return false;
+    }
+    if (!arguments.DefaultTargets.empty()) {
+      status.SetError("DEFAULT_TARGETS requires PACKAGE_INFO.");
+      return false;
+    }
+    if (!arguments.DefaultConfigs.empty()) {
+      status.SetError("DEFAULT_CONFIGURATIONS requires PACKAGE_INFO.");
+      return false;
+    }
+  } else {
+    if (!arguments.Filename.empty()) {
+      status.SetError("PACKAGE_INFO and FILE are mutually exclusive.");
+      return false;
+    }
+    if (!arguments.Namespace.empty()) {
+      status.SetError("PACKAGE_INFO and NAMESPACE are mutually exclusive.");
+      return false;
+    }
+    if (!arguments.Appendix.empty()) {
+      if (!arguments.Version.empty()) {
+        status.SetError("APPENDIX and VERSION are mutually exclusive.");
+        return false;
+      }
+      if (!arguments.DefaultTargets.empty()) {
+        status.SetError("APPENDIX and DEFAULT_TARGETS "
+                        "are mutually exclusive.");
+        return false;
+      }
+      if (!arguments.DefaultConfigs.empty()) {
+        status.SetError("APPENDIX and DEFAULT_CONFIGURATIONS "
+                        "are mutually exclusive.");
+        return false;
+      }
+    }
+  }
+  if (arguments.Version.empty()) {
+    if (!arguments.VersionCompat.empty()) {
+      status.SetError("COMPAT_VERSION requires VERSION.");
+      return false;
+    }
+    if (!arguments.VersionSchema.empty()) {
+      status.SetError("VERSION_SCHEMA requires VERSION.");
+      return false;
+    }
+  }
+
   std::string fname;
   std::string fname;
   bool android = false;
   bool android = false;
+  bool cps = false;
   if (!arguments.AndroidMKFile.empty()) {
   if (!arguments.AndroidMKFile.empty()) {
     fname = arguments.AndroidMKFile;
     fname = arguments.AndroidMKFile;
     android = true;
     android = true;
-  }
-  if (arguments.Filename.empty() && fname.empty()) {
+  } else if (arguments.Filename.empty()) {
     if (args[0] != "EXPORT") {
     if (args[0] != "EXPORT") {
       status.SetError("FILE <filename> option missing.");
       status.SetError("FILE <filename> option missing.");
       return false;
       return false;
     }
     }
-    fname = arguments.ExportSetName + ".cmake";
-  } else if (fname.empty()) {
+    if (arguments.PackageName.empty()) {
+      fname = arguments.ExportSetName + ".cmake";
+    } else {
+      // Validate the package name.
+      if (!cmGeneratorExpression::IsValidTargetName(arguments.PackageName) ||
+          arguments.PackageName.find(':') != std::string::npos) {
+        status.SetError(
+          cmStrCat(R"(PACKAGE_INFO given invalid package name ")"_s,
+                   arguments.PackageName, R"(".)"_s));
+        return false;
+      }
+
+      std::string const pkgNameOnDisk =
+        (arguments.LowerCase ? cmSystemTools::LowerCase(arguments.PackageName)
+                             : std::string{ arguments.PackageName });
+      if (arguments.Appendix.empty()) {
+        fname = cmStrCat(pkgNameOnDisk, ".cps"_s);
+      } else {
+        fname = cmStrCat(pkgNameOnDisk, '-', arguments.Appendix, ".cps"_s);
+      }
+      cps = true;
+    }
+  } else {
     // Make sure the file has a .cmake extension.
     // Make sure the file has a .cmake extension.
     if (cmSystemTools::GetFilenameLastExtension(arguments.Filename) !=
     if (cmSystemTools::GetFilenameLastExtension(arguments.Filename) !=
         ".cmake") {
         ".cmake") {
@@ -298,6 +398,11 @@ bool cmExportCommand(std::vector<std::string> const& args,
   // and APPEND is not specified, if CMP0103 is OLD ignore previous definition
   // and APPEND is not specified, if CMP0103 is OLD ignore previous definition
   // else raise an error
   // else raise an error
   if (gg->GetExportedTargetsFile(fname)) {
   if (gg->GetExportedTargetsFile(fname)) {
+    if (cps) {
+      status.SetError(cmStrCat("command already specified for the file "_s,
+                               cmSystemTools::GetFilenameName(fname), '.'));
+      return false;
+    }
     switch (mf.GetPolicyStatus(cmPolicies::CMP0103)) {
     switch (mf.GetPolicyStatus(cmPolicies::CMP0103)) {
       case cmPolicies::WARN:
       case cmPolicies::WARN:
         mf.IssueMessage(
         mf.IssueMessage(
@@ -316,21 +421,28 @@ bool cmExportCommand(std::vector<std::string> const& args,
     }
     }
   }
   }
 
 
-  // Setup export file generation.
+  // Set up export file generation.
   std::unique_ptr<cmExportBuildFileGenerator> ebfg = nullptr;
   std::unique_ptr<cmExportBuildFileGenerator> ebfg = nullptr;
   if (android) {
   if (android) {
     auto ebag = cm::make_unique<cmExportBuildAndroidMKGenerator>();
     auto ebag = cm::make_unique<cmExportBuildAndroidMKGenerator>();
+    ebag->SetNamespace(arguments.Namespace);
     ebag->SetAppendMode(arguments.Append);
     ebag->SetAppendMode(arguments.Append);
     ebfg = std::move(ebag);
     ebfg = std::move(ebag);
+  } else if (cps) {
+    auto ebpg = cm::make_unique<cmExportBuildPackageInfoGenerator>(
+      arguments.PackageName, arguments.Version, arguments.VersionCompat,
+      arguments.VersionSchema, arguments.DefaultTargets,
+      arguments.DefaultConfigs);
+    ebfg = std::move(ebpg);
   } else {
   } else {
     auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>();
     auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>();
+    ebcg->SetNamespace(arguments.Namespace);
     ebcg->SetAppendMode(arguments.Append);
     ebcg->SetAppendMode(arguments.Append);
     ebcg->SetExportOld(arguments.ExportOld);
     ebcg->SetExportOld(arguments.ExportOld);
     ebcg->SetExportPackageDependencies(arguments.ExportPackageDependencies);
     ebcg->SetExportPackageDependencies(arguments.ExportPackageDependencies);
     ebfg = std::move(ebcg);
     ebfg = std::move(ebcg);
   }
   }
   ebfg->SetExportFile(fname.c_str());
   ebfg->SetExportFile(fname.c_str());
-  ebfg->SetNamespace(arguments.Namespace);
   ebfg->SetCxxModuleDirectory(arguments.CxxModulesDirectory);
   ebfg->SetCxxModuleDirectory(arguments.CxxModulesDirectory);
   if (exportSet) {
   if (exportSet) {
     ebfg->SetExportSet(exportSet);
     ebfg->SetExportSet(exportSet);

+ 10 - 8
Source/cmExportFileGenerator.cxx

@@ -78,19 +78,21 @@ bool cmExportFileGenerator::GenerateImportFile()
   return this->GenerateImportFile(*foutPtr);
   return this->GenerateImportFile(*foutPtr);
 }
 }
 
 
-void cmExportFileGenerator::GenerateImportConfig(std::ostream& os,
-                                                 std::string const& config)
+std::string cmExportFileGenerator::PropertyConfigSuffix(
+  std::string const& config)
 {
 {
   // Construct the property configuration suffix.
   // Construct the property configuration suffix.
-  std::string suffix = "_";
-  if (!config.empty()) {
-    suffix += cmSystemTools::UpperCase(config);
-  } else {
-    suffix += "NOCONFIG";
+  if (config.empty()) {
+    return "_NOCONFIG";
   }
   }
+  return cmStrCat('_', cmSystemTools::UpperCase(config));
+}
 
 
+void cmExportFileGenerator::GenerateImportConfig(std::ostream& os,
+                                                 std::string const& config)
+{
   // Generate the per-config target information.
   // Generate the per-config target information.
-  this->GenerateImportTargetsConfig(os, config, suffix);
+  this->GenerateImportTargetsConfig(os, config, PropertyConfigSuffix(config));
 }
 }
 
 
 bool cmExportFileGenerator::PopulateInterfaceProperties(
 bool cmExportFileGenerator::PopulateInterfaceProperties(

+ 2 - 0
Source/cmExportFileGenerator.h

@@ -170,6 +170,8 @@ protected:
   bool AddTargetNamespace(std::string& input, cmGeneratorTarget const* target,
   bool AddTargetNamespace(std::string& input, cmGeneratorTarget const* target,
                           cmLocalGenerator const* lg);
                           cmLocalGenerator const* lg);
 
 
+  static std::string PropertyConfigSuffix(std::string const& config);
+
   // The namespace in which the exports are placed in the generated file.
   // The namespace in which the exports are placed in the generated file.
   std::string Namespace;
   std::string Namespace;
 
 

+ 6 - 3
Source/cmExportInstallPackageInfoGenerator.cxx

@@ -71,8 +71,8 @@ bool cmExportInstallPackageInfoGenerator::GenerateMainFile(std::ostream& os)
   }
   }
   root["cps_path"] = packagePath;
   root["cps_path"] = packagePath;
 
 
-  bool requiresConfigFiles = false;
   // Create all the imported targets.
   // Create all the imported targets.
+  bool requiresConfigFiles = false;
   for (cmTargetExport const* te : allTargets) {
   for (cmTargetExport const* te : allTargets) {
     cmGeneratorTarget* gt = te->Target;
     cmGeneratorTarget* gt = te->Target;
     cmStateEnums::TargetType targetType = this->GetExportTargetType(te);
     cmStateEnums::TargetType targetType = this->GetExportTargetType(te);
@@ -139,8 +139,11 @@ void cmExportInstallPackageInfoGenerator::GenerateImportTargetsConfig(
     this->PopulateImportProperties(config, suffix, te.get(), properties,
     this->PopulateImportProperties(config, suffix, te.get(), properties,
                                    importedLocations);
                                    importedLocations);
 
 
-    this->GenerateInterfaceConfigProperties(components, te->Target, suffix,
-                                            properties);
+    Json::Value component =
+      this->GenerateInterfaceConfigProperties(suffix, properties);
+    if (!component.empty()) {
+      components[te->Target->GetExportName()] = std::move(component);
+    }
   }
   }
 
 
   this->WritePackageInfo(root, os);
   this->WritePackageInfo(root, os);

+ 4 - 6
Source/cmExportPackageInfoGenerator.cxx

@@ -277,7 +277,7 @@ bool cmExportPackageInfoGenerator::NoteLinkedTarget(
     return true;
     return true;
   }
   }
 
 
-  // Target belongs to multiple namespaces or multiple export sets.
+  // Target belongs to another export from this build.
   auto const& exportInfo = this->FindExportInfo(linkedTarget);
   auto const& exportInfo = this->FindExportInfo(linkedTarget);
   if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) {
   if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) {
     auto const& linkNamespace = *exportInfo.Namespaces.begin();
     auto const& linkNamespace = *exportInfo.Namespaces.begin();
@@ -302,6 +302,7 @@ bool cmExportPackageInfoGenerator::NoteLinkedTarget(
     return true;
     return true;
   }
   }
 
 
+  // Target belongs to multiple namespaces or multiple export sets.
   // cmExportFileGenerator::HandleMissingTarget should have complained about
   // cmExportFileGenerator::HandleMissingTarget should have complained about
   // this already.
   // this already.
   return false;
   return false;
@@ -424,8 +425,7 @@ void cmExportPackageInfoGenerator::GenerateInterfaceListProperty(
   }
   }
 }
 }
 
 
-void cmExportPackageInfoGenerator::GenerateInterfaceConfigProperties(
-  Json::Value& components, cmGeneratorTarget const* target,
+Json::Value cmExportPackageInfoGenerator::GenerateInterfaceConfigProperties(
   std::string const& suffix, ImportPropertyMap const& properties) const
   std::string const& suffix, ImportPropertyMap const& properties) const
 {
 {
   Json::Value component;
   Json::Value component;
@@ -456,7 +456,5 @@ void cmExportPackageInfoGenerator::GenerateInterfaceConfigProperties(
     }
     }
   }
   }
 
 
-  if (!component.empty()) {
-    components[target->GetExportName()] = component;
-  }
+  return component;
 }
 }

+ 1 - 2
Source/cmExportPackageInfoGenerator.h

@@ -62,8 +62,7 @@ protected:
   bool GenerateInterfaceProperties(Json::Value& component,
   bool GenerateInterfaceProperties(Json::Value& component,
                                    cmGeneratorTarget const* target,
                                    cmGeneratorTarget const* target,
                                    ImportPropertyMap const& properties) const;
                                    ImportPropertyMap const& properties) const;
-  void GenerateInterfaceConfigProperties(
-    Json::Value& components, cmGeneratorTarget const* target,
+  Json::Value GenerateInterfaceConfigProperties(
     std::string const& suffix, ImportPropertyMap const& properties) const;
     std::string const& suffix, ImportPropertyMap const& properties) const;
 
 
   cm::string_view GetImportPrefixWithSlash() const override;
   cm::string_view GetImportPrefixWithSlash() const override;

+ 2 - 0
Tests/RunCMake/CMakeLists.txt

@@ -397,6 +397,7 @@ set_property(TEST RunCMake.CompilerId APPEND PROPERTY LABELS "CUDA" "HIP" "ISPC"
 add_RunCMake_test(CompilerTest ${CMake_TEST_LANG_VARS})
 add_RunCMake_test(CompilerTest ${CMake_TEST_LANG_VARS})
 set_property(TEST RunCMake.CompilerTest APPEND PROPERTY LABELS "CUDA" "HIP" "ISPC" "Fortran")
 set_property(TEST RunCMake.CompilerTest APPEND PROPERTY LABELS "CUDA" "HIP" "ISPC" "Fortran")
 add_RunCMake_test(Configure -DMSVC_IDE=${MSVC_IDE})
 add_RunCMake_test(Configure -DMSVC_IDE=${MSVC_IDE})
+add_RunCMake_test(CpsExportImportBuild)
 add_RunCMake_test(CpsExportImportInstall)
 add_RunCMake_test(CpsExportImportInstall)
 add_RunCMake_test(DisallowedCommands)
 add_RunCMake_test(DisallowedCommands)
 if("${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja")
 if("${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja")
@@ -1205,6 +1206,7 @@ add_RunCMake_test(AutoExportDll
   )
   )
 
 
 add_RunCMake_test(AndroidMK)
 add_RunCMake_test(AndroidMK)
+add_RunCMake_test(ExportPackageInfo)
 add_RunCMake_test(InstallPackageInfo)
 add_RunCMake_test(InstallPackageInfo)
 
 
 if(CMake_TEST_ANDROID_NDK OR CMake_TEST_ANDROID_STANDALONE_TOOLCHAIN)
 if(CMake_TEST_ANDROID_NDK OR CMake_TEST_ANDROID_STANDALONE_TOOLCHAIN)

+ 3 - 0
Tests/RunCMake/CpsExportImportBuild/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 4.0.20250220)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 23 - 0
Tests/RunCMake/CpsExportImportBuild/RunCMakeTest.cmake

@@ -0,0 +1,23 @@
+include(RunCMake)
+
+set(RunCMake_TEST_OPTIONS
+  -Wno-dev
+  "-DCMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO:STRING=b80be207-778e-46ba-8080-b23bba22639e"
+  "-DCMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES:STRING=e82e467b-f997-4464-8ace-b00808fff261"
+  )
+
+function(build_project test)
+  set(RunCMake_TEST_NO_CLEAN FALSE)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+  if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+  endif()
+
+  run_cmake(${test})
+
+  set(RunCMake_TEST_NO_CLEAN TRUE)
+  run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release)
+endfunction()
+
+build_project(TestLibrary)
+build_project(TestExecutable)

+ 12 - 0
Tests/RunCMake/CpsExportImportBuild/TestExecutable.cmake

@@ -0,0 +1,12 @@
+project(TestLibrary C)
+
+set(liba_DIR "${CMAKE_BINARY_DIR}/../TestLibrary-build")
+set(libb_DIR "${CMAKE_BINARY_DIR}/../TestLibrary-build")
+
+find_package(libb REQUIRED COMPONENTS libb)
+
+add_executable(app app.c)
+
+target_link_libraries(app PUBLIC libb::libb)
+
+install(TARGETS app DESTINATION bin)

+ 12 - 0
Tests/RunCMake/CpsExportImportBuild/TestLibrary.cmake

@@ -0,0 +1,12 @@
+project(TestLibrary C)
+
+add_library(liba SHARED liba.c)
+add_library(libb SHARED libb.c)
+
+target_link_libraries(libb PUBLIC liba)
+
+install(TARGETS liba EXPORT liba DESTINATION lib)
+export(EXPORT liba PACKAGE_INFO liba)
+
+install(TARGETS libb EXPORT libb DESTINATION lib)
+export(EXPORT libb PACKAGE_INFO libb)

+ 13 - 0
Tests/RunCMake/CpsExportImportBuild/app.c

@@ -0,0 +1,13 @@
+#include <stdio.h>
+
+extern
+#ifdef _WIN32
+__declspec(dllimport)
+#endif
+int ask(void);
+
+int main(void)
+{
+  printf("%i\n", ask());
+  return 0;
+}

+ 7 - 0
Tests/RunCMake/CpsExportImportBuild/liba.c

@@ -0,0 +1,7 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int answer(void)
+{
+  return 42;
+}

+ 13 - 0
Tests/RunCMake/CpsExportImportBuild/libb.c

@@ -0,0 +1,13 @@
+extern
+#ifdef _WIN32
+__declspec(dllimport)
+#endif
+int answer(void);
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int ask(void)
+{
+  return answer();
+}

+ 16 - 0
Tests/RunCMake/ExportPackageInfo/Appendix-check.cmake

@@ -0,0 +1,16 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/Appendix-build")
+
+file(READ "${out_dir}/foo.cps" content)
+expect_value("${content}" "foo" "name")
+expect_value("${content}" "interface" "components" "mammal" "type")
+expect_value("${content}" "1.0" "version")
+
+file(READ "${out_dir}/foo-dog.cps" content)
+expect_value("${content}" "foo" "name")
+expect_value("${content}" "interface" "components" "canine" "type")
+expect_missing("${content}" "version")
+
+expect_array("${content}" 1 "components" "canine" "requires")
+expect_value("${content}" ":mammal" "components" "canine" "requires" 0)

+ 9 - 0
Tests/RunCMake/ExportPackageInfo/Appendix.cmake

@@ -0,0 +1,9 @@
+add_library(mammal INTERFACE)
+add_library(canine INTERFACE)
+target_link_libraries(canine INTERFACE mammal)
+
+install(TARGETS mammal EXPORT mammal DESTINATION .)
+install(TARGETS canine EXPORT canine DESTINATION .)
+
+export(EXPORT mammal PACKAGE_INFO foo VERSION 1.0)
+export(EXPORT canine PACKAGE_INFO foo APPENDIX dog)

+ 34 - 0
Tests/RunCMake/ExportPackageInfo/Assertions.cmake

@@ -0,0 +1,34 @@
+macro(_expect entity op actual expected)
+  if(NOT "${actual}" ${op} "${expected}")
+    list(JOIN ARGN "." name)
+    set(RunCMake_TEST_FAILED
+      "Attribute '${name}' ${entity} '${actual}' does not match expected ${entity} '${expected}'" PARENT_SCOPE)
+    return()
+  endif()
+endmacro()
+
+function(expect_value content expected_value)
+  string(JSON actual_value GET "${content}" ${ARGN})
+  _expect("value" STREQUAL "${actual_value}" "${expected_value}" ${ARGN})
+endfunction()
+
+function(expect_array content expected_length)
+  string(JSON actual_type TYPE "${content}" ${ARGN})
+  _expect("type" STREQUAL "${actual_type}" "ARRAY" ${ARGN})
+
+  string(JSON actual_length LENGTH "${content}" ${ARGN})
+  _expect("length" EQUAL "${actual_length}" "${expected_length}" ${ARGN})
+endfunction()
+
+function(expect_null content)
+  string(JSON actual_type TYPE "${content}" ${ARGN})
+  _expect("type" STREQUAL "${actual_type}" "NULL" ${ARGN})
+endfunction()
+
+function(expect_missing content)
+  string(JSON value ERROR_VARIABLE error GET "${content}" ${ARGN})
+  if(NOT value MATCHES "^(.*-)?NOTFOUND$")
+    set(RunCMake_TEST_FAILED
+      "Attribute '${ARGN}' is unexpectedly present" PARENT_SCOPE)
+  endif()
+endfunction()

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/BadArgs1-result.txt

@@ -0,0 +1 @@
+1

+ 10 - 0
Tests/RunCMake/ExportPackageInfo/BadArgs1-stderr.txt

@@ -0,0 +1,10 @@
+CMake Error at BadArgs1\.cmake:3 \(export\):
+  export COMPAT_VERSION requires VERSION\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs1\.cmake:4 \(export\):
+  export VERSION_SCHEMA requires VERSION\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/ExportPackageInfo/BadArgs1.cmake

@@ -0,0 +1,4 @@
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO foo COMPAT_VERSION 1.0)
+export(EXPORT foo PACKAGE_INFO foo VERSION_SCHEMA simple)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/BadArgs2-result.txt

@@ -0,0 +1 @@
+1

+ 16 - 0
Tests/RunCMake/ExportPackageInfo/BadArgs2-stderr.txt

@@ -0,0 +1,16 @@
+CMake Error at BadArgs2\.cmake:3 \(export\):
+  export APPENDIX and VERSION are mutually exclusive\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs2\.cmake:4 \(export\):
+  export APPENDIX and DEFAULT_TARGETS are mutually exclusive\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs2\.cmake:5 \(export\):
+  export APPENDIX and DEFAULT_CONFIGURATIONS are mutually exclusive\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

+ 5 - 0
Tests/RunCMake/ExportPackageInfo/BadArgs2.cmake

@@ -0,0 +1,5 @@
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO foo APPENDIX test VERSION 1.0)
+export(EXPORT foo PACKAGE_INFO foo APPENDIX test DEFAULT_TARGETS foo)
+export(EXPORT foo PACKAGE_INFO foo APPENDIX test DEFAULT_CONFIGURATIONS Release)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/BadArgs3-result.txt

@@ -0,0 +1 @@
+1

+ 10 - 0
Tests/RunCMake/ExportPackageInfo/BadArgs3-stderr.txt

@@ -0,0 +1,10 @@
+CMake Error at BadArgs3\.cmake:3 \(export\):
+  export PACKAGE_INFO and FILE are mutually exclusive\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs3\.cmake:4 \(export\):
+  export PACKAGE_INFO and NAMESPACE are mutually exclusive\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/ExportPackageInfo/BadArgs3.cmake

@@ -0,0 +1,4 @@
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO foo FILE foo.cps)
+export(EXPORT foo PACKAGE_INFO foo NAMESPACE foo::)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/BadArgs4-result.txt

@@ -0,0 +1 @@
+1

+ 28 - 0
Tests/RunCMake/ExportPackageInfo/BadArgs4-stderr.txt

@@ -0,0 +1,28 @@
+CMake Error at BadArgs4\.cmake:3 \(export\):
+  export LOWER_CASE_FILE requires PACKAGE_INFO\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs4\.cmake:4 \(export\):
+  export APPENDIX requires PACKAGE_INFO\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs4\.cmake:5 \(export\):
+  export VERSION requires PACKAGE_INFO\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs4\.cmake:6 \(export\):
+  export DEFAULT_TARGETS requires PACKAGE_INFO\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs4\.cmake:7 \(export\):
+  export DEFAULT_CONFIGURATIONS requires PACKAGE_INFO\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

+ 7 - 0
Tests/RunCMake/ExportPackageInfo/BadArgs4.cmake

@@ -0,0 +1,7 @@
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo LOWER_CASE_FILE)
+export(EXPORT foo APPENDIX test)
+export(EXPORT foo VERSION 1.0)
+export(EXPORT foo DEFAULT_TARGETS foo)
+export(EXPORT foo DEFAULT_CONFIGURATIONS Release)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/BadDefaultTarget-result.txt

@@ -0,0 +1 @@
+1

+ 8 - 0
Tests/RunCMake/ExportPackageInfo/BadDefaultTarget-stderr.txt

@@ -0,0 +1,8 @@
+CMake Error in CMakeLists.txt:
+  Package "test" specifies DEFAULT_TARGETS "dog", which is not a target in
+  the export set "foo".
+
+
+CMake Error in CMakeLists.txt:
+  Package "test" specifies DEFAULT_TARGETS "cat", which is not a target in
+  the export set "foo".

+ 5 - 0
Tests/RunCMake/ExportPackageInfo/BadDefaultTarget.cmake

@@ -0,0 +1,5 @@
+add_library(foo INTERFACE)
+add_library(dog INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+install(TARGETS dog EXPORT dog DESTINATION .)
+export(EXPORT foo PACKAGE_INFO test DEFAULT_TARGETS dog cat)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/BadName-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/ExportPackageInfo/BadName-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at BadName\.cmake:3 \(export\):
+  export PACKAGE_INFO given invalid package name "%foo"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

+ 3 - 0
Tests/RunCMake/ExportPackageInfo/BadName.cmake

@@ -0,0 +1,3 @@
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO "%foo")

+ 3 - 0
Tests/RunCMake/ExportPackageInfo/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 4.0.20240220)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 3 - 0
Tests/RunCMake/ExportPackageInfo/DependsMultiple.cmake

@@ -0,0 +1,3 @@
+project(DependsMultiple CXX)
+set(NAMESPACE foo::)
+include(DependsMultipleCommon.cmake)

+ 11 - 0
Tests/RunCMake/ExportPackageInfo/DependsMultipleCommon.cmake

@@ -0,0 +1,11 @@
+add_library(foo foo.cxx)
+add_library(bar foo.cxx)
+target_link_libraries(bar foo)
+
+install(TARGETS foo EXPORT foo)
+export(EXPORT foo NAMESPACE ${NAMESPACE})
+export(EXPORT foo PACKAGE_INFO foo)
+
+install(TARGETS bar EXPORT bar)
+export(EXPORT bar)
+export(EXPORT bar PACKAGE_INFO bar)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentNamespace-result.txt

@@ -0,0 +1 @@
+1

+ 22 - 0
Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentNamespace-stderr.txt

@@ -0,0 +1,22 @@
+CMake Error in CMakeLists\.txt:
+  export called with target "bar" which requires target "foo" that is not in
+  this export set, but in another export set which is exported multiple times
+  with different namespaces:.*
+  .*/Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentNamespace-build/foo\.cmake,.*
+  .*/Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentNamespace-build/foo\.cps\.
+
+  An exported target cannot depend upon another target which is exported in
+  more than one export set or with more than one namespace\.  Consider
+  consolidating the exports of the "foo" target to a single export\.
+
+
+CMake Error in CMakeLists\.txt:
+  export called with target "bar" which requires target "foo" that is not in
+  this export set, but in another export set which is exported multiple times
+  with different namespaces:.*
+  .*/Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentNamespace-build/foo\.cmake,.*
+  .*/Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentNamespace-build/foo\.cps\.
+
+  An exported target cannot depend upon another target which is exported in
+  more than one export set or with more than one namespace\.  Consider
+  consolidating the exports of the "foo" target to a single export\.

+ 3 - 0
Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentNamespace.cmake

@@ -0,0 +1,3 @@
+project(DependsMultipleDifferentNamespace CXX)
+set(NAMESPACE "")
+include(DependsMultipleCommon.cmake)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentSets-result.txt

@@ -0,0 +1 @@
+1

+ 22 - 0
Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentSets-stderr.txt

@@ -0,0 +1,22 @@
+CMake Error in CMakeLists\.txt:
+  export called with target "bar" which requires target "foo" that is not in
+  this export set, but in multiple other export sets:.*
+  .*/Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentSets-build/foo-alt\.cmake,.*
+  .*/Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentSets-build/foo\.cmake,.*
+  .*/Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentSets-build/foo\.cps\.
+
+  An exported target cannot depend upon another target which is exported in
+  more than one export set or with more than one namespace\.  Consider
+  consolidating the exports of the "foo" target to a single export\.
+
+
+CMake Error in CMakeLists\.txt:
+  export called with target "bar" which requires target "foo" that is not in
+  this export set, but in multiple other export sets:.*
+  .*/Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentSets-build/foo-alt\.cmake,.*
+  .*/Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentSets-build/foo\.cmake,.*
+  .*/Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentSets-build/foo\.cps\.
+
+  An exported target cannot depend upon another target which is exported in
+  more than one export set or with more than one namespace\.  Consider
+  consolidating the exports of the "foo" target to a single export\.

+ 5 - 0
Tests/RunCMake/ExportPackageInfo/DependsMultipleDifferentSets.cmake

@@ -0,0 +1,5 @@
+project(DependsMultipleDifferentSets CXX)
+include(DependsMultipleCommon.cmake)
+
+install(TARGETS foo EXPORT foo-alt)
+export(EXPORT foo-alt)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/DuplicateOutput-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/ExportPackageInfo/DuplicateOutput-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at DuplicateOutput\.cmake:4 \(export\):
+  export command already specified for the file foo\.cps\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

+ 4 - 0
Tests/RunCMake/ExportPackageInfo/DuplicateOutput.cmake

@@ -0,0 +1,4 @@
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO foo)
+export(EXPORT foo PACKAGE_INFO foo)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/ExperimentalGate-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
Tests/RunCMake/ExportPackageInfo/ExperimentalGate-stderr.txt

@@ -0,0 +1,2 @@
+CMake Error at ExperimentalGate.cmake:5 \(export\):
+  export Unknown argument: "PACKAGE_INFO"\.

+ 5 - 0
Tests/RunCMake/ExportPackageInfo/ExperimentalGate.cmake

@@ -0,0 +1,5 @@
+unset(CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO)
+
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO foo)

+ 7 - 0
Tests/RunCMake/ExportPackageInfo/ExperimentalWarning-stderr.txt

@@ -0,0 +1,7 @@
+CMake Warning \(dev\) at ExperimentalWarning.cmake:8 \(export\):
+  CMake's support for exporting package information in the Common Package
+  Specification format is experimental\.  It is meant only for experimentation
+  and feedback to CMake developers\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.

+ 8 - 0
Tests/RunCMake/ExportPackageInfo/ExperimentalWarning.cmake

@@ -0,0 +1,8 @@
+set(
+  CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO
+  "b80be207-778e-46ba-8080-b23bba22639e"
+  )
+
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO foo)

+ 24 - 0
Tests/RunCMake/ExportPackageInfo/InterfaceProperties-check.cmake

@@ -0,0 +1,24 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/InterfaceProperties-build")
+
+file(READ "${out_dir}/foo.cps" content)
+expect_value("${content}" "foo" "name")
+
+string(JSON component GET "${content}" "components" "foo")
+
+expect_value("${component}" "interface" "type")
+expect_array("${component}" 1 "includes")
+expect_value("${component}" "${CMAKE_CURRENT_LIST_DIR}/foo" "includes" 0)
+expect_array("${component}" 1 "compile_features")
+expect_value("${component}" "c++23" "compile_features" 0)
+expect_array("${component}" 1 "compile_flags")
+expect_value("${component}" "-ffast-math" "compile_flags" 0)
+expect_null("${component}" "compile_definitions" "*" "FOO")
+expect_value("${component}" "BAR" "compile_definitions" "*" "BAR")
+expect_array("${component}" 1 "link_directories")
+expect_value("${component}" "/opt/foo/lib" "link_directories" 0)
+expect_array("${component}" 1 "link_flags")
+expect_value("${component}" "--needed" "link_flags" 0)
+expect_array("${component}" 1 "link_libraries")
+expect_value("${component}" "/usr/lib/libm.so" "link_libraries" 0)

+ 15 - 0
Tests/RunCMake/ExportPackageInfo/InterfaceProperties.cmake

@@ -0,0 +1,15 @@
+add_library(foo INTERFACE)
+
+target_compile_features(foo INTERFACE cxx_std_23)
+target_compile_options(foo INTERFACE -ffast-math)
+target_compile_definitions(foo INTERFACE -DFOO -DBAR=BAR)
+target_include_directories(
+  foo INTERFACE
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/foo>
+  )
+target_link_directories(foo INTERFACE /opt/foo/lib)
+target_link_options(foo INTERFACE --needed)
+target_link_libraries(foo INTERFACE /usr/lib/libm.so)
+
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO foo)

+ 9 - 0
Tests/RunCMake/ExportPackageInfo/LowerCaseFile-check.cmake

@@ -0,0 +1,9 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/LowerCaseFile-build")
+
+file(READ "${out_dir}/lowercase.cps" content)
+expect_value("${content}" "LowerCase" "name")
+
+file(READ "${out_dir}/PreserveCase.cps" content)
+expect_value("${content}" "PreserveCase" "name")

+ 4 - 0
Tests/RunCMake/ExportPackageInfo/LowerCaseFile.cmake

@@ -0,0 +1,4 @@
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO LowerCase LOWER_CASE_FILE)
+export(EXPORT foo PACKAGE_INFO PreserveCase)

+ 16 - 0
Tests/RunCMake/ExportPackageInfo/Metadata-check.cmake

@@ -0,0 +1,16 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/Metadata-build")
+
+file(READ "${out_dir}/foo.cps" content)
+expect_value("${content}" "foo" "name")
+expect_value("${content}" "1.2.3" "version")
+expect_value("${content}" "1.2.0" "compat_version")
+expect_value("${content}" "simple" "version_schema")
+
+expect_array("${content}" 1 "default_components")
+expect_value("${content}" "foo" "default_components" 0)
+
+expect_array("${content}" 2 "configurations")
+expect_value("${content}" "release" "configurations" 0)
+expect_value("${content}" "debug" "configurations" 1)

+ 11 - 0
Tests/RunCMake/ExportPackageInfo/Metadata.cmake

@@ -0,0 +1,11 @@
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(
+  EXPORT foo
+  PACKAGE_INFO foo
+  VERSION 1.2.3
+  VERSION_SCHEMA simple
+  COMPAT_VERSION 1.2.0
+  DEFAULT_TARGETS foo
+  DEFAULT_CONFIGURATIONS release debug
+  )

+ 18 - 0
Tests/RunCMake/ExportPackageInfo/Minimal-check.cmake

@@ -0,0 +1,18 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/Minimal-build")
+
+file(READ "${out_dir}/foo.cps" content)
+expect_value("${content}" "foo" "name")
+expect_value("${content}" "interface" "components" "foo" "type")
+expect_missing("${content}" "version")
+expect_missing("${content}" "configurations")
+expect_missing("${content}" "default_targets")
+expect_missing("${content}" "components" "foo" "compile_definitions")
+expect_missing("${content}" "components" "foo" "compile_features")
+expect_missing("${content}" "components" "foo" "compile_flags")
+expect_missing("${content}" "components" "foo" "link_directories")
+expect_missing("${content}" "components" "foo" "link_features")
+expect_missing("${content}" "components" "foo" "link_flags")
+expect_missing("${content}" "components" "foo" "link_libraries")
+expect_missing("${content}" "components" "foo" "requires")

+ 3 - 0
Tests/RunCMake/ExportPackageInfo/Minimal.cmake

@@ -0,0 +1,3 @@
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO foo)

+ 21 - 0
Tests/RunCMake/ExportPackageInfo/MinimalVersion-check.cmake

@@ -0,0 +1,21 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/MinimalVersion-build")
+
+file(READ "${out_dir}/foo1.cps" content)
+expect_value("${content}" "foo1" "name")
+expect_value("${content}" "1.0" "version")
+expect_missing("${content}" "compat_version")
+expect_missing("${content}" "version_schema")
+
+file(READ "${out_dir}/foo2.cps" content)
+expect_value("${content}" "foo2" "name")
+expect_value("${content}" "1.5" "version")
+expect_value("${content}" "1.0" "compat_version")
+expect_missing("${content}" "version_schema")
+
+file(READ "${out_dir}/foo3.cps" content)
+expect_value("${content}" "foo3" "name")
+expect_value("${content}" "1.0" "version")
+expect_missing("${content}" "compat_version")
+expect_value("${content}" "simple" "version_schema")

+ 16 - 0
Tests/RunCMake/ExportPackageInfo/MinimalVersion.cmake

@@ -0,0 +1,16 @@
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+
+export(EXPORT foo
+  PACKAGE_INFO foo1
+  VERSION 1.0)
+
+export(EXPORT foo
+  PACKAGE_INFO foo2
+  VERSION 1.5
+  COMPAT_VERSION 1.0)
+
+export(EXPORT foo
+  PACKAGE_INFO foo3
+  VERSION 1.0
+  VERSION_SCHEMA simple)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/ReferencesNonExportedTarget-result.txt

@@ -0,0 +1 @@
+1

+ 3 - 0
Tests/RunCMake/ExportPackageInfo/ReferencesNonExportedTarget-stderr.txt

@@ -0,0 +1,3 @@
+CMake Error in CMakeLists.txt:
+  export called with target "canine" which requires target "mammal" that is
+  not in any export set.

+ 6 - 0
Tests/RunCMake/ExportPackageInfo/ReferencesNonExportedTarget.cmake

@@ -0,0 +1,6 @@
+add_library(mammal INTERFACE)
+add_library(canine INTERFACE)
+target_link_libraries(canine INTERFACE mammal)
+
+install(TARGETS canine EXPORT dog DESTINATION .)
+export(EXPORT dog PACKAGE_INFO dog)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/ReferencesWronglyExportedTarget-result.txt

@@ -0,0 +1 @@
+1

+ 7 - 0
Tests/RunCMake/ExportPackageInfo/ReferencesWronglyExportedTarget-stderr.txt

@@ -0,0 +1,7 @@
+CMake Error in CMakeLists.txt:
+  Target "test" references target "foo", which does not use the standard
+  namespace separator.  This is not allowed.
+.*
+CMake Error in CMakeLists.txt:
+  Target "test" references target "bar_bar", which does not use the standard
+  namespace separator.  This is not allowed.

+ 14 - 0
Tests/RunCMake/ExportPackageInfo/ReferencesWronglyExportedTarget.cmake

@@ -0,0 +1,14 @@
+add_library(foo INTERFACE)
+add_library(bar INTERFACE)
+
+add_library(test INTERFACE)
+target_link_libraries(test INTERFACE foo bar)
+
+install(TARGETS foo EXPORT foo DESTINATION .)
+install(TARGETS bar EXPORT bar DESTINATION .)
+
+export(EXPORT foo)
+export(EXPORT bar NAMESPACE bar_)
+
+install(TARGETS test EXPORT test DESTINATION .)
+export(EXPORT test PACKAGE_INFO test)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/ReferencesWronglyImportedTarget-result.txt

@@ -0,0 +1 @@
+1

+ 3 - 0
Tests/RunCMake/ExportPackageInfo/ReferencesWronglyImportedTarget-stderr.txt

@@ -0,0 +1,3 @@
+CMake Error in CMakeLists.txt:
+  Target "foo" references imported target "bar" which does not come from any
+  known package.

+ 7 - 0
Tests/RunCMake/ExportPackageInfo/ReferencesWronglyImportedTarget.cmake

@@ -0,0 +1,7 @@
+add_library(bar INTERFACE IMPORTED)
+
+add_library(foo INTERFACE)
+target_link_libraries(foo INTERFACE bar)
+
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO foo)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/ReferencesWronglyNamespacedTarget-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/ExportPackageInfo/ReferencesWronglyNamespacedTarget-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error in CMakeLists.txt:
+  Target "foo" references target "wrong::lib", which comes from the "broken"
+  package, but does not belong to the package's canonical namespace.  This is
+  not allowed.

+ 11 - 0
Tests/RunCMake/ExportPackageInfo/ReferencesWronglyNamespacedTarget.cmake

@@ -0,0 +1,11 @@
+find_package(
+  broken REQUIRED CONFIG
+  NO_DEFAULT_PATH
+  PATHS ${CMAKE_CURRENT_LIST_DIR}
+  )
+
+add_library(foo INTERFACE)
+target_link_libraries(foo INTERFACE wrong::lib)
+
+install(TARGETS foo EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO foo)

+ 22 - 0
Tests/RunCMake/ExportPackageInfo/Requirements-check.cmake

@@ -0,0 +1,22 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/Requirements-build")
+
+file(READ "${out_dir}/foo.cps" content)
+expect_value("${content}" "foo" "name")
+expect_value("${content}" "interface" "components" "libb" "type")
+
+file(READ "${out_dir}/bar.cps" content)
+expect_value("${content}" "bar" "name")
+expect_array("${content}"      1 "requires"  "foo" "components")
+expect_value("${content}" "libb" "requires"  "foo" "components" 0)
+expect_array("${content}"      1 "requires" "test" "components")
+expect_value("${content}" "liba" "requires" "test" "components" 0)
+expect_value("${content}" "interface" "components" "libc" "type")
+expect_value("${content}" "interface" "components" "libd" "type")
+
+string(JSON component GET "${content}" "components" "libd")
+expect_array("${component}" 3 "requires")
+expect_value("${component}" "test:liba" "requires" 0)
+expect_value("${component}"  "foo:libb" "requires" 1)
+expect_value("${component}"     ":libc" "requires" 2)

+ 20 - 0
Tests/RunCMake/ExportPackageInfo/Requirements.cmake

@@ -0,0 +1,20 @@
+find_package(
+  test REQUIRED CONFIG
+  NO_DEFAULT_PATH
+  PATHS ${CMAKE_CURRENT_LIST_DIR}
+  )
+
+add_library(libb INTERFACE)
+add_library(libc INTERFACE)
+add_library(libd INTERFACE)
+
+add_library(foo ALIAS libb)
+add_library(bar ALIAS libc)
+
+target_link_libraries(libd INTERFACE test::liba foo bar)
+
+install(TARGETS libb EXPORT foo DESTINATION .)
+export(EXPORT foo PACKAGE_INFO foo)
+
+install(TARGETS libc libd EXPORT bar DESTINATION .)
+export(EXPORT bar PACKAGE_INFO bar)

+ 37 - 0
Tests/RunCMake/ExportPackageInfo/RunCMakeTest.cmake

@@ -0,0 +1,37 @@
+include(RunCMake)
+
+# Test experimental gate
+run_cmake(ExperimentalGate)
+run_cmake(ExperimentalWarning)
+
+# Enable experimental feature and suppress warnings
+set(RunCMake_TEST_OPTIONS
+  -Wno-dev
+  "-DCMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO:STRING=b80be207-778e-46ba-8080-b23bba22639e"
+  )
+
+# Test incorrect usage
+run_cmake(BadArgs1)
+run_cmake(BadArgs2)
+run_cmake(BadArgs3)
+run_cmake(BadArgs4)
+run_cmake(BadName)
+run_cmake(DuplicateOutput)
+run_cmake(BadDefaultTarget)
+run_cmake(ReferencesNonExportedTarget)
+run_cmake(ReferencesWronglyExportedTarget)
+run_cmake(ReferencesWronglyImportedTarget)
+run_cmake(ReferencesWronglyNamespacedTarget)
+run_cmake(DependsMultipleDifferentNamespace)
+run_cmake(DependsMultipleDifferentSets)
+
+# Test functionality
+run_cmake(Appendix)
+run_cmake(InterfaceProperties)
+run_cmake(Metadata)
+run_cmake(Minimal)
+run_cmake(MinimalVersion)
+run_cmake(LowerCaseFile)
+run_cmake(Requirements)
+run_cmake(TargetTypes)
+run_cmake(DependsMultiple)

+ 11 - 0
Tests/RunCMake/ExportPackageInfo/TargetTypes-check.cmake

@@ -0,0 +1,11 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/TargetTypes-build")
+
+file(READ "${out_dir}/foo.cps" content)
+expect_value("${content}" "foo" "name")
+expect_value("${content}" "archive"    "components" "foo-static" "type")
+expect_value("${content}" "dylib"      "components" "foo-shared" "type")
+expect_value("${content}" "module"     "components" "foo-module" "type")
+expect_value("${content}" "interface"  "components" "bar"        "type")
+expect_value("${content}" "executable" "components" "test"       "type")

+ 20 - 0
Tests/RunCMake/ExportPackageInfo/TargetTypes.cmake

@@ -0,0 +1,20 @@
+project(TargetTypes CXX)
+
+add_library(foo-static STATIC foo.cxx)
+add_library(foo-shared SHARED foo.cxx)
+add_library(foo-module MODULE foo.cxx)
+add_library(bar INTERFACE)
+add_executable(test test.cxx)
+
+install(
+  TARGETS
+    foo-static
+    foo-shared
+    foo-module
+    bar
+    test
+  EXPORT foo
+  DESTINATION .
+  )
+
+export(EXPORT foo PACKAGE_INFO foo)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/broken-config.cmake

@@ -0,0 +1 @@
+add_library(wrong::lib INTERFACE IMPORTED)

+ 3 - 0
Tests/RunCMake/ExportPackageInfo/foo.cxx

@@ -0,0 +1,3 @@
+void foo()
+{
+}

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/test-config.cmake

@@ -0,0 +1 @@
+add_library(test::liba INTERFACE IMPORTED)

+ 4 - 0
Tests/RunCMake/ExportPackageInfo/test.cxx

@@ -0,0 +1,4 @@
+int main()
+{
+  return 0;
+}

+ 1 - 0
Tests/RunCMake/InstallPackageInfo/BadName-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/InstallPackageInfo/BadName-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at BadName\.cmake:3 \(install\):
+  install PACKAGE_INFO given invalid package name "%foo"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

+ 3 - 0
Tests/RunCMake/InstallPackageInfo/BadName.cmake

@@ -0,0 +1,3 @@
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+install(PACKAGE_INFO "%foo" EXPORT foo)

+ 1 - 0
Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake

@@ -16,6 +16,7 @@ run_cmake(BadArgs2)
 run_cmake(BadArgs3)
 run_cmake(BadArgs3)
 run_cmake(BadArgs4)
 run_cmake(BadArgs4)
 run_cmake(BadArgs5)
 run_cmake(BadArgs5)
+run_cmake(BadName)
 run_cmake(BadDefaultTarget)
 run_cmake(BadDefaultTarget)
 run_cmake(ReferencesNonExportedTarget)
 run_cmake(ReferencesNonExportedTarget)
 run_cmake(ReferencesWronglyExportedTarget)
 run_cmake(ReferencesWronglyExportedTarget)