diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index 332a6c1d90..ee12b1d254 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -16,7 +16,6 @@ #include "cmList.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmMessageType.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmTarget.h" @@ -242,12 +241,11 @@ void cmExportBuildFileGenerator::ComplainAboutDuplicateTarget( this->ReportError(e.str()); } -void cmExportBuildFileGenerator::ReportError( - std::string const& errorMessage) const +void cmExportBuildFileGenerator::IssueMessage(MessageType type, + std::string const& message) const { this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, errorMessage, - this->LG->GetMakefile()->GetBacktrace()); + type, message, this->LG->GetMakefile()->GetBacktrace()); } std::string cmExportBuildFileGenerator::InstallNameDir( diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h index 6b30815b9c..575d8e2149 100644 --- a/Source/cmExportBuildFileGenerator.h +++ b/Source/cmExportBuildFileGenerator.h @@ -86,7 +86,8 @@ protected: void ComplainAboutDuplicateTarget( std::string const& targetName) const override; - void ReportError(std::string const& errorMessage) const override; + void IssueMessage(MessageType type, + std::string const& message) const override; /** Fill in properties indicating built file locations. */ void SetImportLocationProperty(std::string const& config, diff --git a/Source/cmExportBuildPackageInfoGenerator.cxx b/Source/cmExportBuildPackageInfoGenerator.cxx index a8546c0b13..29951d3e2e 100644 --- a/Source/cmExportBuildPackageInfoGenerator.cxx +++ b/Source/cmExportBuildPackageInfoGenerator.cxx @@ -32,7 +32,7 @@ bool cmExportBuildPackageInfoGenerator::GenerateMainFile(std::ostream& os) return false; } - if (!this->CheckDefaultTargets()) { + if (!this->CheckPackage()) { return false; } diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index bae6b504a5..f53cba7529 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -13,6 +13,7 @@ #include #include "cmGeneratorExpression.h" +#include "cmMessageType.h" class cmExportSet; class cmGeneratorTarget; @@ -120,7 +121,13 @@ protected: cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap& properties); - virtual void ReportError(std::string const& errorMessage) const = 0; + virtual void IssueMessage(MessageType type, + std::string const& message) const = 0; + + void ReportError(std::string const& errorMessage) const + { + this->IssueMessage(MessageType::FATAL_ERROR, errorMessage); + } struct ExportInfo { diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index db78ba7862..26ddaad089 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -335,11 +335,11 @@ void cmExportInstallFileGenerator::ComplainAboutDuplicateTarget( this->ReportError(e.str()); } -void cmExportInstallFileGenerator::ReportError( - std::string const& errorMessage) const +void cmExportInstallFileGenerator::IssueMessage( + MessageType type, std::string const& message) const { this->IEGen->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, errorMessage, + type, message, this->IEGen->GetLocalGenerator()->GetMakefile()->GetBacktrace()); } diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h index 79dff1d243..60115640f8 100644 --- a/Source/cmExportInstallFileGenerator.h +++ b/Source/cmExportInstallFileGenerator.h @@ -94,7 +94,8 @@ protected: ExportInfo FindExportInfo(cmGeneratorTarget const* target) const override; - void ReportError(std::string const& errorMessage) const override; + void IssueMessage(MessageType type, + std::string const& message) const override; /** Generate a per-configuration file for the targets. */ virtual bool GenerateImportFileConfig(std::string const& config); diff --git a/Source/cmExportInstallPackageInfoGenerator.cxx b/Source/cmExportInstallPackageInfoGenerator.cxx index 7fab8dc9de..66dd05fc91 100644 --- a/Source/cmExportInstallPackageInfoGenerator.cxx +++ b/Source/cmExportInstallPackageInfoGenerator.cxx @@ -66,7 +66,7 @@ bool cmExportInstallPackageInfoGenerator::GenerateMainFile(std::ostream& os) } } - if (!this->CheckDefaultTargets()) { + if (!this->CheckPackage()) { return false; } diff --git a/Source/cmExportPackageInfoGenerator.cxx b/Source/cmExportPackageInfoGenerator.cxx index d63b2d597c..a3ad46e82f 100644 --- a/Source/cmExportPackageInfoGenerator.cxx +++ b/Source/cmExportPackageInfoGenerator.cxx @@ -16,6 +16,8 @@ #include #include +#include "cmsys/RegularExpression.hxx" + #include "cmArgumentParserTypes.h" #include "cmExportSet.h" #include "cmFindPackageStack.h" @@ -88,6 +90,63 @@ void BuildArray(Json::Value& object, std::string const& property, } } } + +bool CheckSimpleVersion(std::string const& version) +{ + cmsys::RegularExpression regex("^[0-9]+([.][0-9]+)*([-+].*)?$"); + return regex.find(version); +} +} + +bool cmExportPackageInfoGenerator::CheckVersion() const +{ + if (!this->PackageVersion.empty()) { + std::string const& schema = [&] { + if (this->PackageVersionSchema.empty()) { + return std::string{ "simple" }; + } + return cmSystemTools::LowerCase(this->PackageVersionSchema); + }(); + bool (*validator)(std::string const&) = nullptr; + bool result = true; + + if (schema == "simple"_s) { + validator = &CheckSimpleVersion; + } else if (schema == "dpkg"_s || schema == "rpm"_s || + schema == "pep440"_s) { + // TODO + // We don't validate these at this time. Eventually, we would like to do + // so, but will probably need to introduce a policy whether to treat + // invalid versions as an error. + } else if (schema != "custom"_s) { + this->IssueMessage(MessageType::AUTHOR_WARNING, + cmStrCat("Package \""_s, this->GetPackageName(), + "\" uses unrecognized version schema \""_s, + this->PackageVersionSchema, "\"."_s)); + } + + if (validator) { + if (!(*validator)(this->PackageVersion)) { + this->ReportError(cmStrCat("Package \""_s, this->GetPackageName(), + "\" version \""_s, this->PackageVersion, + "\" does not conform to the \""_s, schema, + "\" schema."_s)); + result = false; + } + if (!this->PackageVersionCompat.empty() && + !(*validator)(this->PackageVersionCompat)) { + this->ReportError( + cmStrCat("Package \""_s, this->GetPackageName(), + "\" compatibility version \""_s, this->PackageVersionCompat, + "\" does not conform to the \""_s, schema, "\" schema."_s)); + result = false; + } + } + + return result; + } + + return true; } bool cmExportPackageInfoGenerator::CheckDefaultTargets() const diff --git a/Source/cmExportPackageInfoGenerator.h b/Source/cmExportPackageInfoGenerator.h index 0614e27f0a..8ef85fa000 100644 --- a/Source/cmExportPackageInfoGenerator.h +++ b/Source/cmExportPackageInfoGenerator.h @@ -47,7 +47,10 @@ protected: // Methods to implement export file code generation. bool GenerateImportFile(std::ostream& os) override; - bool CheckDefaultTargets() const; + bool CheckPackage() const + { + return this->CheckVersion() && this->CheckDefaultTargets(); + } Json::Value GeneratePackageInfo() const; Json::Value* GenerateImportTarget(Json::Value& components, @@ -82,6 +85,9 @@ protected: cmGeneratorTarget const* linkedTarget) override; private: + bool CheckVersion() const; + bool CheckDefaultTargets() const; + void GenerateInterfaceLinkProperties( bool& result, Json::Value& component, cmGeneratorTarget const* target, ImportPropertyMap const& properties) const; diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx index 456e6b9453..9843bf7ecf 100644 --- a/Source/cmExportTryCompileFileGenerator.cxx +++ b/Source/cmExportTryCompileFileGenerator.cxx @@ -6,6 +6,7 @@ #include #include +#include #include "cmFileSet.h" #include "cmGenExContext.h" @@ -16,6 +17,7 @@ #include "cmList.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -31,10 +33,25 @@ cmExportTryCompileFileGenerator::cmExportTryCompileFileGenerator( gg->CreateImportedGenerationObjects(mf, targets, this->Exports); } -void cmExportTryCompileFileGenerator::ReportError( - std::string const& errorMessage) const +void cmExportTryCompileFileGenerator::IssueMessage( + MessageType type, std::string const& message) const { - cmSystemTools::Error(errorMessage); + switch (type) { + case MessageType::FATAL_ERROR: + case MessageType::AUTHOR_ERROR: + case MessageType::INTERNAL_ERROR: + case MessageType::DEPRECATION_ERROR: + cmSystemTools::Error(message); + break; + case MessageType::WARNING: + case MessageType::AUTHOR_WARNING: + case MessageType::DEPRECATION_WARNING: + cmSystemTools::Message(cmStrCat("CMake Warning: "_s, message), + "Warning"); + break; + default: + cmSystemTools::Message(message); + } } bool cmExportTryCompileFileGenerator::GenerateMainFile(std::ostream& os) diff --git a/Source/cmExportTryCompileFileGenerator.h b/Source/cmExportTryCompileFileGenerator.h index d9a8361381..00231ba6ff 100644 --- a/Source/cmExportTryCompileFileGenerator.h +++ b/Source/cmExportTryCompileFileGenerator.h @@ -30,7 +30,8 @@ protected: // Implement virtual methods from the superclass. void ComplainAboutDuplicateTarget( std::string const& /*targetName*/) const override {}; - void ReportError(std::string const& errorMessage) const override; + void IssueMessage(MessageType type, + std::string const& message) const override; bool GenerateMainFile(std::ostream& os) override; diff --git a/Tests/RunCMake/ExportPackageInfo/RunCMakeTest.cmake b/Tests/RunCMake/ExportPackageInfo/RunCMakeTest.cmake index b2255e5f6d..8f11a954d2 100644 --- a/Tests/RunCMake/ExportPackageInfo/RunCMakeTest.cmake +++ b/Tests/RunCMake/ExportPackageInfo/RunCMakeTest.cmake @@ -4,6 +4,10 @@ include(RunCMake) run_cmake(ExperimentalGate) run_cmake(ExperimentalWarning) +# Test version check author warning +# TODO Move to be with other tests when experimental gate is removed. +run_cmake(VersionCheckWarning) + # Enable experimental feature and suppress warnings set(RunCMake_TEST_OPTIONS -Wno-dev @@ -49,3 +53,6 @@ run_cmake(FileSetHeaders) run_cmake(DependencyVersionCMake) run_cmake(DependencyVersionCps) run_cmake(TransitiveSymbolicComponent) +run_cmake(VersionCheck) +# run_cmake(VersionCheckWarning) +run_cmake(VersionCheckError) diff --git a/Tests/RunCMake/ExportPackageInfo/VersionCheck.cmake b/Tests/RunCMake/ExportPackageInfo/VersionCheck.cmake new file mode 100644 index 0000000000..206b458584 --- /dev/null +++ b/Tests/RunCMake/ExportPackageInfo/VersionCheck.cmake @@ -0,0 +1,38 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) + +# Try exporting a 'properly' simple version. +export(EXPORT foo PACKAGE_INFO foo1 VERSION 1.2.3) + +# Try exporting a version with many components. +export(EXPORT foo PACKAGE_INFO foo2 VERSION 1.21.23.33.37.42.9.0.12) + +# Try exporting a version with a label. +export(EXPORT foo PACKAGE_INFO foo3 VERSION "1.2.3+git1234abcd") + +# Try exporting a version with a different label. +export(EXPORT foo PACKAGE_INFO foo4 VERSION "1.2.3-0.example") + +# Try exporting with the schema explicitly specified. +export( + EXPORT foo + PACKAGE_INFO foo5 + VERSION "1.2.3-0.example" + VERSION_SCHEMA "simple" +) + +# Try exporting with a custom-schema version. +export( + EXPORT foo + PACKAGE_INFO foo6 + VERSION "foo!test" + VERSION_SCHEMA "custom" +) + +# Try exporting with a recognized but not-checked schema. +export( + EXPORT foo + PACKAGE_INFO foo7 + VERSION "invalid" + VERSION_SCHEMA "pep440" +) diff --git a/Tests/RunCMake/ExportPackageInfo/VersionCheckError-result.txt b/Tests/RunCMake/ExportPackageInfo/VersionCheckError-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/ExportPackageInfo/VersionCheckError-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/ExportPackageInfo/VersionCheckError-stderr.txt b/Tests/RunCMake/ExportPackageInfo/VersionCheckError-stderr.txt new file mode 100644 index 0000000000..3321d78942 --- /dev/null +++ b/Tests/RunCMake/ExportPackageInfo/VersionCheckError-stderr.txt @@ -0,0 +1,2 @@ +CMake Error in CMakeLists\.txt: + Package "foo" version "1.2.3rc1" does not conform to the "simple" schema\. diff --git a/Tests/RunCMake/ExportPackageInfo/VersionCheckError.cmake b/Tests/RunCMake/ExportPackageInfo/VersionCheckError.cmake new file mode 100644 index 0000000000..1e766e6851 --- /dev/null +++ b/Tests/RunCMake/ExportPackageInfo/VersionCheckError.cmake @@ -0,0 +1,5 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) + +# Try exporting a non-conforming version. +export(EXPORT foo PACKAGE_INFO foo VERSION "1.2.3rc1") diff --git a/Tests/RunCMake/ExportPackageInfo/VersionCheckWarning-stderr.txt b/Tests/RunCMake/ExportPackageInfo/VersionCheckWarning-stderr.txt new file mode 100644 index 0000000000..0e833c2b52 --- /dev/null +++ b/Tests/RunCMake/ExportPackageInfo/VersionCheckWarning-stderr.txt @@ -0,0 +1,3 @@ +CMake Warning \(dev\) in CMakeLists\.txt: + Package "foo" uses unrecognized version schema "unrecognized"\. +This warning is for project developers\. Use -Wno-dev to suppress it\. diff --git a/Tests/RunCMake/ExportPackageInfo/VersionCheckWarning.cmake b/Tests/RunCMake/ExportPackageInfo/VersionCheckWarning.cmake new file mode 100644 index 0000000000..5a007e3343 --- /dev/null +++ b/Tests/RunCMake/ExportPackageInfo/VersionCheckWarning.cmake @@ -0,0 +1,15 @@ +set( + CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO + "b80be207-778e-46ba-8080-b23bba22639e" +) + +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) + +# Try exporting with an unrecognized schema. +export( + EXPORT foo + PACKAGE_INFO foo + VERSION "irrelevant" + VERSION_SCHEMA "unrecognized" +) diff --git a/Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake b/Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake index acfe2260e7..91e34b9692 100644 --- a/Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake +++ b/Tests/RunCMake/InstallPackageInfo/RunCMakeTest.cmake @@ -4,6 +4,10 @@ include(RunCMake) run_cmake(ExperimentalGate) run_cmake(ExperimentalWarning) +# Test version check author warning +# TODO Move to be with other tests when experimental gate is removed. +run_cmake(VersionCheckWarning) + # Enable experimental feature and suppress warnings set(RunCMake_TEST_OPTIONS -Wno-dev @@ -58,4 +62,7 @@ run_cmake(DependencyVersionCMake) run_cmake(DependencyVersionCps) run_cmake(TransitiveSymbolicComponent) run_cmake(InstallSymbolicComponent) +run_cmake(VersionCheck) +# run_cmake(VersionCheckWarning) +run_cmake(VersionCheckError) run_cmake_install(Destination) diff --git a/Tests/RunCMake/InstallPackageInfo/VersionCheck.cmake b/Tests/RunCMake/InstallPackageInfo/VersionCheck.cmake new file mode 100644 index 0000000000..13b638e19d --- /dev/null +++ b/Tests/RunCMake/InstallPackageInfo/VersionCheck.cmake @@ -0,0 +1,38 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) + +# Try exporting a 'properly' simple version. +install(PACKAGE_INFO foo1 EXPORT foo VERSION 1.2.3) + +# Try exporting a version with many components. +install(PACKAGE_INFO foo2 EXPORT foo VERSION 1.21.23.33.37.42.9.0.12) + +# Try exporting a version with a label. +install(PACKAGE_INFO foo3 EXPORT foo VERSION "1.2.3+git1234abcd") + +# Try exporting a version with a different label. +install(PACKAGE_INFO foo4 EXPORT foo VERSION "1.2.3-0.example") + +# Try exporting with the schema explicitly specified. +install( + PACKAGE_INFO foo5 + EXPORT foo + VERSION "1.2.3-0.example" + VERSION_SCHEMA "simple" +) + +# Try exporting with a custom-schema version. +install( + PACKAGE_INFO foo6 + EXPORT foo + VERSION "foo!test" + VERSION_SCHEMA "custom" +) + +# Try exporting with a recognized but not-checked schema. +install( + PACKAGE_INFO foo7 + EXPORT foo + VERSION "invalid" + VERSION_SCHEMA "pep440" +) diff --git a/Tests/RunCMake/InstallPackageInfo/VersionCheckError-result.txt b/Tests/RunCMake/InstallPackageInfo/VersionCheckError-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/InstallPackageInfo/VersionCheckError-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/InstallPackageInfo/VersionCheckError-stderr.txt b/Tests/RunCMake/InstallPackageInfo/VersionCheckError-stderr.txt new file mode 100644 index 0000000000..3321d78942 --- /dev/null +++ b/Tests/RunCMake/InstallPackageInfo/VersionCheckError-stderr.txt @@ -0,0 +1,2 @@ +CMake Error in CMakeLists\.txt: + Package "foo" version "1.2.3rc1" does not conform to the "simple" schema\. diff --git a/Tests/RunCMake/InstallPackageInfo/VersionCheckError.cmake b/Tests/RunCMake/InstallPackageInfo/VersionCheckError.cmake new file mode 100644 index 0000000000..2737f1c7ee --- /dev/null +++ b/Tests/RunCMake/InstallPackageInfo/VersionCheckError.cmake @@ -0,0 +1,5 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) + +# Try exporting a non-conforming version. +install(PACKAGE_INFO foo EXPORT foo VERSION "1.2.3rc1") diff --git a/Tests/RunCMake/InstallPackageInfo/VersionCheckWarning-stderr.txt b/Tests/RunCMake/InstallPackageInfo/VersionCheckWarning-stderr.txt new file mode 100644 index 0000000000..0e833c2b52 --- /dev/null +++ b/Tests/RunCMake/InstallPackageInfo/VersionCheckWarning-stderr.txt @@ -0,0 +1,3 @@ +CMake Warning \(dev\) in CMakeLists\.txt: + Package "foo" uses unrecognized version schema "unrecognized"\. +This warning is for project developers\. Use -Wno-dev to suppress it\. diff --git a/Tests/RunCMake/InstallPackageInfo/VersionCheckWarning.cmake b/Tests/RunCMake/InstallPackageInfo/VersionCheckWarning.cmake new file mode 100644 index 0000000000..3f5fa94d28 --- /dev/null +++ b/Tests/RunCMake/InstallPackageInfo/VersionCheckWarning.cmake @@ -0,0 +1,15 @@ +set( + CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO + "b80be207-778e-46ba-8080-b23bba22639e" +) + +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) + +# Try exporting with an unrecognized schema. +install( + PACKAGE_INFO foo + EXPORT foo + VERSION "irrelevant" + VERSION_SCHEMA "unrecognized" +)