Browse Source

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 7 months ago
parent
commit
7dbffb3aa1
47 changed files with 586 additions and 224 deletions
  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
 .. code-block:: cmake
 
 
   export(EXPORT <export-name> PACKAGE_INFO <package-name>
   export(EXPORT <export-name> PACKAGE_INFO <package-name>
+         [PROJECT <project-name>|NO_PROJECT_METADATA]
          [APPENDIX <appendix-name>]
          [APPENDIX <appendix-name>]
          [LOWER_CASE_FILE]
          [LOWER_CASE_FILE]
          [VERSION <version>
          [VERSION <version>

+ 10 - 1
Help/command/install.rst

@@ -992,6 +992,7 @@ Signatures
   .. code-block:: cmake
   .. code-block:: cmake
 
 
     install(PACKAGE_INFO <package-name> EXPORT <export-name>
     install(PACKAGE_INFO <package-name> EXPORT <export-name>
+            [PROJECT <project-name>|NO_PROJECT_METADATA]
             [APPENDIX <appendix-name>]
             [APPENDIX <appendix-name>]
             [DESTINATION <dir>]
             [DESTINATION <dir>]
             [LOWER_CASE_FILE]
             [LOWER_CASE_FILE]
@@ -1056,6 +1057,14 @@ Signatures
     configurations exists.  If not specified, CMake will fall back to the
     configurations exists.  If not specified, CMake will fall back to the
     package's available configurations in an unspecified order.
     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
   If ``APPENDIX`` is specified, rather than generating a top level package
   specification, the specified targets will be exported as an appendix to the
   specification, the specified targets will be exported as an appendix to the
   named package.  Appendices may be used to separate less commonly used targets
   named package.  Appendices may be used to separate less commonly used targets
@@ -1065,7 +1074,7 @@ Signatures
   artifacts produced by multiple build trees.
   artifacts produced by multiple build trees.
 
 
   Appendices are not permitted to change basic package metadata; therefore,
   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
   ``DEFAULT_TARGETS`` or ``DEFAULT_CONFIGURATIONS`` may be specified in
   combination with ``APPENDIX``.  Additionally, it is strongly recommended that
   combination with ``APPENDIX``.  Additionally, it is strongly recommended that
   use of ``LOWER_CASE_FILE`` should be consistent between the main package and
   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
   cmNewLineStyle.cxx
   cmOrderDirectories.cxx
   cmOrderDirectories.cxx
   cmOrderDirectories.h
   cmOrderDirectories.h
+  cmPackageInfoArguments.cxx
+  cmPackageInfoArguments.h
   cmPackageInfoReader.cxx
   cmPackageInfoReader.cxx
   cmPackageInfoReader.h
   cmPackageInfoReader.h
   cmPackageState.h
   cmPackageState.h

+ 3 - 7
Source/cmExportBuildPackageInfoGenerator.cxx

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

+ 2 - 5
Source/cmExportBuildPackageInfoGenerator.h

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

+ 9 - 82
Source/cmExportCommand.cxx

@@ -24,10 +24,10 @@
 #include "cmExportBuildPackageInfoGenerator.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"
+#include "cmPackageInfoArguments.h"
 #include "cmPolicies.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmRange.h"
 #include "cmStateTypes.h"
 #include "cmStateTypes.h"
@@ -63,24 +63,16 @@ bool cmExportCommand(std::vector<std::string> const& args,
     return HandlePackage(args, status);
     return HandlePackage(args, status);
   }
   }
 
 
-  struct Arguments
+  struct Arguments : cmPackageInfoArguments
   {
   {
     cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Targets;
     cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Targets;
     ArgumentParser::NonEmpty<std::string> ExportSetName;
     ArgumentParser::NonEmpty<std::string> ExportSetName;
     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;
@@ -104,14 +96,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
     }
     }
     if (cmExperimental::HasSupportEnabled(
     if (cmExperimental::HasSupportEnabled(
           status.GetMakefile(), cmExperimental::Feature::ExportPackageInfo)) {
           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") {
   } else if (args[0] == "SETUP") {
     parser.Bind("SETUP"_s, &Arguments::ExportSetName);
     parser.Bind("SETUP"_s, &Arguments::ExportSetName);
@@ -129,7 +114,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
   }
   }
 
 
   std::vector<std::string> unknownArgs;
   std::vector<std::string> unknownArgs;
-  Arguments const arguments = parser.Parse(args, &unknownArgs);
+  Arguments arguments = parser.Parse(args, &unknownArgs);
 
 
   if (!unknownArgs.empty()) {
   if (!unknownArgs.empty()) {
     status.SetError("Unknown argument: \"" + unknownArgs.front() + "\".");
     status.SetError("Unknown argument: \"" + unknownArgs.front() + "\".");
@@ -222,24 +207,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
   }
   }
 
 
   if (arguments.PackageName.empty()) {
   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;
       return false;
     }
     }
   } else {
   } else {
@@ -251,30 +219,8 @@ bool cmExportCommand(std::vector<std::string> const& args,
       status.SetError("PACKAGE_INFO and NAMESPACE are mutually exclusive.");
       status.SetError("PACKAGE_INFO and NAMESPACE are mutually exclusive.");
       return false;
       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;
       return false;
     }
     }
   }
   }
@@ -293,23 +239,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
     if (arguments.PackageName.empty()) {
     if (arguments.PackageName.empty()) {
       fname = arguments.ExportSetName + ".cmake";
       fname = arguments.ExportSetName + ".cmake";
     } else {
     } 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;
       cps = true;
     }
     }
   } else {
   } else {
@@ -430,10 +360,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
     ebag->SetAppendMode(arguments.Append);
     ebag->SetAppendMode(arguments.Append);
     ebfg = std::move(ebag);
     ebfg = std::move(ebag);
   } else if (cps) {
   } 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);
     ebfg = std::move(ebpg);
   } else {
   } else {
     auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>();
     auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>();

+ 3 - 8
Source/cmExportInstallPackageInfoGenerator.cxx

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

+ 3 - 6
Source/cmExportInstallPackageInfoGenerator.h

@@ -6,13 +6,13 @@
 
 
 #include <iosfwd>
 #include <iosfwd>
 #include <string>
 #include <string>
-#include <vector>
 
 
 #include "cmExportInstallFileGenerator.h"
 #include "cmExportInstallFileGenerator.h"
 #include "cmExportPackageInfoGenerator.h"
 #include "cmExportPackageInfoGenerator.h"
 
 
 class cmGeneratorTarget;
 class cmGeneratorTarget;
 class cmInstallExportGenerator;
 class cmInstallExportGenerator;
+class cmPackageInfoArguments;
 
 
 /** \class cmExportInstallPackageInfoGenerator
 /** \class cmExportInstallPackageInfoGenerator
  * \brief Generate files exporting targets from an install tree.
  * \brief Generate files exporting targets from an install tree.
@@ -35,11 +35,8 @@ class cmExportInstallPackageInfoGenerator
 public:
 public:
   /** Construct with the export installer that will install the
   /** Construct with the export installer that will install the
       files.  */
       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
   /** Compute the globbing expression used to load per-config import
       files from the main file.  */
       files from the main file.  */

+ 9 - 9
Source/cmExportPackageInfoGenerator.cxx

@@ -15,6 +15,7 @@
 #include <cm3p/json/value.h>
 #include <cm3p/json/value.h>
 #include <cm3p/json/writer.h>
 #include <cm3p/json/writer.h>
 
 
+#include "cmArgumentParserTypes.h"
 #include "cmExportSet.h"
 #include "cmExportSet.h"
 #include "cmFindPackageStack.h"
 #include "cmFindPackageStack.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpression.h"
@@ -22,6 +23,7 @@
 #include "cmList.h"
 #include "cmList.h"
 #include "cmMakefile.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmMessageType.h"
+#include "cmPackageInfoArguments.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTarget.h"
@@ -30,15 +32,13 @@
 static std::string const kCPS_VERSION_STR = "0.13.0";
 static std::string const kCPS_VERSION_STR = "0.13.0";
 
 
 cmExportPackageInfoGenerator::cmExportPackageInfoGenerator(
 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 "cmExportFileGenerator.h"
 #include "cmStateTypes.h"
 #include "cmStateTypes.h"
 
 
-class cmGeneratorTarget;
 namespace Json {
 namespace Json {
 class Value;
 class Value;
 }
 }
 
 
+class cmGeneratorTarget;
+class cmPackageInfoArguments;
+
 /** \class cmExportPackageInfoGenerator
 /** \class cmExportPackageInfoGenerator
  * \brief Generate Common Package Specification package information files
  * \brief Generate Common Package Specification package information files
  * exporting targets from a build or install tree.
  * exporting targets from a build or install tree.
@@ -32,11 +34,7 @@ class Value;
 class cmExportPackageInfoGenerator : virtual public cmExportFileGenerator
 class cmExportPackageInfoGenerator : virtual public cmExportFileGenerator
 {
 {
 public:
 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;
   using cmExportFileGenerator::GenerateImportFile;
 
 

+ 7 - 63
Source/cmInstallCommand.cxx

@@ -45,6 +45,7 @@
 #include "cmList.h"
 #include "cmList.h"
 #include "cmMakefile.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmMessageType.h"
+#include "cmPackageInfoArguments.h"
 #include "cmPolicies.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmRange.h"
 #include "cmRuntimeDependencyArchive.h"
 #include "cmRuntimeDependencyArchive.h"
@@ -2174,26 +2175,12 @@ bool HandlePackageInfoMode(std::vector<std::string> const& args,
   // This is the PACKAGE_INFO mode.
   // This is the PACKAGE_INFO mode.
   cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile);
   cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile);
 
 
-  ArgumentParser::NonEmpty<std::string> pkg;
-  ArgumentParser::NonEmpty<std::string> appendix;
+  cmPackageInfoArguments arguments;
   ArgumentParser::NonEmpty<std::string> exportName;
   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;
   ArgumentParser::NonEmpty<std::string> cxxModulesDirectory;
 
 
-  ica.Bind("PACKAGE_INFO"_s, pkg);
+  arguments.Bind(ica);
   ica.Bind("EXPORT"_s, exportName);
   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?
   // ica.Bind("CXX_MODULES_DIRECTORY"_s, cxxModulesDirectory); TODO?
 
 
   std::vector<std::string> unknownArgs;
   std::vector<std::string> unknownArgs;
@@ -2215,51 +2202,10 @@ bool HandlePackageInfoMode(std::vector<std::string> const& args,
     return false;
     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;
     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.
   // Get or construct the destination path.
   std::string dest = ica.GetDestination();
   std::string dest = ica.GetDestination();
   if (dest.empty()) {
   if (dest.empty()) {
@@ -2267,7 +2213,7 @@ bool HandlePackageInfoMode(std::vector<std::string> const& args,
       dest = std::string{ "cps"_s };
       dest = std::string{ "cps"_s };
     } else {
     } else {
       dest = cmStrCat(helper.GetLibraryDestination(nullptr), "/cps/",
       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>(
     cm::make_unique<cmInstallPackageInfoExportGenerator>(
       &exportSet, dest, ica.GetPermissions(), ica.GetConfigurations(),
       &exportSet, dest, ica.GetPermissions(), ica.GetConfigurations(),
       ica.GetComponent(), message, ica.GetExcludeFromAll(),
       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;
   return true;
 #else
 #else

+ 5 - 9
Source/cmInstallPackageInfoExportGenerator.cxx

@@ -9,27 +9,23 @@
 #include "cmExportInstallFileGenerator.h"
 #include "cmExportInstallFileGenerator.h"
 #include "cmExportInstallPackageInfoGenerator.h"
 #include "cmExportInstallPackageInfoGenerator.h"
 #include "cmListFileCache.h"
 #include "cmListFileCache.h"
+#include "cmPackageInfoArguments.h"
 
 
 class cmExportSet;
 class cmExportSet;
 
 
 cmInstallPackageInfoExportGenerator::cmInstallPackageInfoExportGenerator(
 cmInstallPackageInfoExportGenerator::cmInstallPackageInfoExportGenerator(
   cmExportSet* exportSet, std::string destination, std::string filePermissions,
   cmExportSet* exportSet, std::string destination, std::string filePermissions,
   std::vector<std::string> const& configurations, std::string component,
   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)
   std::string cxxModulesDirectory, cmListFileBacktrace backtrace)
   : cmInstallExportGenerator(
   : cmInstallExportGenerator(
       exportSet, std::move(destination), std::move(filePermissions),
       exportSet, std::move(destination), std::move(filePermissions),
       configurations, std::move(component), message, excludeFromAll,
       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->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() =
 cmInstallPackageInfoExportGenerator::~cmInstallPackageInfoExportGenerator() =

+ 4 - 5
Source/cmInstallPackageInfoExportGenerator.h

@@ -9,6 +9,7 @@
 
 
 class cmExportSet;
 class cmExportSet;
 class cmListFileBacktrace;
 class cmListFileBacktrace;
+class cmPackageInfoArguments;
 
 
 /** \class cmInstallPackageInfoGenerator
 /** \class cmInstallPackageInfoGenerator
  * \brief Generate rules for creating CPS package info files.
  * \brief Generate rules for creating CPS package info files.
@@ -20,11 +21,9 @@ public:
     cmExportSet* exportSet, std::string destination,
     cmExportSet* exportSet, std::string destination,
     std::string filePermissions,
     std::string filePermissions,
     std::vector<std::string> const& configurations, std::string component,
     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(
     cmInstallPackageInfoExportGenerator const&) = delete;
     cmInstallPackageInfoExportGenerator const&) = delete;
   ~cmInstallPackageInfoExportGenerator() override;
   ~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 "cmConfigure.h" // IWYU pragma: keep
 
 
+#include <set>
 #include <string>
 #include <string>
 #include <unordered_map>
 #include <unordered_map>
 #include <vector>
 #include <vector>
@@ -82,6 +83,8 @@ struct cmStateDetail::BuildsystemDirectoryStateType
   std::vector<std::string> NormalTargetNames;
   std::vector<std::string> NormalTargetNames;
   std::vector<std::string> ImportedTargetNames;
   std::vector<std::string> ImportedTargetNames;
 
 
+  std::set<std::string> Projects;
+
   std::string ProjectName;
   std::string ProjectName;
 
 
   cmPropertyMap Properties;
   cmPropertyMap Properties;

+ 8 - 0
Source/cmStateSnapshot.cxx

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

+ 1 - 0
Source/cmStateSnapshot.h

@@ -57,6 +57,7 @@ public:
 
 
   void SetProjectName(std::string const& name);
   void SetProjectName(std::string const& name);
   std::string GetProjectName() const;
   std::string GetProjectName() const;
+  bool CheckProjectName(std::string const& name) const;
 
 
   cmPackageState& GetPackageState(std::string const& packagePath);
   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\.
   export VERSION_SCHEMA requires VERSION\.
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:3 \(include\)
   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 .)
 install(TARGETS foo EXPORT foo DESTINATION .)
 export(EXPORT foo PACKAGE_INFO foo COMPAT_VERSION 1.0)
 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 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\.
   export APPENDIX and DEFAULT_CONFIGURATIONS are mutually exclusive\.
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:3 \(include\)
   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 VERSION 1.0)
 export(EXPORT foo PACKAGE_INFO foo APPENDIX test DEFAULT_TARGETS foo)
 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 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\.
   export DEFAULT_CONFIGURATIONS requires PACKAGE_INFO\.
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:3 \(include\)
   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 VERSION 1.0)
 export(EXPORT foo DEFAULT_TARGETS foo)
 export(EXPORT foo DEFAULT_TARGETS foo)
 export(EXPORT foo DEFAULT_CONFIGURATIONS Release)
 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(Appendix)
 run_cmake(InterfaceProperties)
 run_cmake(InterfaceProperties)
 run_cmake(Metadata)
 run_cmake(Metadata)
+run_cmake(ProjectMetadata)
+run_cmake(NoProjectMetadata)
 run_cmake(Minimal)
 run_cmake(Minimal)
 run_cmake(MinimalVersion)
 run_cmake(MinimalVersion)
 run_cmake(LowerCaseFile)
 run_cmake(LowerCaseFile)

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

@@ -1,2 +1,22 @@
 CMake Error at BadArgs1.cmake:3 \(install\):
 CMake Error at BadArgs1.cmake:3 \(install\):
   install COMPAT_VERSION requires VERSION.
   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)
 add_library(foo INTERFACE)
 install(TARGETS foo EXPORT foo DESTINATION .)
 install(TARGETS foo EXPORT foo DESTINATION .)
 install(PACKAGE_INFO test EXPORT foo COMPAT_VERSION 1.0)
 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\):
 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)
 add_library(foo INTERFACE)
 install(TARGETS foo EXPORT foo DESTINATION .)
 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
 # Test incorrect usage
 run_cmake(BadArgs1)
 run_cmake(BadArgs1)
 run_cmake(BadArgs2)
 run_cmake(BadArgs2)
-run_cmake(BadArgs3)
-run_cmake(BadArgs4)
-run_cmake(BadArgs5)
 run_cmake(BadName)
 run_cmake(BadName)
 run_cmake(BadDefaultTarget)
 run_cmake(BadDefaultTarget)
 run_cmake(ReferencesNonExportedTarget)
 run_cmake(ReferencesNonExportedTarget)
@@ -42,6 +39,8 @@ run_cmake(DependsMultipleDifferentSets)
 run_cmake(Appendix)
 run_cmake(Appendix)
 run_cmake(InterfaceProperties)
 run_cmake(InterfaceProperties)
 run_cmake(Metadata)
 run_cmake(Metadata)
+run_cmake(ProjectMetadata)
+run_cmake(NoProjectMetadata)
 run_cmake(Minimal)
 run_cmake(Minimal)
 run_cmake(MinimalVersion)
 run_cmake(MinimalVersion)
 run_cmake(LowerCaseFile)
 run_cmake(LowerCaseFile)