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 <kwrobot@kitware.com>
Merge-request: !10837
This commit is contained in:
Brad King 2025-06-03 11:57:03 +00:00 committed by Kitware Robot
commit 7dbffb3aa1
47 changed files with 586 additions and 224 deletions

View File

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

View File

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

View File

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

View File

@ -11,17 +11,13 @@
#include <cm3p/json/value.h>
#include "cmGeneratorExpression.h"
#include "cmPackageInfoArguments.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
cmExportBuildPackageInfoGenerator::cmExportBuildPackageInfoGenerator(
std::string packageName, std::string version, std::string versionCompat,
std::string versionSchema, std::vector<std::string> defaultTargets,
std::vector<std::string> defaultConfigurations)
: cmExportPackageInfoGenerator(
std::move(packageName), std::move(version), std::move(versionCompat),
std::move(versionSchema), std::move(defaultTargets),
std::move(defaultConfigurations))
cmPackageInfoArguments arguments)
: cmExportPackageInfoGenerator(std::move(arguments))
{
this->SetNamespace(cmStrCat(this->GetPackageName(), "::"_s));
}

View File

@ -6,7 +6,6 @@
#include <iosfwd>
#include <string>
#include <vector>
#include "cmExportBuildFileGenerator.h"
#include "cmExportPackageInfoGenerator.h"
@ -16,6 +15,7 @@ class Value;
}
class cmGeneratorTarget;
class cmPackageInfoArguments;
/** \class cmExportBuildPackageInfoGenerator
* \brief Generate a file exporting targets from a build tree.
@ -31,10 +31,7 @@ class cmExportBuildPackageInfoGenerator
, public cmExportPackageInfoGenerator
{
public:
cmExportBuildPackageInfoGenerator(
std::string packageName, std::string version, std::string versionCompat,
std::string versionSchema, std::vector<std::string> defaultTargets,
std::vector<std::string> defaultConfigurations);
cmExportBuildPackageInfoGenerator(cmPackageInfoArguments arguments);
protected:
// Implement virtual methods from the superclass.

View File

@ -24,10 +24,10 @@
#include "cmExportBuildPackageInfoGenerator.h"
#include "cmExportSet.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGlobalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPackageInfoArguments.h"
#include "cmPolicies.h"
#include "cmRange.h"
#include "cmStateTypes.h"
@ -63,24 +63,16 @@ bool cmExportCommand(std::vector<std::string> const& args,
return HandlePackage(args, status);
}
struct Arguments
struct Arguments : cmPackageInfoArguments
{
cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Targets;
ArgumentParser::NonEmpty<std::string> ExportSetName;
ArgumentParser::NonEmpty<std::string> Namespace;
ArgumentParser::NonEmpty<std::string> Filename;
ArgumentParser::NonEmpty<std::string> AndroidMKFile;
ArgumentParser::NonEmpty<std::string> PackageName;
ArgumentParser::NonEmpty<std::string> Appendix;
ArgumentParser::NonEmpty<std::string> Version;
ArgumentParser::NonEmpty<std::string> VersionCompat;
ArgumentParser::NonEmpty<std::string> VersionSchema;
ArgumentParser::NonEmpty<std::string> CxxModulesDirectory;
ArgumentParser::NonEmpty<std::vector<std::string>> DefaultTargets;
ArgumentParser::NonEmpty<std::vector<std::string>> DefaultConfigs;
bool Append = false;
bool ExportOld = false;
bool LowerCase = false;
std::vector<std::vector<std::string>> PackageDependencyArgs;
bool ExportPackageDependencies = false;
@ -104,14 +96,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
}
if (cmExperimental::HasSupportEnabled(
status.GetMakefile(), cmExperimental::Feature::ExportPackageInfo)) {
parser.Bind("PACKAGE_INFO"_s, &Arguments::PackageName);
parser.Bind("LOWER_CASE_FILE"_s, &Arguments::LowerCase);
parser.Bind("APPENDIX"_s, &Arguments::Appendix);
parser.Bind("VERSION"_s, &Arguments::Version);
parser.Bind("COMPAT_VERSION"_s, &Arguments::VersionCompat);
parser.Bind("VERSION_SCHEMA"_s, &Arguments::VersionSchema);
parser.Bind("DEFAULT_TARGETS"_s, &Arguments::DefaultTargets);
parser.Bind("DEFAULT_CONFIGURATIONS"_s, &Arguments::DefaultConfigs);
cmPackageInfoArguments::Bind(parser);
}
} else if (args[0] == "SETUP") {
parser.Bind("SETUP"_s, &Arguments::ExportSetName);
@ -129,7 +114,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
}
std::vector<std::string> unknownArgs;
Arguments const arguments = parser.Parse(args, &unknownArgs);
Arguments arguments = parser.Parse(args, &unknownArgs);
if (!unknownArgs.empty()) {
status.SetError("Unknown argument: \"" + unknownArgs.front() + "\".");
@ -222,24 +207,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
}
if (arguments.PackageName.empty()) {
if (arguments.LowerCase) {
status.SetError("LOWER_CASE_FILE requires PACKAGE_INFO.");
return false;
}
if (!arguments.Appendix.empty()) {
status.SetError("APPENDIX requires PACKAGE_INFO.");
return false;
}
if (!arguments.Version.empty()) {
status.SetError("VERSION requires PACKAGE_INFO.");
return false;
}
if (!arguments.DefaultTargets.empty()) {
status.SetError("DEFAULT_TARGETS requires PACKAGE_INFO.");
return false;
}
if (!arguments.DefaultConfigs.empty()) {
status.SetError("DEFAULT_CONFIGURATIONS requires PACKAGE_INFO.");
if (!arguments.Check(status, false)) {
return false;
}
} else {
@ -251,30 +219,8 @@ bool cmExportCommand(std::vector<std::string> const& args,
status.SetError("PACKAGE_INFO and NAMESPACE are mutually exclusive.");
return false;
}
if (!arguments.Appendix.empty()) {
if (!arguments.Version.empty()) {
status.SetError("APPENDIX and VERSION are mutually exclusive.");
return false;
}
if (!arguments.DefaultTargets.empty()) {
status.SetError("APPENDIX and DEFAULT_TARGETS "
"are mutually exclusive.");
return false;
}
if (!arguments.DefaultConfigs.empty()) {
status.SetError("APPENDIX and DEFAULT_CONFIGURATIONS "
"are mutually exclusive.");
return false;
}
}
}
if (arguments.Version.empty()) {
if (!arguments.VersionCompat.empty()) {
status.SetError("COMPAT_VERSION requires VERSION.");
return false;
}
if (!arguments.VersionSchema.empty()) {
status.SetError("VERSION_SCHEMA requires VERSION.");
if (!arguments.Check(status) ||
!arguments.SetMetadataFromProject(status)) {
return false;
}
}
@ -293,23 +239,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
if (arguments.PackageName.empty()) {
fname = arguments.ExportSetName + ".cmake";
} else {
// Validate the package name.
if (!cmGeneratorExpression::IsValidTargetName(arguments.PackageName) ||
arguments.PackageName.find(':') != std::string::npos) {
status.SetError(
cmStrCat(R"(PACKAGE_INFO given invalid package name ")"_s,
arguments.PackageName, R"(".)"_s));
return false;
}
std::string const pkgNameOnDisk =
(arguments.LowerCase ? cmSystemTools::LowerCase(arguments.PackageName)
: std::string{ arguments.PackageName });
if (arguments.Appendix.empty()) {
fname = cmStrCat(pkgNameOnDisk, ".cps"_s);
} else {
fname = cmStrCat(pkgNameOnDisk, '-', arguments.Appendix, ".cps"_s);
}
fname = arguments.GetPackageFileName();
cps = true;
}
} else {
@ -430,10 +360,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
ebag->SetAppendMode(arguments.Append);
ebfg = std::move(ebag);
} else if (cps) {
auto ebpg = cm::make_unique<cmExportBuildPackageInfoGenerator>(
arguments.PackageName, arguments.Version, arguments.VersionCompat,
arguments.VersionSchema, arguments.DefaultTargets,
arguments.DefaultConfigs);
auto ebpg = cm::make_unique<cmExportBuildPackageInfoGenerator>(arguments);
ebfg = std::move(ebpg);
} else {
auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>();

View File

@ -15,6 +15,7 @@
#include "cmInstallExportGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmPackageInfoArguments.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
@ -22,14 +23,8 @@
#include "cmTargetExport.h"
cmExportInstallPackageInfoGenerator::cmExportInstallPackageInfoGenerator(
cmInstallExportGenerator* iegen, std::string packageName,
std::string version, std::string versionCompat, std::string versionSchema,
std::vector<std::string> defaultTargets,
std::vector<std::string> defaultConfigurations)
: cmExportPackageInfoGenerator(
std::move(packageName), std::move(version), std::move(versionCompat),
std::move(versionSchema), std::move(defaultTargets),
std::move(defaultConfigurations))
cmInstallExportGenerator* iegen, cmPackageInfoArguments arguments)
: cmExportPackageInfoGenerator(std::move(arguments))
, cmExportInstallFileGenerator(iegen)
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}

View File

@ -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*);

View File

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

View File

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

View File

@ -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);

View File

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

View File

@ -2,3 +2,5 @@ add_library(foo INTERFACE)
install(TARGETS foo EXPORT foo DESTINATION .)
export(EXPORT foo PACKAGE_INFO foo COMPAT_VERSION 1.0)
export(EXPORT foo PACKAGE_INFO foo VERSION_SCHEMA simple)
export(EXPORT foo PACKAGE_INFO foo PROJECT foo NO_PROJECT_METADATA)
export(EXPORT foo PACKAGE_INFO foo PROJECT bar)

View File

@ -14,3 +14,9 @@ CMake Error at BadArgs2\.cmake:5 \(export\):
export APPENDIX and DEFAULT_CONFIGURATIONS are mutually exclusive\.
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)
CMake Error at BadArgs2\.cmake:6 \(export\):
export APPENDIX and PROJECT are mutually exclusive\.
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)

View File

@ -3,3 +3,4 @@ install(TARGETS foo EXPORT foo DESTINATION .)
export(EXPORT foo PACKAGE_INFO foo APPENDIX test VERSION 1.0)
export(EXPORT foo PACKAGE_INFO foo APPENDIX test DEFAULT_TARGETS foo)
export(EXPORT foo PACKAGE_INFO foo APPENDIX test DEFAULT_CONFIGURATIONS Release)
export(EXPORT foo PACKAGE_INFO foo APPENDIX test PROJECT foo)

View File

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

View File

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

View File

@ -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")

View File

@ -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
)

View File

@ -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")

View File

@ -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
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)

View File

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

View File

@ -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)

View File

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

View File

@ -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)

View File

@ -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")

View File

@ -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
)

View File

@ -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")

View File

@ -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
)

View File

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