Просмотр исходного кода

Merge topic 'cps-metadata-from-project'

ecf81c1bc1 CPS: Get metadata from project
9adf93d112 Tests/InstallPackageInfo: Consolidate bad arguments tests
d4ab279742 cmState: Track known projects
0ccbe5a2e1 CPS: Refactor argument parsing

Acked-by: Kitware Robot <[email protected]>
Merge-request: !10837
Brad King 6 месяцев назад
Родитель
Сommit
7dbffb3aa1
47 измененных файлов с 586 добавлено и 224 удалено
  1. 1 0
      Help/command/export.rst
  2. 10 1
      Help/command/install.rst
  3. 2 0
      Source/CMakeLists.txt
  4. 3 7
      Source/cmExportBuildPackageInfoGenerator.cxx
  5. 2 5
      Source/cmExportBuildPackageInfoGenerator.h
  6. 9 82
      Source/cmExportCommand.cxx
  7. 3 8
      Source/cmExportInstallPackageInfoGenerator.cxx
  8. 3 6
      Source/cmExportInstallPackageInfoGenerator.h
  9. 9 9
      Source/cmExportPackageInfoGenerator.cxx
  10. 4 6
      Source/cmExportPackageInfoGenerator.h
  11. 7 63
      Source/cmInstallCommand.cxx
  12. 5 9
      Source/cmInstallPackageInfoExportGenerator.cxx
  13. 4 5
      Source/cmInstallPackageInfoExportGenerator.h
  14. 188 0
      Source/cmPackageInfoArguments.cxx
  15. 111 0
      Source/cmPackageInfoArguments.h
  16. 3 0
      Source/cmStatePrivate.h
  17. 8 0
      Source/cmStateSnapshot.cxx
  18. 1 0
      Source/cmStateSnapshot.h
  19. 12 0
      Tests/RunCMake/ExportPackageInfo/BadArgs1-stderr.txt
  20. 2 0
      Tests/RunCMake/ExportPackageInfo/BadArgs1.cmake
  21. 6 0
      Tests/RunCMake/ExportPackageInfo/BadArgs2-stderr.txt
  22. 1 0
      Tests/RunCMake/ExportPackageInfo/BadArgs2.cmake
  23. 12 0
      Tests/RunCMake/ExportPackageInfo/BadArgs4-stderr.txt
  24. 2 0
      Tests/RunCMake/ExportPackageInfo/BadArgs4.cmake
  25. 8 0
      Tests/RunCMake/ExportPackageInfo/NoProjectMetadata-check.cmake
  26. 10 0
      Tests/RunCMake/ExportPackageInfo/NoProjectMetadata.cmake
  27. 18 0
      Tests/RunCMake/ExportPackageInfo/ProjectMetadata-check.cmake
  28. 25 0
      Tests/RunCMake/ExportPackageInfo/ProjectMetadata.cmake
  29. 2 0
      Tests/RunCMake/ExportPackageInfo/RunCMakeTest.cmake
  30. 20 0
      Tests/RunCMake/InstallPackageInfo/BadArgs1-stderr.txt
  31. 3 0
      Tests/RunCMake/InstallPackageInfo/BadArgs1.cmake
  32. 21 1
      Tests/RunCMake/InstallPackageInfo/BadArgs2-stderr.txt
  33. 4 1
      Tests/RunCMake/InstallPackageInfo/BadArgs2.cmake
  34. 0 1
      Tests/RunCMake/InstallPackageInfo/BadArgs3-result.txt
  35. 0 2
      Tests/RunCMake/InstallPackageInfo/BadArgs3-stderr.txt
  36. 0 3
      Tests/RunCMake/InstallPackageInfo/BadArgs3.cmake
  37. 0 1
      Tests/RunCMake/InstallPackageInfo/BadArgs4-result.txt
  38. 0 2
      Tests/RunCMake/InstallPackageInfo/BadArgs4-stderr.txt
  39. 0 3
      Tests/RunCMake/InstallPackageInfo/BadArgs4.cmake
  40. 0 1
      Tests/RunCMake/InstallPackageInfo/BadArgs5-result.txt
  41. 0 2
      Tests/RunCMake/InstallPackageInfo/BadArgs5-stderr.txt
  42. 0 3
      Tests/RunCMake/InstallPackageInfo/BadArgs5.cmake
  43. 8 0
      Tests/RunCMake/InstallPackageInfo/NoProjectMetadata-check.cmake
  44. 11 0
      Tests/RunCMake/InstallPackageInfo/NoProjectMetadata.cmake
  45. 18 0
      Tests/RunCMake/InstallPackageInfo/ProjectMetadata-check.cmake
  46. 28 0
      Tests/RunCMake/InstallPackageInfo/ProjectMetadata.cmake
  47. 2 3
      Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake

+ 1 - 0
Help/command/export.rst

@@ -133,6 +133,7 @@ Exporting Targets to the |CPS|
 .. code-block:: cmake
 
   export(EXPORT <export-name> PACKAGE_INFO <package-name>
+         [PROJECT <project-name>|NO_PROJECT_METADATA]
          [APPENDIX <appendix-name>]
          [LOWER_CASE_FILE]
          [VERSION <version>

+ 10 - 1
Help/command/install.rst

@@ -992,6 +992,7 @@ Signatures
   .. code-block:: cmake
 
     install(PACKAGE_INFO <package-name> EXPORT <export-name>
+            [PROJECT <project-name>|NO_PROJECT_METADATA]
             [APPENDIX <appendix-name>]
             [DESTINATION <dir>]
             [LOWER_CASE_FILE]
@@ -1056,6 +1057,14 @@ Signatures
     configurations exists.  If not specified, CMake will fall back to the
     package's available configurations in an unspecified order.
 
+  By default, if the specified ``<package-name>`` matches the current CMake
+  :variable:`PROJECT_NAME`, package metadata will be inherited from the
+  project.  The ``PROJECT <project-name>`` option may be used to specify a
+  different project from which to inherit metadata.  If ``NO_PROJECT_METADATA``
+  is specified, automatic inheritance of package metadata will be disabled.
+  In any case, any metadata values specified in the ``install`` command will
+  take precedence.
+
   If ``APPENDIX`` is specified, rather than generating a top level package
   specification, the specified targets will be exported as an appendix to the
   named package.  Appendices may be used to separate less commonly used targets
@@ -1065,7 +1074,7 @@ Signatures
   artifacts produced by multiple build trees.
 
   Appendices are not permitted to change basic package metadata; therefore,
-  none of ``VERSION``, ``COMPAT_VERSION``, ``VERSION_SCHEMA``,
+  none of ``PROJECT``, ``VERSION``, ``COMPAT_VERSION``, ``VERSION_SCHEMA``,
   ``DEFAULT_TARGETS`` or ``DEFAULT_CONFIGURATIONS`` may be specified in
   combination with ``APPENDIX``.  Additionally, it is strongly recommended that
   use of ``LOWER_CASE_FILE`` should be consistent between the main package and

+ 2 - 0
Source/CMakeLists.txt

@@ -409,6 +409,8 @@ add_library(
   cmNewLineStyle.cxx
   cmOrderDirectories.cxx
   cmOrderDirectories.h
+  cmPackageInfoArguments.cxx
+  cmPackageInfoArguments.h
   cmPackageInfoReader.cxx
   cmPackageInfoReader.h
   cmPackageState.h

+ 3 - 7
Source/cmExportBuildPackageInfoGenerator.cxx

@@ -11,17 +11,13 @@
 #include <cm3p/json/value.h>
 
 #include "cmGeneratorExpression.h"
+#include "cmPackageInfoArguments.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))
+  cmPackageInfoArguments arguments)
+  : cmExportPackageInfoGenerator(std::move(arguments))
 {
   this->SetNamespace(cmStrCat(this->GetPackageName(), "::"_s));
 }

+ 2 - 5
Source/cmExportBuildPackageInfoGenerator.h

@@ -6,7 +6,6 @@
 
 #include <iosfwd>
 #include <string>
-#include <vector>
 
 #include "cmExportBuildFileGenerator.h"
 #include "cmExportPackageInfoGenerator.h"
@@ -16,6 +15,7 @@ class Value;
 }
 
 class cmGeneratorTarget;
+class cmPackageInfoArguments;
 
 /** \class cmExportBuildPackageInfoGenerator
  * \brief Generate a file exporting targets from a build tree.
@@ -31,10 +31,7 @@ class cmExportBuildPackageInfoGenerator
   , 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);
+  cmExportBuildPackageInfoGenerator(cmPackageInfoArguments arguments);
 
 protected:
   // Implement virtual methods from the superclass.

+ 9 - 82
Source/cmExportCommand.cxx

@@ -24,10 +24,10 @@
 #include "cmExportBuildPackageInfoGenerator.h"
 #include "cmExportSet.h"
 #include "cmGeneratedFileStream.h"
-#include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmPackageInfoArguments.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmStateTypes.h"
@@ -63,24 +63,16 @@ bool cmExportCommand(std::vector<std::string> const& args,
     return HandlePackage(args, status);
   }
 
-  struct Arguments
+  struct Arguments : cmPackageInfoArguments
   {
     cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Targets;
     ArgumentParser::NonEmpty<std::string> ExportSetName;
     ArgumentParser::NonEmpty<std::string> Namespace;
     ArgumentParser::NonEmpty<std::string> Filename;
     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::vector<std::string>> DefaultTargets;
-    ArgumentParser::NonEmpty<std::vector<std::string>> DefaultConfigs;
     bool Append = false;
     bool ExportOld = false;
-    bool LowerCase = false;
 
     std::vector<std::vector<std::string>> PackageDependencyArgs;
     bool ExportPackageDependencies = false;
@@ -104,14 +96,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
     }
     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);
+      cmPackageInfoArguments::Bind(parser);
     }
   } else if (args[0] == "SETUP") {
     parser.Bind("SETUP"_s, &Arguments::ExportSetName);
@@ -129,7 +114,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
   }
 
   std::vector<std::string> unknownArgs;
-  Arguments const arguments = parser.Parse(args, &unknownArgs);
+  Arguments arguments = parser.Parse(args, &unknownArgs);
 
   if (!unknownArgs.empty()) {
     status.SetError("Unknown argument: \"" + unknownArgs.front() + "\".");
@@ -222,24 +207,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
   }
 
   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.");
+    if (!arguments.Check(status, false)) {
       return false;
     }
   } else {
@@ -251,30 +219,8 @@ bool cmExportCommand(std::vector<std::string> const& args,
       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.");
+    if (!arguments.Check(status) ||
+        !arguments.SetMetadataFromProject(status)) {
       return false;
     }
   }
@@ -293,23 +239,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
     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);
-      }
+      fname = arguments.GetPackageFileName();
       cps = true;
     }
   } else {
@@ -430,10 +360,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
     ebag->SetAppendMode(arguments.Append);
     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);
+    auto ebpg = cm::make_unique<cmExportBuildPackageInfoGenerator>(arguments);
     ebfg = std::move(ebpg);
   } else {
     auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>();

+ 3 - 8
Source/cmExportInstallPackageInfoGenerator.cxx

@@ -15,6 +15,7 @@
 #include "cmInstallExportGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
+#include "cmPackageInfoArguments.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
@@ -22,14 +23,8 @@
 #include "cmTargetExport.h"
 
 cmExportInstallPackageInfoGenerator::cmExportInstallPackageInfoGenerator(
-  cmInstallExportGenerator* iegen, 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))
+  cmInstallExportGenerator* iegen, cmPackageInfoArguments arguments)
+  : cmExportPackageInfoGenerator(std::move(arguments))
   , cmExportInstallFileGenerator(iegen)
 {
 }

+ 3 - 6
Source/cmExportInstallPackageInfoGenerator.h

@@ -6,13 +6,13 @@
 
 #include <iosfwd>
 #include <string>
-#include <vector>
 
 #include "cmExportInstallFileGenerator.h"
 #include "cmExportPackageInfoGenerator.h"
 
 class cmGeneratorTarget;
 class cmInstallExportGenerator;
+class cmPackageInfoArguments;
 
 /** \class cmExportInstallPackageInfoGenerator
  * \brief Generate files exporting targets from an install tree.
@@ -35,11 +35,8 @@ class cmExportInstallPackageInfoGenerator
 public:
   /** Construct with the export installer that will install the
       files.  */
-  cmExportInstallPackageInfoGenerator(
-    cmInstallExportGenerator* iegen, std::string packageName,
-    std::string version, std::string versionCompat, std::string versionSchema,
-    std::vector<std::string> defaultTargets,
-    std::vector<std::string> defaultConfigurations);
+  cmExportInstallPackageInfoGenerator(cmInstallExportGenerator* iegen,
+                                      cmPackageInfoArguments arguments);
 
   /** Compute the globbing expression used to load per-config import
       files from the main file.  */

+ 9 - 9
Source/cmExportPackageInfoGenerator.cxx

@@ -15,6 +15,7 @@
 #include <cm3p/json/value.h>
 #include <cm3p/json/writer.h>
 
+#include "cmArgumentParserTypes.h"
 #include "cmExportSet.h"
 #include "cmFindPackageStack.h"
 #include "cmGeneratorExpression.h"
@@ -22,6 +23,7 @@
 #include "cmList.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmPackageInfoArguments.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
@@ -30,15 +32,13 @@
 static std::string const kCPS_VERSION_STR = "0.13.0";
 
 cmExportPackageInfoGenerator::cmExportPackageInfoGenerator(
-  std::string packageName, std::string version, std::string versionCompat,
-  std::string versionSchema, std::vector<std::string> defaultTargets,
-  std::vector<std::string> defaultConfigurations)
-  : PackageName(std::move(packageName))
-  , PackageVersion(std::move(version))
-  , PackageVersionCompat(std::move(versionCompat))
-  , PackageVersionSchema(std::move(versionSchema))
-  , DefaultTargets(std::move(defaultTargets))
-  , DefaultConfigurations(std::move(defaultConfigurations))
+  cmPackageInfoArguments arguments)
+  : PackageName(std::move(arguments.PackageName))
+  , PackageVersion(std::move(arguments.Version))
+  , PackageVersionCompat(std::move(arguments.VersionCompat))
+  , PackageVersionSchema(std::move(arguments.VersionSchema))
+  , DefaultTargets(std::move(arguments.DefaultTargets))
+  , DefaultConfigurations(std::move(arguments.DefaultConfigs))
 {
 }
 

+ 4 - 6
Source/cmExportPackageInfoGenerator.h

@@ -15,11 +15,13 @@
 #include "cmExportFileGenerator.h"
 #include "cmStateTypes.h"
 
-class cmGeneratorTarget;
 namespace Json {
 class Value;
 }
 
+class cmGeneratorTarget;
+class cmPackageInfoArguments;
+
 /** \class cmExportPackageInfoGenerator
  * \brief Generate Common Package Specification package information files
  * exporting targets from a build or install tree.
@@ -32,11 +34,7 @@ class Value;
 class cmExportPackageInfoGenerator : virtual public cmExportFileGenerator
 {
 public:
-  cmExportPackageInfoGenerator(std::string packageName, std::string version,
-                               std::string versionCompat,
-                               std::string versionSchema,
-                               std::vector<std::string> defaultTargets,
-                               std::vector<std::string> defaultConfigurations);
+  cmExportPackageInfoGenerator(cmPackageInfoArguments arguments);
 
   using cmExportFileGenerator::GenerateImportFile;
 

+ 7 - 63
Source/cmInstallCommand.cxx

@@ -45,6 +45,7 @@
 #include "cmList.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmPackageInfoArguments.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmRuntimeDependencyArchive.h"
@@ -2174,26 +2175,12 @@ bool HandlePackageInfoMode(std::vector<std::string> const& args,
   // This is the PACKAGE_INFO mode.
   cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile);
 
-  ArgumentParser::NonEmpty<std::string> pkg;
-  ArgumentParser::NonEmpty<std::string> appendix;
+  cmPackageInfoArguments arguments;
   ArgumentParser::NonEmpty<std::string> exportName;
-  bool lowerCase = false;
-  ArgumentParser::NonEmpty<std::string> version;
-  ArgumentParser::NonEmpty<std::string> versionCompat;
-  ArgumentParser::NonEmpty<std::string> versionSchema;
-  ArgumentParser::NonEmpty<std::vector<std::string>> defaultTargets;
-  ArgumentParser::NonEmpty<std::vector<std::string>> defaultConfigs;
   ArgumentParser::NonEmpty<std::string> cxxModulesDirectory;
 
-  ica.Bind("PACKAGE_INFO"_s, pkg);
+  arguments.Bind(ica);
   ica.Bind("EXPORT"_s, exportName);
-  ica.Bind("APPENDIX"_s, appendix);
-  ica.Bind("LOWER_CASE_FILE"_s, lowerCase);
-  ica.Bind("VERSION"_s, version);
-  ica.Bind("COMPAT_VERSION"_s, versionCompat);
-  ica.Bind("VERSION_SCHEMA"_s, versionSchema);
-  ica.Bind("DEFAULT_TARGETS"_s, defaultTargets);
-  ica.Bind("DEFAULT_CONFIGURATIONS"_s, defaultConfigs);
   // ica.Bind("CXX_MODULES_DIRECTORY"_s, cxxModulesDirectory); TODO?
 
   std::vector<std::string> unknownArgs;
@@ -2215,51 +2202,10 @@ bool HandlePackageInfoMode(std::vector<std::string> const& args,
     return false;
   }
 
-  if (version.empty()) {
-    if (!versionCompat.empty()) {
-      status.SetError("COMPAT_VERSION requires VERSION.");
-      return false;
-    }
-    if (!versionSchema.empty()) {
-      status.SetError("VERSION_SCHEMA requires VERSION.");
-      return false;
-    }
-  } else {
-    if (!appendix.empty()) {
-      status.SetError("APPENDIX and VERSION are mutually exclusive.");
-      return false;
-    }
-  }
-  if (!appendix.empty()) {
-    if (!defaultTargets.empty()) {
-      status.SetError("APPENDIX and DEFAULT_TARGETS are mutually exclusive.");
-      return false;
-    }
-    if (!defaultConfigs.empty()) {
-      status.SetError("APPENDIX and DEFAULT_CONFIGURATIONS "
-                      "are mutually exclusive.");
-      return false;
-    }
-  }
-
-  // Validate the package name.
-  if (!cmGeneratorExpression::IsValidTargetName(pkg) ||
-      pkg.find(':') != std::string::npos) {
-    status.SetError(
-      cmStrCat(args[0], " given invalid package name \"", pkg, "\"."));
+  if (!arguments.Check(status) || !arguments.SetMetadataFromProject(status)) {
     return false;
   }
 
-  // Construct the case-normalized package name and the file name.
-  std::string const pkgNameOnDisk =
-    (lowerCase ? cmSystemTools::LowerCase(pkg) : pkg);
-  std::string pkgFileName = [&]() -> std::string {
-    if (appendix.empty()) {
-      return cmStrCat(pkgNameOnDisk, ".cps");
-    }
-    return cmStrCat(pkgNameOnDisk, '-', appendix, ".cps");
-  }();
-
   // Get or construct the destination path.
   std::string dest = ica.GetDestination();
   if (dest.empty()) {
@@ -2267,7 +2213,7 @@ bool HandlePackageInfoMode(std::vector<std::string> const& args,
       dest = std::string{ "cps"_s };
     } else {
       dest = cmStrCat(helper.GetLibraryDestination(nullptr), "/cps/",
-                      pkgNameOnDisk);
+                      arguments.GetPackageDirName());
     }
   }
 
@@ -2282,10 +2228,8 @@ bool HandlePackageInfoMode(std::vector<std::string> const& args,
     cm::make_unique<cmInstallPackageInfoExportGenerator>(
       &exportSet, dest, ica.GetPermissions(), ica.GetConfigurations(),
       ica.GetComponent(), message, ica.GetExcludeFromAll(),
-      std::move(pkgFileName), std::move(pkg), std::move(version),
-      std::move(versionCompat), std::move(versionSchema),
-      std::move(defaultTargets), std::move(defaultConfigs),
-      std::move(cxxModulesDirectory), helper.Makefile->GetBacktrace()));
+      std::move(arguments), std::move(cxxModulesDirectory),
+      helper.Makefile->GetBacktrace()));
 
   return true;
 #else

+ 5 - 9
Source/cmInstallPackageInfoExportGenerator.cxx

@@ -9,27 +9,23 @@
 #include "cmExportInstallFileGenerator.h"
 #include "cmExportInstallPackageInfoGenerator.h"
 #include "cmListFileCache.h"
+#include "cmPackageInfoArguments.h"
 
 class cmExportSet;
 
 cmInstallPackageInfoExportGenerator::cmInstallPackageInfoExportGenerator(
   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 packageName, std::string version, std::string versionCompat,
-  std::string versionSchema, std::vector<std::string> defaultTargets,
-  std::vector<std::string> defaultConfigurations,
+  MessageLevel message, bool excludeFromAll, cmPackageInfoArguments arguments,
   std::string cxxModulesDirectory, cmListFileBacktrace backtrace)
   : cmInstallExportGenerator(
       exportSet, std::move(destination), std::move(filePermissions),
       configurations, std::move(component), message, excludeFromAll,
-      std::move(filename), packageName + "::", std::move(cxxModulesDirectory),
-      std::move(backtrace))
+      arguments.GetPackageFileName(), arguments.GetNamespace(),
+      std::move(cxxModulesDirectory), std::move(backtrace))
 {
   this->EFGen = cm::make_unique<cmExportInstallPackageInfoGenerator>(
-    this, std::move(packageName), std::move(version), std::move(versionCompat),
-    std::move(versionSchema), std::move(defaultTargets),
-    std::move(defaultConfigurations));
+    this, std::move(arguments));
 }
 
 cmInstallPackageInfoExportGenerator::~cmInstallPackageInfoExportGenerator() =

+ 4 - 5
Source/cmInstallPackageInfoExportGenerator.h

@@ -9,6 +9,7 @@
 
 class cmExportSet;
 class cmListFileBacktrace;
+class cmPackageInfoArguments;
 
 /** \class cmInstallPackageInfoGenerator
  * \brief Generate rules for creating CPS package info files.
@@ -20,11 +21,9 @@ public:
     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 packageName, std::string version, std::string versionCompat,
-    std::string versionSchema, std::vector<std::string> defaultTargets,
-    std::vector<std::string> defaultConfigurations,
-    std::string cxxModulesDirectory, cmListFileBacktrace backtrace);
+    MessageLevel message, bool excludeFromAll,
+    cmPackageInfoArguments arguments, std::string cxxModulesDirectory,
+    cmListFileBacktrace backtrace);
   cmInstallPackageInfoExportGenerator(
     cmInstallPackageInfoExportGenerator const&) = delete;
   ~cmInstallPackageInfoExportGenerator() override;

+ 188 - 0
Source/cmPackageInfoArguments.cxx

@@ -0,0 +1,188 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file LICENSE.rst or https://cmake.org/licensing for details.  */
+#include "cmPackageInfoArguments.h"
+
+#include <utility>
+
+#include "cmExecutionStatus.h"
+#include "cmGeneratorExpression.h"
+#include "cmMakefile.h"
+#include "cmStateSnapshot.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmValue.h"
+
+template void cmPackageInfoArguments::Bind<void>(cmArgumentParser<void>&,
+                                                 cmPackageInfoArguments*);
+
+bool cmPackageInfoArguments::Check(cmExecutionStatus& status,
+                                   bool enable) const
+{
+  if (!enable) {
+    // Check if any options were given.
+    if (this->LowerCase) {
+      status.SetError("LOWER_CASE_FILE requires PACKAGE_INFO.");
+      return false;
+    }
+    if (!this->Appendix.empty()) {
+      status.SetError("APPENDIX requires PACKAGE_INFO.");
+      return false;
+    }
+    if (!this->Version.empty()) {
+      status.SetError("VERSION requires PACKAGE_INFO.");
+      return false;
+    }
+    if (!this->DefaultTargets.empty()) {
+      status.SetError("DEFAULT_TARGETS requires PACKAGE_INFO.");
+      return false;
+    }
+    if (!this->DefaultConfigs.empty()) {
+      status.SetError("DEFAULT_CONFIGURATIONS requires PACKAGE_INFO.");
+      return false;
+    }
+    if (!this->ProjectName.empty()) {
+      status.SetError("PROJECT requires PACKAGE_INFO.");
+      return false;
+    }
+    if (this->NoProjectDefaults) {
+      status.SetError("NO_PROJECT_METADATA requires PACKAGE_INFO.");
+      return false;
+    }
+  }
+
+  // Check for incompatible options.
+  if (!this->Appendix.empty()) {
+    if (!this->Version.empty()) {
+      status.SetError("APPENDIX and VERSION are mutually exclusive.");
+      return false;
+    }
+    if (!this->DefaultTargets.empty()) {
+      status.SetError("APPENDIX and DEFAULT_TARGETS "
+                      "are mutually exclusive.");
+      return false;
+    }
+    if (!this->DefaultConfigs.empty()) {
+      status.SetError("APPENDIX and DEFAULT_CONFIGURATIONS "
+                      "are mutually exclusive.");
+      return false;
+    }
+    if (!this->ProjectName.empty()) {
+      status.SetError("APPENDIX and PROJECT are mutually exclusive.");
+      return false;
+    }
+  }
+  if (this->NoProjectDefaults) {
+    if (!this->ProjectName.empty()) {
+      status.SetError("PROJECT and NO_PROJECT_METADATA "
+                      "are mutually exclusive.");
+      return false;
+    }
+  }
+
+  // Check for options that require other options.
+  if (this->Version.empty()) {
+    if (!this->VersionCompat.empty()) {
+      status.SetError("COMPAT_VERSION requires VERSION.");
+      return false;
+    }
+    if (!this->VersionSchema.empty()) {
+      status.SetError("VERSION_SCHEMA requires VERSION.");
+      return false;
+    }
+  }
+
+  // Validate the package name.
+  if (!this->PackageName.empty()) {
+    if (!cmGeneratorExpression::IsValidTargetName(this->PackageName) ||
+        this->PackageName.find(':') != std::string::npos) {
+      status.SetError(
+        cmStrCat(R"(PACKAGE_INFO given invalid package name ")"_s,
+                 this->PackageName, R"(".)"_s));
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool cmPackageInfoArguments::SetMetadataFromProject(cmExecutionStatus& status)
+{
+  // Determine what project to use for inherited metadata.
+  if (!this->SetEffectiveProject(status)) {
+    return false;
+  }
+
+  if (this->ProjectName.empty()) {
+    // We are not inheriting from a project.
+    return true;
+  }
+
+  cmMakefile& mf = status.GetMakefile();
+  if (this->Version.empty()) {
+    cmValue const& version =
+      mf.GetDefinition(cmStrCat(this->ProjectName, "_VERSION"_s));
+    if (version) {
+      this->Version = version;
+      cmValue const& compatVersion =
+        mf.GetDefinition(cmStrCat(this->ProjectName, "_COMPAT_VERSION"_s));
+      if (compatVersion) {
+        this->VersionCompat = compatVersion;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool cmPackageInfoArguments::SetEffectiveProject(cmExecutionStatus& status)
+{
+  if (!this->Appendix.empty()) {
+    // Appendices are not allowed to specify package metadata.
+    return true;
+  }
+
+  if (this->NoProjectDefaults) {
+    // User requested that metadata not be inherited.
+    return true;
+  }
+
+  cmMakefile& mf = status.GetMakefile();
+  if (!this->ProjectName.empty()) {
+    // User specified a project; make sure it exists.
+    if (!mf.GetStateSnapshot().CheckProjectName(this->ProjectName)) {
+      status.SetError(cmStrCat(R"(PROJECT given invalid project name ")"_s,
+                               this->ProjectName, R"(".)"_s));
+      return false;
+    }
+  } else {
+    // No project was specified; check if the package name is also a project.
+    std::string project = mf.GetStateSnapshot().GetProjectName();
+    if (this->PackageName == project) {
+      this->ProjectName = std::move(project);
+    }
+  }
+
+  return true;
+}
+
+std::string cmPackageInfoArguments::GetNamespace() const
+{
+  return cmStrCat(this->PackageName, "::"_s);
+}
+
+std::string cmPackageInfoArguments::GetPackageDirName() const
+{
+  if (this->LowerCase) {
+    return cmSystemTools::LowerCase(this->PackageName);
+  }
+  return this->PackageName;
+}
+
+std::string cmPackageInfoArguments::GetPackageFileName() const
+{
+  std::string const pkgNameOnDisk = this->GetPackageDirName();
+  if (!this->Appendix.empty()) {
+    return cmStrCat(pkgNameOnDisk, '-', this->Appendix, ".cps"_s);
+  }
+  return cmStrCat(pkgNameOnDisk, ".cps"_s);
+}

+ 111 - 0
Source/cmPackageInfoArguments.h

@@ -0,0 +1,111 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file LICENSE.rst or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+#include <vector>
+
+#include <cm/type_traits>
+#include <cmext/string_view>
+
+#include "cmArgumentParser.h" // IWYU pragma: keep
+#include "cmArgumentParserTypes.h"
+
+class cmExecutionStatus;
+
+/** \class cmPackageInfoArguments
+ * \brief Convey information about a package.
+ *
+ * This class encapsulates several attributes of package metadata. It is used
+ * both as a convenience container to convey several values in a single
+ * container, and also provides utilities to obtain this metadata from commands
+ * which produce packages (i.e. export and install).
+ */
+class cmPackageInfoArguments
+{
+public:
+  template <typename T,
+            typename = cm::enable_if_t<
+              std::is_base_of<cmPackageInfoArguments, T>::value>>
+  static void Bind(cmArgumentParser<T>& parser)
+  {
+    cmPackageInfoArguments::Bind(parser, nullptr);
+  }
+
+  void Bind(cmArgumentParser<void>& parser)
+  {
+    cmPackageInfoArguments::Bind(parser, this);
+  }
+
+  std::string GetNamespace() const;
+  std::string GetPackageDirName() const;
+  std::string GetPackageFileName() const;
+
+  /// Ensure that no conflicting options were specified.  If \p enable is
+  /// \c false, forbid specifying any options whatsoever.
+  bool Check(cmExecutionStatus& status, bool enable = true) const;
+
+  /// Set metadata (not already specified) from either the specified project,
+  /// or from the project which matches the package name.
+  bool SetMetadataFromProject(cmExecutionStatus& status);
+
+  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::vector<std::string>> DefaultTargets;
+  ArgumentParser::NonEmpty<std::vector<std::string>> DefaultConfigs;
+  bool LowerCase = false;
+
+  ArgumentParser::NonEmpty<std::string> ProjectName;
+  bool NoProjectDefaults = false;
+
+private:
+  bool SetEffectiveProject(cmExecutionStatus& status);
+
+  template <typename T>
+  static void Bind(cmArgumentParser<T>& parser, cmPackageInfoArguments* self)
+  {
+    Bind(self, parser, "PACKAGE_INFO"_s, &cmPackageInfoArguments::PackageName);
+    Bind(self, parser, "LOWER_CASE_FILE"_s,
+         &cmPackageInfoArguments::LowerCase);
+    Bind(self, parser, "APPENDIX"_s, &cmPackageInfoArguments::Appendix);
+    Bind(self, parser, "VERSION"_s, &cmPackageInfoArguments::Version);
+    Bind(self, parser, "COMPAT_VERSION"_s,
+         &cmPackageInfoArguments::VersionCompat);
+    Bind(self, parser, "VERSION_SCHEMA"_s,
+         &cmPackageInfoArguments::VersionSchema);
+    Bind(self, parser, "DEFAULT_TARGETS"_s,
+         &cmPackageInfoArguments::DefaultTargets);
+    Bind(self, parser, "DEFAULT_CONFIGURATIONS"_s,
+         &cmPackageInfoArguments::DefaultConfigs);
+
+    Bind(self, parser, "PROJECT"_s, &cmPackageInfoArguments::ProjectName);
+    Bind(self, parser, "NO_PROJECT_METADATA"_s,
+         &cmPackageInfoArguments::NoProjectDefaults);
+  }
+
+  template <typename T, typename U,
+            typename = cm::enable_if_t<
+              std::is_base_of<cmPackageInfoArguments, T>::value>>
+  static void Bind(cmPackageInfoArguments*, cmArgumentParser<T>& parser,
+                   cm::static_string_view name,
+                   U cmPackageInfoArguments::*member)
+  {
+    parser.Bind(name, member);
+  }
+
+  template <typename U>
+  static void Bind(cmPackageInfoArguments* self,
+                   cmArgumentParser<void>& parser, cm::static_string_view name,
+                   U cmPackageInfoArguments::*member)
+  {
+    parser.Bind(name, (self)->*member);
+  }
+};
+
+extern template void cmPackageInfoArguments::Bind<void>(
+  cmArgumentParser<void>&, cmPackageInfoArguments*);

+ 3 - 0
Source/cmStatePrivate.h

@@ -5,6 +5,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <set>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -82,6 +83,8 @@ struct cmStateDetail::BuildsystemDirectoryStateType
   std::vector<std::string> NormalTargetNames;
   std::vector<std::string> ImportedTargetNames;
 
+  std::set<std::string> Projects;
+
   std::string ProjectName;
 
   cmPropertyMap Properties;

+ 8 - 0
Source/cmStateSnapshot.cxx

@@ -5,10 +5,12 @@
 
 #include <algorithm>
 #include <cassert>
+#include <set>
 #include <string>
 #include <unordered_map>
 
 #include <cm/iterator>
+#include <cmext/algorithm>
 
 #include "cmDefinitions.h"
 #include "cmLinkedTree.h"
@@ -413,6 +415,7 @@ cmStateDirectory cmStateSnapshot::GetDirectory() const
 void cmStateSnapshot::SetProjectName(std::string const& name)
 {
   this->Position->BuildSystemDirectory->ProjectName = name;
+  this->Position->BuildSystemDirectory->Projects.insert(name);
 }
 
 std::string cmStateSnapshot::GetProjectName() const
@@ -420,6 +423,11 @@ std::string cmStateSnapshot::GetProjectName() const
   return this->Position->BuildSystemDirectory->ProjectName;
 }
 
+bool cmStateSnapshot::CheckProjectName(std::string const& name) const
+{
+  return cm::contains(this->Position->BuildSystemDirectory->Projects, name);
+}
+
 cmPackageState& cmStateSnapshot::GetPackageState(
   std::string const& packagePath)
 {

+ 1 - 0
Source/cmStateSnapshot.h

@@ -57,6 +57,7 @@ public:
 
   void SetProjectName(std::string const& name);
   std::string GetProjectName() const;
+  bool CheckProjectName(std::string const& name) const;
 
   cmPackageState& GetPackageState(std::string const& packagePath);
 

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

@@ -8,3 +8,15 @@ CMake Error at BadArgs1\.cmake:4 \(export\):
   export VERSION_SCHEMA requires VERSION\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs1\.cmake:5 \(export\):
+  export PROJECT and NO_PROJECT_METADATA are mutually exclusive\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs1\.cmake:6 \(export\):
+  export PROJECT given invalid project name "bar"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

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

@@ -2,3 +2,5 @@ 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)
+export(EXPORT foo PACKAGE_INFO foo PROJECT foo NO_PROJECT_METADATA)
+export(EXPORT foo PACKAGE_INFO foo PROJECT bar)

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

@@ -14,3 +14,9 @@ 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\)
+
+
+CMake Error at BadArgs2\.cmake:6 \(export\):
+  export APPENDIX and PROJECT are mutually exclusive\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

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

@@ -3,3 +3,4 @@ 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)
+export(EXPORT foo PACKAGE_INFO foo APPENDIX test PROJECT foo)

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

@@ -26,3 +26,15 @@ CMake Error at BadArgs4\.cmake:7 \(export\):
   export DEFAULT_CONFIGURATIONS requires PACKAGE_INFO\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs4\.cmake:8 \(export\):
+  export PROJECT requires PACKAGE_INFO\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs4\.cmake:9 \(export\):
+  export NO_PROJECT_METADATA requires PACKAGE_INFO\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

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

@@ -5,3 +5,5 @@ export(EXPORT foo APPENDIX test)
 export(EXPORT foo VERSION 1.0)
 export(EXPORT foo DEFAULT_TARGETS foo)
 export(EXPORT foo DEFAULT_CONFIGURATIONS Release)
+export(EXPORT foo PROJECT foo)
+export(EXPORT foo NO_PROJECT_METADATA)

+ 8 - 0
Tests/RunCMake/ExportPackageInfo/NoProjectMetadata-check.cmake

@@ -0,0 +1,8 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/NoProjectMetadata-build")
+
+file(READ "${out_dir}/foo.cps" content)
+expect_value("${content}" "foo" "name")
+expect_missing("${content}" "version")
+expect_missing("${content}" "compat_version")

+ 10 - 0
Tests/RunCMake/ExportPackageInfo/NoProjectMetadata.cmake

@@ -0,0 +1,10 @@
+project(foo VERSION 1.2.3 COMPAT_VERSION 1.1.0)
+
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+
+export(
+  EXPORT foo
+  PACKAGE_INFO foo
+  NO_PROJECT_METADATA
+  )

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

@@ -0,0 +1,18 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/ProjectMetadata-build")
+
+file(READ "${out_dir}/foo.cps" content)
+expect_value("${content}" "foo" "name")
+expect_value("${content}" "1.2.3" "version")
+expect_value("${content}" "1.1.0" "compat_version")
+
+file(READ "${out_dir}/test1.cps" content)
+expect_value("${content}" "test1" "name")
+expect_value("${content}" "1.2.3" "version")
+expect_value("${content}" "1.1.0" "compat_version")
+
+file(READ "${out_dir}/test2.cps" content)
+expect_value("${content}" "test2" "name")
+expect_value("${content}" "1.4.7" "version")
+expect_missing("${content}" "compat_version")

+ 25 - 0
Tests/RunCMake/ExportPackageInfo/ProjectMetadata.cmake

@@ -0,0 +1,25 @@
+project(foo VERSION 1.2.3 COMPAT_VERSION 1.1.0)
+
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+
+# Test inheriting from project matching package name.
+export(
+  EXPORT foo
+  PACKAGE_INFO foo
+  )
+
+# Test inheriting from a specified project.
+export(
+  EXPORT foo
+  PROJECT foo
+  PACKAGE_INFO test1
+  )
+
+# Test that inheriting doesn't override explicitly specified metadata.
+export(
+  EXPORT foo
+  PROJECT foo
+  PACKAGE_INFO test2
+  VERSION 1.4.7
+  )

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

@@ -31,6 +31,8 @@ run_cmake(LinkOnlyRecursive)
 run_cmake(Appendix)
 run_cmake(InterfaceProperties)
 run_cmake(Metadata)
+run_cmake(ProjectMetadata)
+run_cmake(NoProjectMetadata)
 run_cmake(Minimal)
 run_cmake(MinimalVersion)
 run_cmake(LowerCaseFile)

+ 20 - 0
Tests/RunCMake/InstallPackageInfo/BadArgs1-stderr.txt

@@ -1,2 +1,22 @@
 CMake Error at BadArgs1.cmake:3 \(install\):
   install COMPAT_VERSION requires VERSION.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs1.cmake:4 \(install\):
+  install VERSION_SCHEMA requires VERSION.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs1\.cmake:5 \(install\):
+  install PROJECT and NO_PROJECT_METADATA are mutually exclusive\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs1\.cmake:6 \(install\):
+  install PROJECT given invalid project name "bar"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

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

@@ -1,3 +1,6 @@
 add_library(foo INTERFACE)
 install(TARGETS foo EXPORT foo DESTINATION .)
 install(PACKAGE_INFO test EXPORT foo COMPAT_VERSION 1.0)
+install(PACKAGE_INFO test EXPORT foo VERSION_SCHEMA simple)
+install(PACKAGE_INFO test EXPORT foo PROJECT foo NO_PROJECT_METADATA)
+install(PACKAGE_INFO test EXPORT foo PROJECT bar)

+ 21 - 1
Tests/RunCMake/InstallPackageInfo/BadArgs2-stderr.txt

@@ -1,2 +1,22 @@
 CMake Error at BadArgs2.cmake:3 \(install\):
-  install VERSION_SCHEMA requires VERSION.
+  install APPENDIX and VERSION are mutually exclusive.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs2.cmake:4 \(install\):
+  install APPENDIX and DEFAULT_TARGETS are mutually exclusive.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs2.cmake:5 \(install\):
+  install APPENDIX and DEFAULT_CONFIGURATIONS are mutually exclusive.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at BadArgs2.cmake:6 \(install\):
+  install APPENDIX and PROJECT are mutually exclusive.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

+ 4 - 1
Tests/RunCMake/InstallPackageInfo/BadArgs2.cmake

@@ -1,3 +1,6 @@
 add_library(foo INTERFACE)
 install(TARGETS foo EXPORT foo DESTINATION .)
-install(PACKAGE_INFO test EXPORT foo VERSION_SCHEMA simple)
+install(PACKAGE_INFO test EXPORT foo APPENDIX test VERSION 1.0)
+install(PACKAGE_INFO test EXPORT foo APPENDIX test DEFAULT_TARGETS foo)
+install(PACKAGE_INFO test EXPORT foo APPENDIX test DEFAULT_CONFIGURATIONS test)
+install(PACKAGE_INFO test EXPORT foo APPENDIX test PROJECT foo)

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

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

+ 0 - 2
Tests/RunCMake/InstallPackageInfo/BadArgs3-stderr.txt

@@ -1,2 +0,0 @@
-CMake Error at BadArgs3.cmake:3 \(install\):
-  install APPENDIX and VERSION are mutually exclusive.

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

@@ -1,3 +0,0 @@
-add_library(foo INTERFACE)
-install(TARGETS foo EXPORT foo DESTINATION .)
-install(PACKAGE_INFO test EXPORT foo APPENDIX test VERSION 1.0)

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

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

+ 0 - 2
Tests/RunCMake/InstallPackageInfo/BadArgs4-stderr.txt

@@ -1,2 +0,0 @@
-CMake Error at BadArgs4.cmake:3 \(install\):
-  install APPENDIX and DEFAULT_TARGETS are mutually exclusive.

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

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

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

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

+ 0 - 2
Tests/RunCMake/InstallPackageInfo/BadArgs5-stderr.txt

@@ -1,2 +0,0 @@
-CMake Error at BadArgs5.cmake:3 \(install\):
-  install APPENDIX and DEFAULT_CONFIGURATIONS are mutually exclusive.

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

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

+ 8 - 0
Tests/RunCMake/InstallPackageInfo/NoProjectMetadata-check.cmake

@@ -0,0 +1,8 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/NoProjectMetadata-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983")
+
+file(READ "${out_dir}/foo.cps" content)
+expect_value("${content}" "foo" "name")
+expect_missing("${content}" "version")
+expect_missing("${content}" "compat_version")

+ 11 - 0
Tests/RunCMake/InstallPackageInfo/NoProjectMetadata.cmake

@@ -0,0 +1,11 @@
+project(foo VERSION 1.2.3 COMPAT_VERSION 1.1.0)
+
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+
+install(
+  PACKAGE_INFO foo
+  DESTINATION cps
+  EXPORT foo
+  NO_PROJECT_METADATA
+  )

+ 18 - 0
Tests/RunCMake/InstallPackageInfo/ProjectMetadata-check.cmake

@@ -0,0 +1,18 @@
+include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
+
+set(out_dir "${RunCMake_BINARY_DIR}/ProjectMetadata-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983")
+
+file(READ "${out_dir}/foo.cps" content)
+expect_value("${content}" "foo" "name")
+expect_value("${content}" "1.2.3" "version")
+expect_value("${content}" "1.1.0" "compat_version")
+
+file(READ "${out_dir}/test1.cps" content)
+expect_value("${content}" "test1" "name")
+expect_value("${content}" "1.2.3" "version")
+expect_value("${content}" "1.1.0" "compat_version")
+
+file(READ "${out_dir}/test2.cps" content)
+expect_value("${content}" "test2" "name")
+expect_value("${content}" "1.4.7" "version")
+expect_missing("${content}" "compat_version")

+ 28 - 0
Tests/RunCMake/InstallPackageInfo/ProjectMetadata.cmake

@@ -0,0 +1,28 @@
+project(foo VERSION 1.2.3 COMPAT_VERSION 1.1.0)
+
+add_library(foo INTERFACE)
+install(TARGETS foo EXPORT foo DESTINATION .)
+
+# Test inheriting from project matching package name.
+install(
+  PACKAGE_INFO foo
+  DESTINATION cps
+  EXPORT foo
+  )
+
+# Test inheriting from a specified project.
+install(
+  PACKAGE_INFO test1
+  DESTINATION cps
+  EXPORT foo
+  PROJECT foo
+  )
+
+# Test that inheriting doesn't override explicitly specified metadata.
+install(
+  PACKAGE_INFO test2
+  DESTINATION cps
+  EXPORT foo
+  PROJECT foo
+  VERSION 1.4.7
+  )

+ 2 - 3
Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake

@@ -26,9 +26,6 @@ endfunction()
 # Test incorrect usage
 run_cmake(BadArgs1)
 run_cmake(BadArgs2)
-run_cmake(BadArgs3)
-run_cmake(BadArgs4)
-run_cmake(BadArgs5)
 run_cmake(BadName)
 run_cmake(BadDefaultTarget)
 run_cmake(ReferencesNonExportedTarget)
@@ -42,6 +39,8 @@ run_cmake(DependsMultipleDifferentSets)
 run_cmake(Appendix)
 run_cmake(InterfaceProperties)
 run_cmake(Metadata)
+run_cmake(ProjectMetadata)
+run_cmake(NoProjectMetadata)
 run_cmake(Minimal)
 run_cmake(MinimalVersion)
 run_cmake(LowerCaseFile)