From 0ccbe5a2e1e3e8144cb5433bd1669f62753e8d9a Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Mon, 10 Mar 2025 14:41:53 -0400 Subject: [PATCH 1/4] CPS: Refactor argument parsing CPS package metadata involves a fair amount of passing around a particular set of values, as well as shared argument handling, which is only going to increase as additional features are added. In order to reduce code duplication (now and going forward), create a helper class to handle the shared argument parsing and to serve as a container to pass metadata values. --- Source/CMakeLists.txt | 2 + Source/cmExportBuildPackageInfoGenerator.cxx | 10 +- Source/cmExportBuildPackageInfoGenerator.h | 7 +- Source/cmExportCommand.cxx | 88 ++------------- .../cmExportInstallPackageInfoGenerator.cxx | 11 +- Source/cmExportInstallPackageInfoGenerator.h | 9 +- Source/cmExportPackageInfoGenerator.cxx | 18 +-- Source/cmExportPackageInfoGenerator.h | 10 +- Source/cmInstallCommand.cxx | 70 ++---------- .../cmInstallPackageInfoExportGenerator.cxx | 14 +-- Source/cmInstallPackageInfoExportGenerator.h | 9 +- Source/cmPackageInfoArguments.cxx | 104 ++++++++++++++++++ Source/cmPackageInfoArguments.h | 98 +++++++++++++++++ 13 files changed, 251 insertions(+), 199 deletions(-) create mode 100644 Source/cmPackageInfoArguments.cxx create mode 100644 Source/cmPackageInfoArguments.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 59bd8cef59..b1c8290d78 100644 --- a/Source/CMakeLists.txt +++ b/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 diff --git a/Source/cmExportBuildPackageInfoGenerator.cxx b/Source/cmExportBuildPackageInfoGenerator.cxx index f609624775..775360194d 100644 --- a/Source/cmExportBuildPackageInfoGenerator.cxx +++ b/Source/cmExportBuildPackageInfoGenerator.cxx @@ -11,17 +11,13 @@ #include #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 defaultTargets, - std::vector 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)); } diff --git a/Source/cmExportBuildPackageInfoGenerator.h b/Source/cmExportBuildPackageInfoGenerator.h index 7bca3e2398..0851d332a8 100644 --- a/Source/cmExportBuildPackageInfoGenerator.h +++ b/Source/cmExportBuildPackageInfoGenerator.h @@ -6,7 +6,6 @@ #include #include -#include #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 defaultTargets, - std::vector defaultConfigurations); + cmExportBuildPackageInfoGenerator(cmPackageInfoArguments arguments); protected: // Implement virtual methods from the superclass. diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index af81fe31fe..25f09a753a 100644 --- a/Source/cmExportCommand.cxx +++ b/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 const& args, return HandlePackage(args, status); } - struct Arguments + struct Arguments : cmPackageInfoArguments { cm::optional>> Targets; ArgumentParser::NonEmpty ExportSetName; ArgumentParser::NonEmpty Namespace; ArgumentParser::NonEmpty Filename; ArgumentParser::NonEmpty AndroidMKFile; - ArgumentParser::NonEmpty PackageName; - ArgumentParser::NonEmpty Appendix; - ArgumentParser::NonEmpty Version; - ArgumentParser::NonEmpty VersionCompat; - ArgumentParser::NonEmpty VersionSchema; ArgumentParser::NonEmpty CxxModulesDirectory; - ArgumentParser::NonEmpty> DefaultTargets; - ArgumentParser::NonEmpty> DefaultConfigs; bool Append = false; bool ExportOld = false; - bool LowerCase = false; std::vector> PackageDependencyArgs; bool ExportPackageDependencies = false; @@ -104,14 +96,7 @@ bool cmExportCommand(std::vector 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); @@ -222,24 +207,7 @@ bool cmExportCommand(std::vector 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,7 @@ bool cmExportCommand(std::vector 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)) { return false; } } @@ -293,23 +238,7 @@ bool cmExportCommand(std::vector 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 +359,7 @@ bool cmExportCommand(std::vector const& args, ebag->SetAppendMode(arguments.Append); ebfg = std::move(ebag); } else if (cps) { - auto ebpg = cm::make_unique( - arguments.PackageName, arguments.Version, arguments.VersionCompat, - arguments.VersionSchema, arguments.DefaultTargets, - arguments.DefaultConfigs); + auto ebpg = cm::make_unique(arguments); ebfg = std::move(ebpg); } else { auto ebcg = cm::make_unique(); diff --git a/Source/cmExportInstallPackageInfoGenerator.cxx b/Source/cmExportInstallPackageInfoGenerator.cxx index 98959d356e..1571c50b2a 100644 --- a/Source/cmExportInstallPackageInfoGenerator.cxx +++ b/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 defaultTargets, - std::vector 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) { } diff --git a/Source/cmExportInstallPackageInfoGenerator.h b/Source/cmExportInstallPackageInfoGenerator.h index d7f9c27039..abdd0151fc 100644 --- a/Source/cmExportInstallPackageInfoGenerator.h +++ b/Source/cmExportInstallPackageInfoGenerator.h @@ -6,13 +6,13 @@ #include #include -#include #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 defaultTargets, - std::vector defaultConfigurations); + cmExportInstallPackageInfoGenerator(cmInstallExportGenerator* iegen, + cmPackageInfoArguments arguments); /** Compute the globbing expression used to load per-config import files from the main file. */ diff --git a/Source/cmExportPackageInfoGenerator.cxx b/Source/cmExportPackageInfoGenerator.cxx index 9887b8d58d..71df7456e2 100644 --- a/Source/cmExportPackageInfoGenerator.cxx +++ b/Source/cmExportPackageInfoGenerator.cxx @@ -15,6 +15,7 @@ #include #include +#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 defaultTargets, - std::vector 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)) { } diff --git a/Source/cmExportPackageInfoGenerator.h b/Source/cmExportPackageInfoGenerator.h index 1392ba3ecf..1f849c9b67 100644 --- a/Source/cmExportPackageInfoGenerator.h +++ b/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 defaultTargets, - std::vector defaultConfigurations); + cmExportPackageInfoGenerator(cmPackageInfoArguments arguments); using cmExportFileGenerator::GenerateImportFile; diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 48586dce8e..3573207588 100644 --- a/Source/cmInstallCommand.cxx +++ b/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 const& args, // This is the PACKAGE_INFO mode. cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile); - ArgumentParser::NonEmpty pkg; - ArgumentParser::NonEmpty appendix; + cmPackageInfoArguments arguments; ArgumentParser::NonEmpty exportName; - bool lowerCase = false; - ArgumentParser::NonEmpty version; - ArgumentParser::NonEmpty versionCompat; - ArgumentParser::NonEmpty versionSchema; - ArgumentParser::NonEmpty> defaultTargets; - ArgumentParser::NonEmpty> defaultConfigs; ArgumentParser::NonEmpty 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 unknownArgs; @@ -2215,51 +2202,10 @@ bool HandlePackageInfoMode(std::vector 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)) { 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 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 const& args, cm::make_unique( &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 diff --git a/Source/cmInstallPackageInfoExportGenerator.cxx b/Source/cmInstallPackageInfoExportGenerator.cxx index 756ef6a727..55dcbd100f 100644 --- a/Source/cmInstallPackageInfoExportGenerator.cxx +++ b/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 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 defaultTargets, - std::vector 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( - 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() = diff --git a/Source/cmInstallPackageInfoExportGenerator.h b/Source/cmInstallPackageInfoExportGenerator.h index a5413d91f8..0209f82ee6 100644 --- a/Source/cmInstallPackageInfoExportGenerator.h +++ b/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 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 defaultTargets, - std::vector defaultConfigurations, - std::string cxxModulesDirectory, cmListFileBacktrace backtrace); + MessageLevel message, bool excludeFromAll, + cmPackageInfoArguments arguments, std::string cxxModulesDirectory, + cmListFileBacktrace backtrace); cmInstallPackageInfoExportGenerator( cmInstallPackageInfoExportGenerator const&) = delete; ~cmInstallPackageInfoExportGenerator() override; diff --git a/Source/cmPackageInfoArguments.cxx b/Source/cmPackageInfoArguments.cxx new file mode 100644 index 0000000000..5395c0e26f --- /dev/null +++ b/Source/cmPackageInfoArguments.cxx @@ -0,0 +1,104 @@ +/* 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 "cmExecutionStatus.h" +#include "cmGeneratorExpression.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +template void cmPackageInfoArguments::Bind(cmArgumentParser&, + 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; + } + } + + // 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; + } + } + + // 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; +} + +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); +} diff --git a/Source/cmPackageInfoArguments.h b/Source/cmPackageInfoArguments.h new file mode 100644 index 0000000000..0d1ee9c0aa --- /dev/null +++ b/Source/cmPackageInfoArguments.h @@ -0,0 +1,98 @@ +/* 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 +#include + +#include +#include + +#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 ::value>> + static void Bind(cmArgumentParser& parser) + { + cmPackageInfoArguments::Bind(parser, nullptr); + } + + void Bind(cmArgumentParser& 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; + + ArgumentParser::NonEmpty PackageName; + ArgumentParser::NonEmpty Appendix; + ArgumentParser::NonEmpty Version; + ArgumentParser::NonEmpty VersionCompat; + ArgumentParser::NonEmpty VersionSchema; + ArgumentParser::NonEmpty> DefaultTargets; + ArgumentParser::NonEmpty> DefaultConfigs; + bool LowerCase = false; + +private: + template + static void Bind(cmArgumentParser& 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); + } + + template ::value>> + static void Bind(cmPackageInfoArguments*, cmArgumentParser& parser, + cm::static_string_view name, + U cmPackageInfoArguments::*member) + { + parser.Bind(name, member); + } + + template + static void Bind(cmPackageInfoArguments* self, + cmArgumentParser& parser, cm::static_string_view name, + U cmPackageInfoArguments::*member) + { + parser.Bind(name, (self)->*member); + } +}; + +extern template void cmPackageInfoArguments::Bind( + cmArgumentParser&, cmPackageInfoArguments*); From d4ab2797424b1c9de3a765c1b5fd2e7fcf5a2b28 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Mon, 10 Mar 2025 15:27:05 -0400 Subject: [PATCH 2/4] cmState: Track known projects Add a member to directory state that records the set of known projects. Add a method to query whether a project exists. Internally, while this doesn't allow us to confirm that a variable like `foo_VERSION` is actually associated with a project `foo`, it provides a much stronger indicator than the mere existence of said variable. --- Source/cmStatePrivate.h | 3 +++ Source/cmStateSnapshot.cxx | 8 ++++++++ Source/cmStateSnapshot.h | 1 + 3 files changed, 12 insertions(+) diff --git a/Source/cmStatePrivate.h b/Source/cmStatePrivate.h index c693d34168..afcf1fca95 100644 --- a/Source/cmStatePrivate.h +++ b/Source/cmStatePrivate.h @@ -5,6 +5,7 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include #include #include #include @@ -82,6 +83,8 @@ struct cmStateDetail::BuildsystemDirectoryStateType std::vector NormalTargetNames; std::vector ImportedTargetNames; + std::set Projects; + std::string ProjectName; cmPropertyMap Properties; diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx index 3e3fbbbe04..3b35c4adb8 100644 --- a/Source/cmStateSnapshot.cxx +++ b/Source/cmStateSnapshot.cxx @@ -5,10 +5,12 @@ #include #include +#include #include #include #include +#include #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) { diff --git a/Source/cmStateSnapshot.h b/Source/cmStateSnapshot.h index edc7e29399..07071b1239 100644 --- a/Source/cmStateSnapshot.h +++ b/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); From 9adf93d1125f27fb5423ce0ac9aad97d320d4168 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Thu, 29 May 2025 13:43:15 -0400 Subject: [PATCH 3/4] Tests/InstallPackageInfo: Consolidate bad arguments tests Consolidate InstallPackageInfo's bad arguments tests into only two test cases that each test multiple invocations, rather than having a single test per invocation. This makes them match the similar ExportPackageInfo tests which were necessarily consolidated due to those testing many more variants (due to differences in the respective interfaces). This will simplify adding additional tests in the future. --- .../InstallPackageInfo/BadArgs1-stderr.txt | 8 ++++++++ Tests/RunCMake/InstallPackageInfo/BadArgs1.cmake | 1 + .../InstallPackageInfo/BadArgs2-stderr.txt | 16 +++++++++++++++- Tests/RunCMake/InstallPackageInfo/BadArgs2.cmake | 4 +++- .../InstallPackageInfo/BadArgs3-result.txt | 1 - .../InstallPackageInfo/BadArgs3-stderr.txt | 2 -- Tests/RunCMake/InstallPackageInfo/BadArgs3.cmake | 3 --- .../InstallPackageInfo/BadArgs4-result.txt | 1 - .../InstallPackageInfo/BadArgs4-stderr.txt | 2 -- Tests/RunCMake/InstallPackageInfo/BadArgs4.cmake | 3 --- .../InstallPackageInfo/BadArgs5-result.txt | 1 - .../InstallPackageInfo/BadArgs5-stderr.txt | 2 -- Tests/RunCMake/InstallPackageInfo/BadArgs5.cmake | 3 --- .../InstallPackageInfo/RunCMakeTest.cmake | 3 --- 14 files changed, 27 insertions(+), 23 deletions(-) delete mode 100644 Tests/RunCMake/InstallPackageInfo/BadArgs3-result.txt delete mode 100644 Tests/RunCMake/InstallPackageInfo/BadArgs3-stderr.txt delete mode 100644 Tests/RunCMake/InstallPackageInfo/BadArgs3.cmake delete mode 100644 Tests/RunCMake/InstallPackageInfo/BadArgs4-result.txt delete mode 100644 Tests/RunCMake/InstallPackageInfo/BadArgs4-stderr.txt delete mode 100644 Tests/RunCMake/InstallPackageInfo/BadArgs4.cmake delete mode 100644 Tests/RunCMake/InstallPackageInfo/BadArgs5-result.txt delete mode 100644 Tests/RunCMake/InstallPackageInfo/BadArgs5-stderr.txt delete mode 100644 Tests/RunCMake/InstallPackageInfo/BadArgs5.cmake diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs1-stderr.txt b/Tests/RunCMake/InstallPackageInfo/BadArgs1-stderr.txt index 92ba6fbf0e..dd8cca0946 100644 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs1-stderr.txt +++ b/Tests/RunCMake/InstallPackageInfo/BadArgs1-stderr.txt @@ -1,2 +1,10 @@ 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\) diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs1.cmake b/Tests/RunCMake/InstallPackageInfo/BadArgs1.cmake index b99997c67c..7e08a5de31 100644 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs1.cmake +++ b/Tests/RunCMake/InstallPackageInfo/BadArgs1.cmake @@ -1,3 +1,4 @@ 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) diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs2-stderr.txt b/Tests/RunCMake/InstallPackageInfo/BadArgs2-stderr.txt index 636335ccd3..17b593dde5 100644 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs2-stderr.txt +++ b/Tests/RunCMake/InstallPackageInfo/BadArgs2-stderr.txt @@ -1,2 +1,16 @@ 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\) diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs2.cmake b/Tests/RunCMake/InstallPackageInfo/BadArgs2.cmake index 265d93c92b..3c3e14b8cb 100644 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs2.cmake +++ b/Tests/RunCMake/InstallPackageInfo/BadArgs2.cmake @@ -1,3 +1,5 @@ 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) diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs3-result.txt b/Tests/RunCMake/InstallPackageInfo/BadArgs3-result.txt deleted file mode 100644 index d00491fd7e..0000000000 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs3-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs3-stderr.txt b/Tests/RunCMake/InstallPackageInfo/BadArgs3-stderr.txt deleted file mode 100644 index 11e1a8c738..0000000000 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs3-stderr.txt +++ /dev/null @@ -1,2 +0,0 @@ -CMake Error at BadArgs3.cmake:3 \(install\): - install APPENDIX and VERSION are mutually exclusive. diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs3.cmake b/Tests/RunCMake/InstallPackageInfo/BadArgs3.cmake deleted file mode 100644 index 5f57f6a384..0000000000 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs3.cmake +++ /dev/null @@ -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) diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs4-result.txt b/Tests/RunCMake/InstallPackageInfo/BadArgs4-result.txt deleted file mode 100644 index d00491fd7e..0000000000 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs4-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs4-stderr.txt b/Tests/RunCMake/InstallPackageInfo/BadArgs4-stderr.txt deleted file mode 100644 index 067a07bacb..0000000000 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs4-stderr.txt +++ /dev/null @@ -1,2 +0,0 @@ -CMake Error at BadArgs4.cmake:3 \(install\): - install APPENDIX and DEFAULT_TARGETS are mutually exclusive. diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs4.cmake b/Tests/RunCMake/InstallPackageInfo/BadArgs4.cmake deleted file mode 100644 index 426d10b9bb..0000000000 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs4.cmake +++ /dev/null @@ -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) diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs5-result.txt b/Tests/RunCMake/InstallPackageInfo/BadArgs5-result.txt deleted file mode 100644 index d00491fd7e..0000000000 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs5-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs5-stderr.txt b/Tests/RunCMake/InstallPackageInfo/BadArgs5-stderr.txt deleted file mode 100644 index 4f7d28524f..0000000000 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs5-stderr.txt +++ /dev/null @@ -1,2 +0,0 @@ -CMake Error at BadArgs5.cmake:3 \(install\): - install APPENDIX and DEFAULT_CONFIGURATIONS are mutually exclusive. diff --git a/Tests/RunCMake/InstallPackageInfo/BadArgs5.cmake b/Tests/RunCMake/InstallPackageInfo/BadArgs5.cmake deleted file mode 100644 index 356c856b8c..0000000000 --- a/Tests/RunCMake/InstallPackageInfo/BadArgs5.cmake +++ /dev/null @@ -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) diff --git a/Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake b/Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake index cf04fa7deb..6253963369 100644 --- a/Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake +++ b/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) From ecf81c1bc12dbc1e8dda0fe4139422b553013700 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Thu, 29 May 2025 15:41:32 -0400 Subject: [PATCH 4/4] CPS: Get metadata from project Modify {export,install}(PACKAGE_INFO) commands to inherit version information from the current project in situations where it seems reasonable to do so. Add an option to explicitly request inheritance from a specific project. This leverages the recently added project(COMPAT_VERSION). --- Help/command/export.rst | 1 + Help/command/install.rst | 11 ++- Source/cmExportCommand.cxx | 5 +- Source/cmInstallCommand.cxx | 2 +- Source/cmPackageInfoArguments.cxx | 84 +++++++++++++++++++ Source/cmPackageInfoArguments.h | 13 +++ .../ExportPackageInfo/BadArgs1-stderr.txt | 12 +++ .../RunCMake/ExportPackageInfo/BadArgs1.cmake | 2 + .../ExportPackageInfo/BadArgs2-stderr.txt | 6 ++ .../RunCMake/ExportPackageInfo/BadArgs2.cmake | 1 + .../ExportPackageInfo/BadArgs4-stderr.txt | 12 +++ .../RunCMake/ExportPackageInfo/BadArgs4.cmake | 2 + .../NoProjectMetadata-check.cmake | 8 ++ .../ExportPackageInfo/NoProjectMetadata.cmake | 10 +++ .../ProjectMetadata-check.cmake | 18 ++++ .../ExportPackageInfo/ProjectMetadata.cmake | 25 ++++++ .../ExportPackageInfo/RunCMakeTest.cmake | 2 + .../InstallPackageInfo/BadArgs1-stderr.txt | 12 +++ .../InstallPackageInfo/BadArgs1.cmake | 2 + .../InstallPackageInfo/BadArgs2-stderr.txt | 6 ++ .../InstallPackageInfo/BadArgs2.cmake | 1 + .../NoProjectMetadata-check.cmake | 8 ++ .../NoProjectMetadata.cmake | 11 +++ .../ProjectMetadata-check.cmake | 18 ++++ .../InstallPackageInfo/ProjectMetadata.cmake | 28 +++++++ .../InstallPackageInfo/RunCMakeTest.cmake | 2 + 26 files changed, 298 insertions(+), 4 deletions(-) create mode 100644 Tests/RunCMake/ExportPackageInfo/NoProjectMetadata-check.cmake create mode 100644 Tests/RunCMake/ExportPackageInfo/NoProjectMetadata.cmake create mode 100644 Tests/RunCMake/ExportPackageInfo/ProjectMetadata-check.cmake create mode 100644 Tests/RunCMake/ExportPackageInfo/ProjectMetadata.cmake create mode 100644 Tests/RunCMake/InstallPackageInfo/NoProjectMetadata-check.cmake create mode 100644 Tests/RunCMake/InstallPackageInfo/NoProjectMetadata.cmake create mode 100644 Tests/RunCMake/InstallPackageInfo/ProjectMetadata-check.cmake create mode 100644 Tests/RunCMake/InstallPackageInfo/ProjectMetadata.cmake diff --git a/Help/command/export.rst b/Help/command/export.rst index 1427d89ec1..0011167039 100644 --- a/Help/command/export.rst +++ b/Help/command/export.rst @@ -133,6 +133,7 @@ Exporting Targets to the |CPS| .. code-block:: cmake export(EXPORT PACKAGE_INFO + [PROJECT |NO_PROJECT_METADATA] [APPENDIX ] [LOWER_CASE_FILE] [VERSION diff --git a/Help/command/install.rst b/Help/command/install.rst index 5040e4497c..21fcf4ea05 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -992,6 +992,7 @@ Signatures .. code-block:: cmake install(PACKAGE_INFO EXPORT + [PROJECT |NO_PROJECT_METADATA] [APPENDIX ] [DESTINATION ] [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 ```` matches the current CMake + :variable:`PROJECT_NAME`, package metadata will be inherited from the + project. The ``PROJECT `` 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 diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 25f09a753a..42590f3917 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -114,7 +114,7 @@ bool cmExportCommand(std::vector const& args, } std::vector unknownArgs; - Arguments const arguments = parser.Parse(args, &unknownArgs); + Arguments arguments = parser.Parse(args, &unknownArgs); if (!unknownArgs.empty()) { status.SetError("Unknown argument: \"" + unknownArgs.front() + "\"."); @@ -219,7 +219,8 @@ bool cmExportCommand(std::vector const& args, status.SetError("PACKAGE_INFO and NAMESPACE are mutually exclusive."); return false; } - if (!arguments.Check(status)) { + if (!arguments.Check(status) || + !arguments.SetMetadataFromProject(status)) { return false; } } diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 3573207588..8e3293e940 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -2202,7 +2202,7 @@ bool HandlePackageInfoMode(std::vector const& args, return false; } - if (!arguments.Check(status)) { + if (!arguments.Check(status) || !arguments.SetMetadataFromProject(status)) { return false; } diff --git a/Source/cmPackageInfoArguments.cxx b/Source/cmPackageInfoArguments.cxx index 5395c0e26f..f9eac81f27 100644 --- a/Source/cmPackageInfoArguments.cxx +++ b/Source/cmPackageInfoArguments.cxx @@ -2,10 +2,15 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmPackageInfoArguments.h" +#include + #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(cmArgumentParser&, cmPackageInfoArguments*); @@ -35,6 +40,14 @@ bool cmPackageInfoArguments::Check(cmExecutionStatus& status, 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. @@ -53,6 +66,17 @@ bool cmPackageInfoArguments::Check(cmExecutionStatus& status, "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. @@ -81,6 +105,66 @@ bool cmPackageInfoArguments::Check(cmExecutionStatus& status, 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); diff --git a/Source/cmPackageInfoArguments.h b/Source/cmPackageInfoArguments.h index 0d1ee9c0aa..33a94ab129 100644 --- a/Source/cmPackageInfoArguments.h +++ b/Source/cmPackageInfoArguments.h @@ -47,6 +47,10 @@ public: /// \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 PackageName; ArgumentParser::NonEmpty Appendix; ArgumentParser::NonEmpty Version; @@ -56,7 +60,12 @@ public: ArgumentParser::NonEmpty> DefaultConfigs; bool LowerCase = false; + ArgumentParser::NonEmpty ProjectName; + bool NoProjectDefaults = false; + private: + bool SetEffectiveProject(cmExecutionStatus& status); + template static void Bind(cmArgumentParser& parser, cmPackageInfoArguments* self) { @@ -73,6 +82,10 @@ private: &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