From 7155903e53f7f8f452b040d472bc66a6272002cf Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Tue, 3 Jun 2025 14:16:29 -0400 Subject: [PATCH 1/3] cmExportPackageInfoGenerator: Fix style Use PascalCase for cmExportPackageInfoGenerator's local helper function. --- Source/cmExportPackageInfoGenerator.cxx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/cmExportPackageInfoGenerator.cxx b/Source/cmExportPackageInfoGenerator.cxx index 71df7456e2..bd3db81ace 100644 --- a/Source/cmExportPackageInfoGenerator.cxx +++ b/Source/cmExportPackageInfoGenerator.cxx @@ -64,7 +64,7 @@ void cmExportPackageInfoGenerator::WritePackageInfo( namespace { template -void buildArray(Json::Value& object, std::string const& property, +void BuildArray(Json::Value& object, std::string const& property, T const& values) { if (!values.empty()) { @@ -115,8 +115,8 @@ Json::Value cmExportPackageInfoGenerator::GeneratePackageInfo() const } } - buildArray(package, "default_components", this->DefaultTargets); - buildArray(package, "configurations", this->DefaultConfigurations); + BuildArray(package, "default_components", this->DefaultTargets); + BuildArray(package, "configurations", this->DefaultConfigurations); // TODO: description, website, license @@ -382,9 +382,9 @@ void cmExportPackageInfoGenerator::GenerateInterfaceLinkProperties( addLibraries(allowList["LINK_ONLY"], linkRequires); addLibraries(cmList{ interfaceLinkLibraries }, buildRequires); - buildArray(component, "requires", buildRequires); - buildArray(component, "link_requires", linkRequires); - buildArray(component, "link_libraries", linkLibraries); + BuildArray(component, "requires", buildRequires); + BuildArray(component, "link_requires", linkRequires); + BuildArray(component, "link_libraries", linkLibraries); } void cmExportPackageInfoGenerator::GenerateInterfaceCompileFeatures( @@ -412,7 +412,7 @@ void cmExportPackageInfoGenerator::GenerateInterfaceCompileFeatures( } } - buildArray(component, "compile_features", features); + BuildArray(component, "compile_features", features); } void cmExportPackageInfoGenerator::GenerateInterfaceCompileDefines( @@ -512,7 +512,7 @@ Json::Value cmExportPackageInfoGenerator::GenerateInterfaceConfigProperties( languages.emplace_back(std::move(ll)); } } - buildArray(component, "link_languages", languages); + BuildArray(component, "link_languages", languages); } } From f224e131a5b3fbda2e9ec27edf7ef88db8dceb22 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Tue, 3 Jun 2025 14:31:44 -0400 Subject: [PATCH 2/3] CPS: Refactor metadata handling Add some helper functions to simplify some repetitive code dealing with CPS metadata. The duplication isn't bad now, but in the future we will be handling additional properties that will benefit from these helpers. --- Source/cmExportPackageInfoGenerator.cxx | 21 +++++++++++++-------- Source/cmPackageInfoArguments.cxx | 23 ++++++++++++++--------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Source/cmExportPackageInfoGenerator.cxx b/Source/cmExportPackageInfoGenerator.cxx index bd3db81ace..1376a45f0b 100644 --- a/Source/cmExportPackageInfoGenerator.cxx +++ b/Source/cmExportPackageInfoGenerator.cxx @@ -63,6 +63,16 @@ void cmExportPackageInfoGenerator::WritePackageInfo( } namespace { +bool SetProperty(Json::Value& object, std::string const& property, + std::string const& value) +{ + if (!value.empty()) { + object[property] = value; + return true; + } + return false; +} + template void BuildArray(Json::Value& object, std::string const& property, T const& values) @@ -105,14 +115,9 @@ Json::Value cmExportPackageInfoGenerator::GeneratePackageInfo() const package["name"] = this->GetPackageName(); package["cps_version"] = std::string(kCPS_VERSION_STR); - if (!this->PackageVersion.empty()) { - package["version"] = this->PackageVersion; - if (!this->PackageVersionCompat.empty()) { - package["compat_version"] = this->PackageVersionCompat; - } - if (!this->PackageVersionSchema.empty()) { - package["version_schema"] = this->PackageVersionSchema; - } + if (SetProperty(package, "version", this->PackageVersion)) { + SetProperty(package, "compat_version", this->PackageVersionCompat); + SetProperty(package, "version_schema", this->PackageVersionSchema); } BuildArray(package, "default_components", this->DefaultTargets); diff --git a/Source/cmPackageInfoArguments.cxx b/Source/cmPackageInfoArguments.cxx index f9eac81f27..c53d57eb76 100644 --- a/Source/cmPackageInfoArguments.cxx +++ b/Source/cmPackageInfoArguments.cxx @@ -4,6 +4,8 @@ #include +#include + #include "cmExecutionStatus.h" #include "cmGeneratorExpression.h" #include "cmMakefile.h" @@ -118,16 +120,19 @@ bool cmPackageInfoArguments::SetMetadataFromProject(cmExecutionStatus& status) } cmMakefile& mf = status.GetMakefile(); + auto mapProjectValue = [&](std::string& arg, cm::string_view suffix) { + cmValue const& projectValue = + mf.GetDefinition(cmStrCat(this->ProjectName, '_', suffix)); + if (projectValue) { + arg = *projectValue; + return true; + } + return false; + }; + 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; - } + if (mapProjectValue(this->Version, "VERSION"_s)) { + mapProjectValue(this->VersionCompat, "COMPAT_VERSION"_s); } } From da97747dac2b4f78d3a3c5bcac0eb9b8cd6be1eb Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Tue, 3 Jun 2025 16:24:13 -0400 Subject: [PATCH 3/3] CPS: Support additional metadata Add support for specifying CPS's supplemental `description` and `website` attributes. Add ability to inherit these from the `project()`, similar to how version information can be inherited. --- Help/command/export.rst | 4 +++- Help/command/install.rst | 13 +++++++++++++ Source/cmExportPackageInfoGenerator.cxx | 6 +++++- Source/cmExportPackageInfoGenerator.h | 2 ++ Source/cmPackageInfoArguments.cxx | 8 ++++++++ Source/cmPackageInfoArguments.h | 4 ++++ .../RunCMake/ExportPackageInfo/Metadata-check.cmake | 3 +++ Tests/RunCMake/ExportPackageInfo/Metadata.cmake | 2 ++ .../ExportPackageInfo/NoProjectMetadata-check.cmake | 2 ++ .../ExportPackageInfo/NoProjectMetadata.cmake | 7 ++++++- .../ExportPackageInfo/ProjectMetadata-check.cmake | 6 ++++++ .../ExportPackageInfo/ProjectMetadata.cmake | 9 ++++++++- .../InstallPackageInfo/Metadata-check.cmake | 3 +++ Tests/RunCMake/InstallPackageInfo/Metadata.cmake | 2 ++ .../NoProjectMetadata-check.cmake | 2 ++ .../InstallPackageInfo/NoProjectMetadata.cmake | 7 ++++++- .../InstallPackageInfo/ProjectMetadata-check.cmake | 6 ++++++ .../InstallPackageInfo/ProjectMetadata.cmake | 9 ++++++++- 18 files changed, 89 insertions(+), 6 deletions(-) diff --git a/Help/command/export.rst b/Help/command/export.rst index 0011167039..6668e97250 100644 --- a/Help/command/export.rst +++ b/Help/command/export.rst @@ -140,7 +140,9 @@ Exporting Targets to the |CPS| [COMPAT_VERSION ] [VERSION_SCHEMA ]] [DEFAULT_TARGETS ...] - [DEFAULT_CONFIGURATIONS ...]) + [DEFAULT_CONFIGURATIONS ...] + [DESCRIPTION ] + [HOMEPAGE_URL ]) .. versionadded:: 4.1 .. note:: diff --git a/Help/command/install.rst b/Help/command/install.rst index 21fcf4ea05..001bee0a02 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -1001,6 +1001,8 @@ Signatures [VERSION_SCHEMA ]] [DEFAULT_TARGETS ...] [DEFAULT_CONFIGURATIONS ...] + [DESCRIPTION ] + [HOMEPAGE_URL ] [PERMISSIONS ...] [CONFIGURATIONS ...] [COMPONENT ] @@ -1057,6 +1059,17 @@ Signatures configurations exists. If not specified, CMake will fall back to the package's available configurations in an unspecified order. + ``DESCRIPTION `` + .. versionadded:: 4.1 + + An informational description of the project. It is recommended that this + description is a relatively short string, usually no more than a few words. + + ``HOMEPAGE_URL `` + .. versionadded:: 4.1 + + An informational canonical home URL for the project. + 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 diff --git a/Source/cmExportPackageInfoGenerator.cxx b/Source/cmExportPackageInfoGenerator.cxx index 1376a45f0b..f2fbc280bf 100644 --- a/Source/cmExportPackageInfoGenerator.cxx +++ b/Source/cmExportPackageInfoGenerator.cxx @@ -37,6 +37,8 @@ cmExportPackageInfoGenerator::cmExportPackageInfoGenerator( , PackageVersion(std::move(arguments.Version)) , PackageVersionCompat(std::move(arguments.VersionCompat)) , PackageVersionSchema(std::move(arguments.VersionSchema)) + , PackageDescription(std::move(arguments.Description)) + , PackageWebsite(std::move(arguments.Website)) , DefaultTargets(std::move(arguments.DefaultTargets)) , DefaultConfigurations(std::move(arguments.DefaultConfigs)) { @@ -123,7 +125,9 @@ Json::Value cmExportPackageInfoGenerator::GeneratePackageInfo() const BuildArray(package, "default_components", this->DefaultTargets); BuildArray(package, "configurations", this->DefaultConfigurations); - // TODO: description, website, license + SetProperty(package, "description", this->PackageDescription); + SetProperty(package, "website", this->PackageWebsite); + // TODO: license return package; } diff --git a/Source/cmExportPackageInfoGenerator.h b/Source/cmExportPackageInfoGenerator.h index 1f849c9b67..fb7d79f7ad 100644 --- a/Source/cmExportPackageInfoGenerator.h +++ b/Source/cmExportPackageInfoGenerator.h @@ -108,6 +108,8 @@ private: std::string const PackageVersion; std::string const PackageVersionCompat; std::string const PackageVersionSchema; + std::string const PackageDescription; + std::string const PackageWebsite; std::vector DefaultTargets; std::vector DefaultConfigurations; diff --git a/Source/cmPackageInfoArguments.cxx b/Source/cmPackageInfoArguments.cxx index c53d57eb76..5324380e17 100644 --- a/Source/cmPackageInfoArguments.cxx +++ b/Source/cmPackageInfoArguments.cxx @@ -136,6 +136,14 @@ bool cmPackageInfoArguments::SetMetadataFromProject(cmExecutionStatus& status) } } + if (this->Description.empty()) { + mapProjectValue(this->Description, "DESCRIPTION"_s); + } + + if (this->Website.empty()) { + mapProjectValue(this->Website, "HOMEPAGE_URL"_s); + } + return true; } diff --git a/Source/cmPackageInfoArguments.h b/Source/cmPackageInfoArguments.h index 33a94ab129..a00219759b 100644 --- a/Source/cmPackageInfoArguments.h +++ b/Source/cmPackageInfoArguments.h @@ -56,6 +56,8 @@ public: ArgumentParser::NonEmpty Version; ArgumentParser::NonEmpty VersionCompat; ArgumentParser::NonEmpty VersionSchema; + ArgumentParser::NonEmpty Description; + ArgumentParser::NonEmpty Website; ArgumentParser::NonEmpty> DefaultTargets; ArgumentParser::NonEmpty> DefaultConfigs; bool LowerCase = false; @@ -82,6 +84,8 @@ private: &cmPackageInfoArguments::DefaultTargets); Bind(self, parser, "DEFAULT_CONFIGURATIONS"_s, &cmPackageInfoArguments::DefaultConfigs); + Bind(self, parser, "DESCRIPTION"_s, &cmPackageInfoArguments::Description); + Bind(self, parser, "HOMEPAGE_URL"_s, &cmPackageInfoArguments::Website); Bind(self, parser, "PROJECT"_s, &cmPackageInfoArguments::ProjectName); Bind(self, parser, "NO_PROJECT_METADATA"_s, diff --git a/Tests/RunCMake/ExportPackageInfo/Metadata-check.cmake b/Tests/RunCMake/ExportPackageInfo/Metadata-check.cmake index 76e5e55848..f9b3cc7d5a 100644 --- a/Tests/RunCMake/ExportPackageInfo/Metadata-check.cmake +++ b/Tests/RunCMake/ExportPackageInfo/Metadata-check.cmake @@ -14,3 +14,6 @@ expect_value("${content}" "foo" "default_components" 0) expect_array("${content}" 2 "configurations") expect_value("${content}" "release" "configurations" 0) expect_value("${content}" "debug" "configurations" 1) + +expect_value("${content}" "Sample package" "description") +expect_value("${content}" "https://www.example.com/package/foo" "website") diff --git a/Tests/RunCMake/ExportPackageInfo/Metadata.cmake b/Tests/RunCMake/ExportPackageInfo/Metadata.cmake index 1de002196c..2311695019 100644 --- a/Tests/RunCMake/ExportPackageInfo/Metadata.cmake +++ b/Tests/RunCMake/ExportPackageInfo/Metadata.cmake @@ -8,4 +8,6 @@ export( COMPAT_VERSION 1.2.0 DEFAULT_TARGETS foo DEFAULT_CONFIGURATIONS release debug + DESCRIPTION "Sample package" + HOMEPAGE_URL "https://www.example.com/package/foo" ) diff --git a/Tests/RunCMake/ExportPackageInfo/NoProjectMetadata-check.cmake b/Tests/RunCMake/ExportPackageInfo/NoProjectMetadata-check.cmake index e119976bcb..721a57a9a1 100644 --- a/Tests/RunCMake/ExportPackageInfo/NoProjectMetadata-check.cmake +++ b/Tests/RunCMake/ExportPackageInfo/NoProjectMetadata-check.cmake @@ -6,3 +6,5 @@ file(READ "${out_dir}/foo.cps" content) expect_value("${content}" "foo" "name") expect_missing("${content}" "version") expect_missing("${content}" "compat_version") +expect_missing("${content}" "description") +expect_missing("${content}" "website") diff --git a/Tests/RunCMake/ExportPackageInfo/NoProjectMetadata.cmake b/Tests/RunCMake/ExportPackageInfo/NoProjectMetadata.cmake index 9148a95c29..425d92858d 100644 --- a/Tests/RunCMake/ExportPackageInfo/NoProjectMetadata.cmake +++ b/Tests/RunCMake/ExportPackageInfo/NoProjectMetadata.cmake @@ -1,4 +1,9 @@ -project(foo VERSION 1.2.3 COMPAT_VERSION 1.1.0) +project(foo + VERSION 1.2.3 + COMPAT_VERSION 1.1.0 + DESCRIPTION "Sample package" + HOMEPAGE_URL "https://www.example.com/package/foo" + ) add_library(foo INTERFACE) install(TARGETS foo EXPORT foo DESTINATION .) diff --git a/Tests/RunCMake/ExportPackageInfo/ProjectMetadata-check.cmake b/Tests/RunCMake/ExportPackageInfo/ProjectMetadata-check.cmake index 32927351d7..13300995f0 100644 --- a/Tests/RunCMake/ExportPackageInfo/ProjectMetadata-check.cmake +++ b/Tests/RunCMake/ExportPackageInfo/ProjectMetadata-check.cmake @@ -6,13 +6,19 @@ 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") +expect_value("${content}" "Sample package" "description") +expect_value("${content}" "https://www.example.com/package/foo" "website") 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") +expect_value("${content}" "Sample package" "description") +expect_value("${content}" "https://www.example.com/package/foo" "website") file(READ "${out_dir}/test2.cps" content) expect_value("${content}" "test2" "name") expect_value("${content}" "1.4.7" "version") expect_missing("${content}" "compat_version") +expect_value("${content}" "Don't inherit" "description") +expect_value("${content}" "https://www.example.com/package/bar" "website") diff --git a/Tests/RunCMake/ExportPackageInfo/ProjectMetadata.cmake b/Tests/RunCMake/ExportPackageInfo/ProjectMetadata.cmake index dac7ab0411..f53ba0ecb9 100644 --- a/Tests/RunCMake/ExportPackageInfo/ProjectMetadata.cmake +++ b/Tests/RunCMake/ExportPackageInfo/ProjectMetadata.cmake @@ -1,4 +1,9 @@ -project(foo VERSION 1.2.3 COMPAT_VERSION 1.1.0) +project(foo + VERSION 1.2.3 + COMPAT_VERSION 1.1.0 + DESCRIPTION "Sample package" + HOMEPAGE_URL "https://www.example.com/package/foo" + ) add_library(foo INTERFACE) install(TARGETS foo EXPORT foo DESTINATION .) @@ -22,4 +27,6 @@ export( PROJECT foo PACKAGE_INFO test2 VERSION 1.4.7 + DESCRIPTION "Don't inherit" + HOMEPAGE_URL "https://www.example.com/package/bar" ) diff --git a/Tests/RunCMake/InstallPackageInfo/Metadata-check.cmake b/Tests/RunCMake/InstallPackageInfo/Metadata-check.cmake index 8db8c298a7..7eca3b0594 100644 --- a/Tests/RunCMake/InstallPackageInfo/Metadata-check.cmake +++ b/Tests/RunCMake/InstallPackageInfo/Metadata-check.cmake @@ -14,3 +14,6 @@ expect_value("${content}" "foo" "default_components" 0) expect_array("${content}" 2 "configurations") expect_value("${content}" "release" "configurations" 0) expect_value("${content}" "debug" "configurations" 1) + +expect_value("${content}" "Sample package" "description") +expect_value("${content}" "https://www.example.com/package/foo" "website") diff --git a/Tests/RunCMake/InstallPackageInfo/Metadata.cmake b/Tests/RunCMake/InstallPackageInfo/Metadata.cmake index f8fc9b873d..d551f3b40d 100644 --- a/Tests/RunCMake/InstallPackageInfo/Metadata.cmake +++ b/Tests/RunCMake/InstallPackageInfo/Metadata.cmake @@ -9,4 +9,6 @@ install( COMPAT_VERSION 1.2.0 DEFAULT_TARGETS foo DEFAULT_CONFIGURATIONS release debug + DESCRIPTION "Sample package" + HOMEPAGE_URL "https://www.example.com/package/foo" ) diff --git a/Tests/RunCMake/InstallPackageInfo/NoProjectMetadata-check.cmake b/Tests/RunCMake/InstallPackageInfo/NoProjectMetadata-check.cmake index 6ecded2008..ee87992bdb 100644 --- a/Tests/RunCMake/InstallPackageInfo/NoProjectMetadata-check.cmake +++ b/Tests/RunCMake/InstallPackageInfo/NoProjectMetadata-check.cmake @@ -6,3 +6,5 @@ file(READ "${out_dir}/foo.cps" content) expect_value("${content}" "foo" "name") expect_missing("${content}" "version") expect_missing("${content}" "compat_version") +expect_missing("${content}" "description") +expect_missing("${content}" "website") diff --git a/Tests/RunCMake/InstallPackageInfo/NoProjectMetadata.cmake b/Tests/RunCMake/InstallPackageInfo/NoProjectMetadata.cmake index 7a5c4c34d3..603392cc69 100644 --- a/Tests/RunCMake/InstallPackageInfo/NoProjectMetadata.cmake +++ b/Tests/RunCMake/InstallPackageInfo/NoProjectMetadata.cmake @@ -1,4 +1,9 @@ -project(foo VERSION 1.2.3 COMPAT_VERSION 1.1.0) +project(foo + VERSION 1.2.3 + COMPAT_VERSION 1.1.0 + DESCRIPTION "Sample package" + HOMEPAGE_URL "https://www.example.com/package/foo" + ) add_library(foo INTERFACE) install(TARGETS foo EXPORT foo DESTINATION .) diff --git a/Tests/RunCMake/InstallPackageInfo/ProjectMetadata-check.cmake b/Tests/RunCMake/InstallPackageInfo/ProjectMetadata-check.cmake index d113c06312..5c39b3fffc 100644 --- a/Tests/RunCMake/InstallPackageInfo/ProjectMetadata-check.cmake +++ b/Tests/RunCMake/InstallPackageInfo/ProjectMetadata-check.cmake @@ -6,13 +6,19 @@ 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") +expect_value("${content}" "Sample package" "description") +expect_value("${content}" "https://www.example.com/package/foo" "website") 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") +expect_value("${content}" "Sample package" "description") +expect_value("${content}" "https://www.example.com/package/foo" "website") file(READ "${out_dir}/test2.cps" content) expect_value("${content}" "test2" "name") expect_value("${content}" "1.4.7" "version") expect_missing("${content}" "compat_version") +expect_value("${content}" "Don't inherit" "description") +expect_value("${content}" "https://www.example.com/package/bar" "website") diff --git a/Tests/RunCMake/InstallPackageInfo/ProjectMetadata.cmake b/Tests/RunCMake/InstallPackageInfo/ProjectMetadata.cmake index 7395a17229..e86372f4cb 100644 --- a/Tests/RunCMake/InstallPackageInfo/ProjectMetadata.cmake +++ b/Tests/RunCMake/InstallPackageInfo/ProjectMetadata.cmake @@ -1,4 +1,9 @@ -project(foo VERSION 1.2.3 COMPAT_VERSION 1.1.0) +project(foo + VERSION 1.2.3 + COMPAT_VERSION 1.1.0 + DESCRIPTION "Sample package" + HOMEPAGE_URL "https://www.example.com/package/foo" + ) add_library(foo INTERFACE) install(TARGETS foo EXPORT foo DESTINATION .) @@ -25,4 +30,6 @@ install( EXPORT foo PROJECT foo VERSION 1.4.7 + DESCRIPTION "Don't inherit" + HOMEPAGE_URL "https://www.example.com/package/bar" )