diff --git a/CompileFlags.cmake b/CompileFlags.cmake index f94e079ad7..3080daae83 100644 --- a/CompileFlags.cmake +++ b/CompileFlags.cmake @@ -37,7 +37,13 @@ elseif(_CLANG_MSVC_WINDOWS AND "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUA string(APPEND CMAKE_EXE_LINKER_FLAGS " -Xlinker -stack:20000000") endif() -#silence duplicate symbol warnings on AIX +# Silence "Additional optimization may be attained by recompiling and +# specifying MAXMEM option" warning on XLC (AIX) +if(CMAKE_CXX_COMPILER_ID MATCHES "^(XL|XLClang)$") + string(APPEND CMAKE_CXX_FLAGS " -qmaxmem=-1") +endif() + +# Silence duplicate symbol warnings on AIX if(CMAKE_SYSTEM_NAME MATCHES "AIX") if(NOT CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -bhalt:5 ") diff --git a/Help/command/install.rst b/Help/command/install.rst index a2e7491053..b1991475b7 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -19,6 +19,7 @@ Synopsis install(`SCRIPT`_ [...]) install(`CODE`_ [...]) install(`EXPORT`_ [...]) + install(`PACKAGE_INFO`_ [...]) install(`RUNTIME_DEPENDENCY_SET`_ [...]) Introduction @@ -904,6 +905,61 @@ Signatures executable from the installation tree using the imported target name ``mp_myexe`` as if the target were built in its own tree. +.. signature:: + install(PACKAGE_INFO [...]) + + .. versionadded:: 3.31 + .. note:: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO``. + + Installs a |CPS|_ file exporting targets for dependent projects: + + .. code-block:: cmake + + install(PACKAGE_INFO EXPORT + [APPENDIX ] + [DESTINATION ] + [LOWER_CASE_FILE] + [VERSION + [COMPAT_VERSION ] + [VERSION_SCHEMA ]] + [DEFAULT_TARGETS ...] + [DEFAULT_CONFIGURATIONS ...] + [PERMISSIONS ...] + [CONFIGURATIONS ...] + [COMPONENT ] + [EXCLUDE_FROM_ALL]) + + The ``PACKAGE_INFO`` form generates and installs a |CPS| file which describes + installed targets such that they can be consumed by another project. + Target installations are associated with the export ```` + using the ``EXPORT`` option of the :command:`install(TARGETS)` signature + documented above. Unlike :command:`install(EXPORT)`, this information is not + expressed in CMake code, and can be consumed by tools other than CMake. When + imported into another CMake project, the imported targets will be prefixed + with ``::``. By default, the generated file will be called + ``[-].cps``. If ``LOWER_CASE_FILE`` is given, + the package name as it appears on disk (in both the file name and install + destination) will be first converted to lower case. + + If ``DESTINATION`` is not specified, a platform-specific default is used. + + 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 + (along with their external dependencies) from the rest of a package. This + enables consumers to ignore transitive dependencies for targets that they + don't use, and also allows a single logical "package" to be composed of + artifacts produced by multiple build trees. + + Appendices are not permitted to change basic package metadata; therefore, + none of ``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 + any appendices. + .. signature:: install(RUNTIME_DEPENDENCY_SET [...]) @@ -1096,3 +1152,6 @@ and by CPack. You can also invoke this script manually with This is an environment variable rather than a CMake variable. It allows you to change the installation prefix on UNIX systems. See :envvar:`DESTDIR` for details. + +.. _CPS: https://cps-org.github.io/cps/ +.. |CPS| replace:: Common Package Specification diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst index 35ea34f89c..fb33112606 100644 --- a/Help/dev/experimental.rst +++ b/Help/dev/experimental.rst @@ -39,6 +39,23 @@ When activated, this experimental feature provides the following: using the ``CMAKE_EXPORT_FIND_PACKAGE_NAME`` variable and/or ``EXPORT_FIND_PACKAGE_NAME`` target property. +Export |CPS| Package Information +================================ + +In order to activate support for this experimental feature, set + +* variable ``CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO`` to +* value ``b80be207-778e-46ba-8080-b23bba22639e``. + +This UUID may change in future versions of CMake. Be sure to use the value +documented here by the source tree of the version of CMake with which you are +experimenting. + +When activated, this experimental feature provides the following: + +* The experimental ``install(PACKAGE_INFO)`` command is available to export + package information in the |CPS|_ format. + C++ ``import std`` support ========================== @@ -60,3 +77,6 @@ When activated, this experimental feature provides the following: * Targets with the property set to a true value and at least ``cxx_std_23`` may use ``import std;`` in any scanned C++ source file. + +.. _CPS: https://cps-org.github.io/cps/ +.. |CPS| replace:: Common Package Specification diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 6841f1d3fa..67360bc051 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -217,6 +217,10 @@ add_library( cmExportInstallCMakeConfigGenerator.cxx cmExportInstallFileGenerator.h cmExportInstallFileGenerator.cxx + cmExportInstallPackageInfoGenerator.h + cmExportInstallPackageInfoGenerator.cxx + cmExportPackageInfoGenerator.h + cmExportPackageInfoGenerator.cxx cmExportTryCompileFileGenerator.h cmExportTryCompileFileGenerator.cxx cmExportSet.h @@ -336,6 +340,8 @@ add_library( cmInstallFilesGenerator.cxx cmInstallImportedRuntimeArtifactsGenerator.h cmInstallImportedRuntimeArtifactsGenerator.cxx + cmInstallPackageInfoExportGenerator.h + cmInstallPackageInfoExportGenerator.cxx cmInstallRuntimeDependencySet.h cmInstallRuntimeDependencySet.cxx cmInstallRuntimeDependencySetGenerator.h diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx index a2e6e70c6f..4504c07276 100644 --- a/Source/cmExperimental.cxx +++ b/Source/cmExperimental.cxx @@ -46,6 +46,16 @@ cmExperimental::FeatureData LookupTable[] = { {}, cmExperimental::TryCompileCondition::Always, false }, + // ExportPackageInfo + { "ExportPackageInfo", + "b80be207-778e-46ba-8080-b23bba22639e", + "CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO", + "CMake's support for exporting package information in the Common Package " + "Specification format. It is meant only for experimentation and feedback " + "to CMake developers.", + {}, + cmExperimental::TryCompileCondition::Always, + false }, }; static_assert(sizeof(LookupTable) / sizeof(LookupTable[0]) == static_cast(cmExperimental::Feature::Sentinel), diff --git a/Source/cmExperimental.h b/Source/cmExperimental.h index 05764f947e..46a9bc4757 100644 --- a/Source/cmExperimental.h +++ b/Source/cmExperimental.h @@ -20,6 +20,7 @@ public: ExportPackageDependencies, WindowsKernelModeDriver, CxxImportStd, + ExportPackageInfo, Sentinel, }; diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 24ed273d08..5e9461d654 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -331,6 +331,14 @@ void cmExportFileGenerator::PopulateCustomTransitiveInterfaceProperties( } } +bool cmExportFileGenerator::NoteLinkedTarget( + cmGeneratorTarget const* /*target*/, std::string const& /*linkedName*/, + cmGeneratorTarget const* /*linkedTarget*/) +{ + // Default implementation does nothing; only needed by some generators. + return true; +} + bool cmExportFileGenerator::AddTargetNamespace(std::string& input, cmGeneratorTarget const* target, cmLocalGenerator const* lg) @@ -352,8 +360,9 @@ bool cmExportFileGenerator::AddTargetNamespace(std::string& input, if (tgt->IsImported()) { input = tgt->GetName(); - return true; + return this->NoteLinkedTarget(target, input, tgt); } + if (this->ExportedTargets.find(tgt) != this->ExportedTargets.end()) { input = this->Namespace + tgt->GetExportName(); } else { @@ -365,7 +374,8 @@ bool cmExportFileGenerator::AddTargetNamespace(std::string& input, input = tgt->GetName(); } } - return true; + + return this->NoteLinkedTarget(target, input, tgt); } void cmExportFileGenerator::ResolveTargetsInGeneratorExpressions( diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index f765493ac1..b1c9ce3b11 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -95,6 +95,11 @@ protected: std::string const& config, std::string const& suffix) = 0; + /** Record a target referenced by an exported target. */ + virtual bool NoteLinkedTarget(cmGeneratorTarget const* target, + std::string const& linkedName, + cmGeneratorTarget const* linkedTarget); + /** Each subclass knows how to deal with a target that is missing from an * export set. */ virtual void HandleMissingTarget(std::string& link_libs, diff --git a/Source/cmExportInstallPackageInfoGenerator.cxx b/Source/cmExportInstallPackageInfoGenerator.cxx new file mode 100644 index 0000000000..b9b715edba --- /dev/null +++ b/Source/cmExportInstallPackageInfoGenerator.cxx @@ -0,0 +1,197 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmExportInstallPackageInfoGenerator.h" + +#include +#include +#include +#include + +#include + +#include "cmExportSet.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmInstallExportGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#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)) + , cmExportInstallFileGenerator(iegen) +{ +} + +std::string cmExportInstallPackageInfoGenerator::GetConfigImportFileGlob() + const +{ + std::string glob = cmStrCat(this->FileBase, "@*", this->FileExt); + return glob; +} + +std::string const& cmExportInstallPackageInfoGenerator::GetExportName() const +{ + return this->GetPackageName(); +} + +bool cmExportInstallPackageInfoGenerator::GenerateMainFile(std::ostream& os) +{ + std::vector allTargets; + { + auto visitor = [&](cmTargetExport const* te) { allTargets.push_back(te); }; + + if (!this->CollectExports(visitor)) { + return false; + } + } + + if (!this->CheckDefaultTargets()) { + return false; + } + + Json::Value root = this->GeneratePackageInfo(); + Json::Value& components = root["components"]; + + // Compute the relative import prefix for the file + std::string const& packagePath = this->GenerateImportPrefix(); + if (packagePath.empty()) { + return false; + } + root["cps_path"] = packagePath; + + bool requiresConfigFiles = false; + // Create all the imported targets. + for (cmTargetExport const* te : allTargets) { + cmGeneratorTarget* gt = te->Target; + cmStateEnums::TargetType targetType = this->GetExportTargetType(te); + + Json::Value* const component = + this->GenerateImportTarget(components, gt, targetType); + if (!component) { + return false; + } + + ImportPropertyMap properties; + if (!this->PopulateInterfaceProperties(te, properties)) { + return false; + } + this->PopulateInterfaceLinkLibrariesProperty( + gt, cmGeneratorExpression::InstallInterface, properties); + + if (targetType != cmStateEnums::INTERFACE_LIBRARY) { + requiresConfigFiles = true; + } + + // Set configuration-agnostic properties for component. + this->GenerateInterfaceProperties(*component, gt, properties); + } + + this->GeneratePackageRequires(root); + + // Write the primary packing information file. + this->WritePackageInfo(root, os); + + bool result = true; + + // Generate an import file for each configuration. + if (requiresConfigFiles) { + for (std::string const& c : this->Configurations) { + if (!this->GenerateImportFileConfig(c)) { + result = false; + } + } + } + + return result; +} + +void cmExportInstallPackageInfoGenerator::GenerateImportTargetsConfig( + std::ostream& os, std::string const& config, std::string const& suffix) +{ + Json::Value root; + root["name"] = this->GetPackageName(); + root["configuration"] = config; + + Json::Value& components = root["components"]; + + for (auto const& te : this->GetExportSet()->GetTargetExports()) { + // Collect import properties for this target. + if (this->GetExportTargetType(te.get()) == + cmStateEnums::INTERFACE_LIBRARY) { + continue; + } + + ImportPropertyMap properties; + std::set importedLocations; + + this->PopulateImportProperties(config, suffix, te.get(), properties, + importedLocations); + + this->GenerateInterfaceConfigProperties(components, te->Target, suffix, + properties); + } + + this->WritePackageInfo(root, os); +} + +std::string cmExportInstallPackageInfoGenerator::GenerateImportPrefix() const +{ + std::string expDest = this->IEGen->GetDestination(); + if (cmSystemTools::FileIsFullPath(expDest)) { + std::string const& installPrefix = + this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition( + "CMAKE_INSTALL_PREFIX"); + if (cmHasPrefix(expDest, installPrefix)) { + auto n = installPrefix.length(); + while (n < expDest.length() && expDest[n] == '/') { + ++n; + } + expDest = expDest.substr(n); + } else { + this->ReportError( + cmStrCat("install(PACKAGE_INFO \"", this->GetExportName(), + "\" ...) specifies DESTINATION \"", expDest, + "\" which is not a subdirectory of the install prefix.")); + return {}; + } + } + + if (expDest.empty()) { + return this->GetInstallPrefix(); + } + return cmStrCat(this->GetImportPrefixWithSlash(), expDest); +} + +std::string cmExportInstallPackageInfoGenerator::InstallNameDir( + cmGeneratorTarget const* target, std::string const& config) +{ + std::string install_name_dir; + + cmMakefile* mf = target->Target->GetMakefile(); + if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) { + install_name_dir = + target->GetInstallNameDirForInstallTree(config, "@prefix@"); + } + + return install_name_dir; +} + +std::string cmExportInstallPackageInfoGenerator::GetCxxModulesDirectory() const +{ + // TODO: Implement a not-CMake-specific mechanism for providing module + // information. + // return IEGen->GetCxxModuleDirectory(); + return {}; +} diff --git a/Source/cmExportInstallPackageInfoGenerator.h b/Source/cmExportInstallPackageInfoGenerator.h new file mode 100644 index 0000000000..5861b050d8 --- /dev/null +++ b/Source/cmExportInstallPackageInfoGenerator.h @@ -0,0 +1,66 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include +#include +#include + +#include "cmExportInstallFileGenerator.h" +#include "cmExportPackageInfoGenerator.h" + +class cmGeneratorTarget; +class cmInstallExportGenerator; + +/** \class cmExportInstallPackageInfoGenerator + * \brief Generate files exporting targets from an install tree. + * + * cmExportInstallPackageInfoGenerator generates files exporting targets from + * an installation tree. The files are placed in a temporary location for + * installation by cmInstallExportGenerator. The file format is the Common + * Package Specification (https://cps-org.github.io/cps/). + * + * One main file is generated that describes the imported targets. Additional, + * per-configuration files describe target locations and settings for each + * configuration. + * + * This is used to implement the INSTALL(PACKAGE_INFO) command. + */ +class cmExportInstallPackageInfoGenerator + : public cmExportPackageInfoGenerator + , public cmExportInstallFileGenerator +{ +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); + + /** Compute the globbing expression used to load per-config import + files from the main file. */ + std::string GetConfigImportFileGlob() const override; + +protected: + std::string const& GetExportName() const override; + + // Implement virtual methods from the superclass. + bool GenerateMainFile(std::ostream& os) override; + void GenerateImportTargetsConfig(std::ostream& os, std::string const& config, + std::string const& suffix) override; + + char GetConfigFileNameSeparator() const override { return '@'; } + + /** Generate the cps_path, which determines the import prefix. */ + std::string GenerateImportPrefix() const; + + std::string InstallNameDir(cmGeneratorTarget const* target, + std::string const& config) override; + + std::string GetCxxModulesDirectory() const override; + // TODO: Generate C++ module info in a not-CMake-specific format. +}; diff --git a/Source/cmExportPackageInfoGenerator.cxx b/Source/cmExportPackageInfoGenerator.cxx new file mode 100644 index 0000000000..7625953e26 --- /dev/null +++ b/Source/cmExportPackageInfoGenerator.cxx @@ -0,0 +1,452 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmExportPackageInfoGenerator.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "cmExportSet.h" +#include "cmFindPackageStack.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmList.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmValue.h" + +constexpr char const* cmExportPackageInfoGenerator::CPS_VERSION_STR; + +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)) +{ +} + +cm::string_view cmExportPackageInfoGenerator::GetImportPrefixWithSlash() const +{ + return "@prefix@/"_s; +} + +bool cmExportPackageInfoGenerator::GenerateImportFile(std::ostream& os) +{ + return this->GenerateMainFile(os); +} + +void cmExportPackageInfoGenerator::WritePackageInfo( + Json::Value const& packageInfo, std::ostream& os) const +{ + Json::StreamWriterBuilder builder; + builder["indentation"] = " "; + builder["commentStyle"] = "None"; + std::unique_ptr const writer(builder.newStreamWriter()); + writer->write(packageInfo, &os); +} + +namespace { +template +void buildArray(Json::Value& object, std::string const& property, + T const& values) +{ + if (!values.empty()) { + Json::Value& array = object[property]; + for (auto const& item : values) { + array.append(item); + } + } +} +} + +bool cmExportPackageInfoGenerator::CheckDefaultTargets() const +{ + bool result = true; + std::set exportedTargetNames; + for (auto const* te : this->ExportedTargets) { + exportedTargetNames.emplace(te->GetExportName()); + } + + for (auto const& name : this->DefaultTargets) { + if (!cm::contains(exportedTargetNames, name)) { + this->ReportError( + cmStrCat("Package \"", this->GetPackageName(), + "\" specifies DEFAULT_TARGETS \"", name, + "\", which is not a target in the export set \"", + this->GetExportSet()->GetName(), "\".")); + result = false; + } + } + + return result; +} + +Json::Value cmExportPackageInfoGenerator::GeneratePackageInfo() const +{ + Json::Value package; + + package["name"] = this->GetPackageName(); + package["cps_version"] = this->CPS_VERSION_STR; + + if (!this->PackageVersion.empty()) { + package["version"] = this->PackageVersion; + if (!this->PackageVersion.empty()) { + package["compat_version"] = this->PackageVersionCompat; + } + if (!this->PackageVersion.empty()) { + package["version_schema"] = this->PackageVersionSchema; + } + } + + buildArray(package, "default_components", this->DefaultTargets); + buildArray(package, "configurations", this->DefaultConfigurations); + + // TODO: description, website, license + + return package; +} + +void cmExportPackageInfoGenerator::GeneratePackageRequires( + Json::Value& package) const +{ + if (!this->Requirements.empty()) { + Json::Value& requirements = package["requires"]; + for (auto const& requirement : this->Requirements) { + // TODO: version, hint + requirements[requirement] = Json::Value{}; + } + } +} + +Json::Value* cmExportPackageInfoGenerator::GenerateImportTarget( + Json::Value& components, cmGeneratorTarget const* target, + cmStateEnums::TargetType targetType) const +{ + auto const& name = target->GetExportName(); + if (name.empty()) { + return nullptr; + } + + Json::Value& component = components[name]; + Json::Value& type = component["type"]; + switch (targetType) { + case cmStateEnums::EXECUTABLE: + type = "executable"; + break; + case cmStateEnums::STATIC_LIBRARY: + type = "archive"; + break; + case cmStateEnums::SHARED_LIBRARY: + type = "dylib"; + break; + case cmStateEnums::MODULE_LIBRARY: + type = "module"; + break; + case cmStateEnums::INTERFACE_LIBRARY: + type = "interface"; + break; + default: + type = "unknown"; + break; + } + return &component; +} + +bool cmExportPackageInfoGenerator::GenerateInterfaceProperties( + Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const +{ + bool result = true; + + this->GenerateInterfaceLinkProperties(result, component, target, properties); + + this->GenerateInterfaceCompileFeatures(result, component, target, + properties); + this->GenerateInterfaceCompileDefines(result, component, target, properties); + + this->GenerateInterfaceListProperty(result, component, target, + "compile_flags", "COMPILE_OPTIONS"_s, + properties); + this->GenerateInterfaceListProperty(result, component, target, "link_flags", + "LINK_OPTIONS"_s, properties); + this->GenerateInterfaceListProperty(result, component, target, + "link_directories", "LINK_DIRECTORIES"_s, + properties); + this->GenerateInterfaceListProperty(result, component, target, "includes", + "INCLUDE_DIRECTORIES"_s, properties); + + // TODO: description, license + + return result; +} + +namespace { +bool forbidGeneratorExpressions(std::string const& propertyName, + std::string const& propertyValue, + cmGeneratorTarget const* target) +{ + std::string const& evaluatedValue = cmGeneratorExpression::Preprocess( + propertyValue, cmGeneratorExpression::StripAllGeneratorExpressions); + if (evaluatedValue != propertyValue) { + target->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Property \"", propertyName, "\" of target \"", + target->GetName(), + "\" contains a generator expression. This is not allowed.")); + return false; + } + return true; +} +} + +bool cmExportPackageInfoGenerator::NoteLinkedTarget( + cmGeneratorTarget const* target, std::string const& linkedName, + cmGeneratorTarget const* linkedTarget) +{ + if (cm::contains(this->ExportedTargets, linkedTarget)) { + // Target is internal to this package. + this->LinkTargets.emplace(linkedName, + cmStrCat(':', linkedTarget->GetExportName())); + return true; + } + + if (linkedTarget->IsImported()) { + // Target is imported from a found package. + auto pkgName = [linkedTarget]() -> std::string { + auto const& pkgStack = linkedTarget->Target->GetFindPackageStack(); + if (!pkgStack.Empty()) { + return pkgStack.Top().Name; + } + + return linkedTarget->Target->GetProperty("EXPORT_FIND_PACKAGE_NAME"); + }(); + + if (pkgName.empty()) { + target->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", target->GetName(), + "\" references imported target \"", linkedName, + "\" which does not come from any known package.")); + return false; + } + + auto const& prefix = cmStrCat(pkgName, "::"); + if (!cmHasPrefix(linkedName, prefix)) { + target->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", target->GetName(), "\" references target \"", + linkedName, "\", which comes from the \"", pkgName, + "\" package, but does not belong to the package's " + "canonical namespace. This is not allowed.")); + return false; + } + + // TODO: Record package version, hint. + this->Requirements.emplace(pkgName); + this->LinkTargets.emplace( + linkedName, cmStrCat(pkgName, ':', linkedName.substr(prefix.length()))); + return true; + } + + // Target belongs to another export from this build. + auto const& exportInfo = this->FindExportInfo(linkedTarget); + if (exportInfo.first.size() == 1) { + auto const& linkNamespace = exportInfo.second; + if (!cmHasSuffix(linkNamespace, "::")) { + target->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", target->GetName(), "\" references target \"", + linkedName, + "\", which does not use the standard namespace separator. " + "This is not allowed.")); + return false; + } + + auto pkgName = + cm::string_view{ linkNamespace.data(), linkNamespace.size() - 2 }; + + if (pkgName == this->GetPackageName()) { + this->LinkTargets.emplace(linkedName, + cmStrCat(':', linkedTarget->GetExportName())); + } else { + this->Requirements.emplace(pkgName); + this->LinkTargets.emplace( + linkedName, cmStrCat(pkgName, ':', linkedTarget->GetExportName())); + } + return true; + } + + // cmExportFileGenerator::HandleMissingTarget should have complained about + // this already. (In fact, we probably shouldn't ever get here.) + return false; +} + +void cmExportPackageInfoGenerator::GenerateInterfaceLinkProperties( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const +{ + auto const& iter = properties.find("INTERFACE_LINK_LIBRARIES"); + if (iter == properties.end()) { + return; + } + + // TODO: Support $. + if (!forbidGeneratorExpressions(iter->first, iter->second, target)) { + result = false; + return; + } + + std::vector buildRequires; + // std::vector linkRequires; TODO + std::vector linkLibraries; + + for (auto const& name : cmList{ iter->second }) { + auto const& ti = this->LinkTargets.find(name); + if (ti != this->LinkTargets.end()) { + if (ti->second.empty()) { + result = false; + } else { + buildRequires.emplace_back(ti->second); + } + } else { + linkLibraries.emplace_back(name); + } + } + + buildArray(component, "requires", buildRequires); + // buildArray(component, "link_requires", linkRequires); TODO + buildArray(component, "link_libraries", linkLibraries); +} + +void cmExportPackageInfoGenerator::GenerateInterfaceCompileFeatures( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const +{ + auto const& iter = properties.find("INTERFACE_COMPILE_FEATURES"); + if (iter == properties.end()) { + return; + } + + if (!forbidGeneratorExpressions(iter->first, iter->second, target)) { + result = false; + return; + } + + std::set features; + for (auto const& value : cmList{ iter->second }) { + if (cmHasLiteralPrefix(value, "c_std_")) { + auto suffix = cm::string_view{ value }.substr(6, 2); + features.emplace(cmStrCat("cxx", suffix)); + } else if (cmHasLiteralPrefix(value, "cxx_std_")) { + auto suffix = cm::string_view{ value }.substr(8, 2); + features.emplace(cmStrCat("c++", suffix)); + } + } + + buildArray(component, "compile_features", features); +} + +void cmExportPackageInfoGenerator::GenerateInterfaceCompileDefines( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const +{ + auto const& iter = properties.find("INTERFACE_COMPILE_DEFINITIONS"); + if (iter == properties.end()) { + return; + } + + // TODO: Support language-specific defines. + if (!forbidGeneratorExpressions(iter->first, iter->second, target)) { + result = false; + return; + } + + Json::Value defines; + for (auto const& def : cmList{ iter->second }) { + auto const n = def.find('='); + if (n == std::string::npos) { + defines[def] = Json::Value{}; + } else { + defines[def.substr(0, n)] = def.substr(n + 1); + } + } + + if (!defines.empty()) { + component["compile_definitions"]["*"] = std::move(defines); + } +} + +void cmExportPackageInfoGenerator::GenerateInterfaceListProperty( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + std::string const& outName, cm::string_view inName, + ImportPropertyMap const& properties) const +{ + auto const& prop = cmStrCat("INTERFACE_", inName); + auto const& iter = properties.find(prop); + if (iter == properties.end()) { + return; + } + + if (!forbidGeneratorExpressions(prop, iter->second, target)) { + result = false; + return; + } + + Json::Value& array = component[outName]; + for (auto const& value : cmList{ iter->second }) { + array.append(value); + } +} + +void cmExportPackageInfoGenerator::GenerateInterfaceConfigProperties( + Json::Value& components, cmGeneratorTarget const* target, + std::string const& suffix, ImportPropertyMap const& properties) const +{ + Json::Value component; + auto const suffixLength = suffix.length(); + + for (auto const& p : properties) { + if (!cmHasSuffix(p.first, suffix)) { + continue; + } + auto const n = p.first.length() - suffixLength - 9; + auto const prop = cm::string_view{ p.first }.substr(9, n); + + if (prop == "LOCATION") { + component["location"] = p.second; + } else if (prop == "IMPLIB") { + component["link_location"] = p.second; + } else if (prop == "LINK_INTERFACE_LANGUAGES") { + std::vector languages; + for (auto const& lang : cmList{ p.second }) { + auto ll = cmSystemTools::LowerCase(lang); + if (ll == "cxx") { + languages.emplace_back("cpp"); + } else { + languages.emplace_back(std::move(ll)); + } + } + buildArray(component, "link_languages", languages); + } + } + + if (!component.empty()) { + components[target->GetExportName()] = component; + } +} diff --git a/Source/cmExportPackageInfoGenerator.h b/Source/cmExportPackageInfoGenerator.h new file mode 100644 index 0000000000..1fb17035b0 --- /dev/null +++ b/Source/cmExportPackageInfoGenerator.h @@ -0,0 +1,116 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include +#include +#include +#include +#include + +#include + +#include "cmExportFileGenerator.h" +#include "cmStateTypes.h" + +class cmGeneratorTarget; +namespace Json { +class Value; +} + +/** \class cmExportPackageInfoGenerator + * \brief Generate Common Package Specification package information files + * exporting targets from a build or install tree. + * + * cmExportPackageInfoGenerator is the superclass for + * cmExportBuildPackageInfoGenerator and cmExportInstallPackageInfoGenerator. + * It contains common code generation routines for the two kinds of export + * implementations. + */ +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); + + using cmExportFileGenerator::GenerateImportFile; + +protected: + std::string const& GetPackageName() const { return this->PackageName; } + + void WritePackageInfo(Json::Value const& packageInfo, + std::ostream& os) const; + + // Methods to implement export file code generation. + bool GenerateImportFile(std::ostream& os) override; + + bool CheckDefaultTargets() const; + + Json::Value GeneratePackageInfo() const; + Json::Value* GenerateImportTarget(Json::Value& components, + cmGeneratorTarget const* target, + cmStateEnums::TargetType targetType) const; + + void GeneratePackageRequires(Json::Value& package) const; + + using ImportPropertyMap = std::map; + bool GenerateInterfaceProperties(Json::Value& component, + cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const; + void GenerateInterfaceConfigProperties( + Json::Value& components, cmGeneratorTarget const* target, + std::string const& suffix, ImportPropertyMap const& properties) const; + + cm::string_view GetImportPrefixWithSlash() const override; + + std::string GetCxxModuleFile(std::string const& /*name*/) const override + { + // TODO + return {}; + } + + void GenerateCxxModuleConfigInformation(std::string const& /*name*/, + std::ostream& /*os*/) const override + { + // TODO + } + + bool NoteLinkedTarget(cmGeneratorTarget const* target, + std::string const& linkedName, + cmGeneratorTarget const* linkedTarget) override; + +private: + void GenerateInterfaceLinkProperties( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const; + + void GenerateInterfaceCompileFeatures( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const; + + void GenerateInterfaceCompileDefines( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const; + + void GenerateInterfaceListProperty( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + std::string const& outName, cm::string_view inName, + ImportPropertyMap const& properties) const; + + std::string const PackageName; + std::string const PackageVersion; + std::string const PackageVersionCompat; + std::string const PackageVersionSchema; + std::vector DefaultTargets; + std::vector DefaultConfigurations; + + std::map LinkTargets; + std::set Requirements; + + static constexpr char const* CPS_VERSION_STR = "0.12.0"; +}; diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 31605851a1..f4cc4c3b33 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -36,6 +36,7 @@ #include "cmInstallGenerator.h" #include "cmInstallGetRuntimeDependenciesGenerator.h" #include "cmInstallImportedRuntimeArtifactsGenerator.h" +#include "cmInstallPackageInfoExportGenerator.h" #include "cmInstallRuntimeDependencySet.h" #include "cmInstallRuntimeDependencySetGenerator.h" #include "cmInstallScriptGenerator.h" @@ -2162,6 +2163,143 @@ bool HandleExportMode(std::vector const& args, return true; } +bool HandlePackageInfoMode(std::vector const& args, + cmExecutionStatus& status) +{ +#ifndef CMAKE_BOOTSTRAP + if (!cmExperimental::HasSupportEnabled( + status.GetMakefile(), cmExperimental::Feature::ExportPackageInfo)) { + status.SetError("does not recognize sub-command PACKAGE_INFO"); + return false; + } + + Helper helper(status); + + // This is the PACKAGE_INFO mode. + cmInstallCommandArguments ica(helper.DefaultComponentName); + + ArgumentParser::NonEmpty pkg; + ArgumentParser::NonEmpty appendix; + ArgumentParser::NonEmpty exportName; + bool lowerCase = false; + ArgumentParser::NonEmpty version; + ArgumentParser::NonEmpty versionCompat; + ArgumentParser::NonEmpty versionSchema; + ArgumentParser::NonEmpty> defaultTargets; + ArgumentParser::NonEmpty> defaultConfigs; + ArgumentParser::NonEmpty cxxModulesDirectory; + + // TODO: Support DESTINATION. + ica.Bind("PACKAGE_INFO"_s, pkg); + 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; + ica.Parse(args, &unknownArgs); + + if (!unknownArgs.empty()) { + // Unknown argument. + status.SetError( + cmStrCat(args[0], " given unknown argument \"", unknownArgs[0], "\".")); + return false; + } + + if (!ica.Finalize()) { + return false; + } + + if (exportName.empty()) { + status.SetError(cmStrCat(args[0], " missing EXPORT.")); + 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, "\".")); + 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()) { + if (helper.Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Windows") { + dest = std::string{ "cps"_s }; + } else { + dest = cmStrCat(helper.GetLibraryDestination(nullptr), "/cps/", + pkgNameOnDisk); + } + } + + cmExportSet& exportSet = + helper.Makefile->GetGlobalGenerator()->GetExportSets()[exportName]; + + cmInstallGenerator::MessageLevel message = + cmInstallGenerator::SelectMessageLevel(helper.Makefile); + + // Create the export install generator. + helper.Makefile->AddInstallGenerator( + 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())); + + return true; +#else + static_cast(args); + status.SetError("PACKAGE_INFO not supported in bootstrap cmake"); + return false; +#endif +} + bool HandleRuntimeDependencySetMode(std::vector const& args, cmExecutionStatus& status) { @@ -2525,6 +2663,7 @@ bool cmInstallCommand(std::vector const& args, { "DIRECTORY"_s, HandleDirectoryMode }, { "EXPORT"_s, HandleExportMode }, { "EXPORT_ANDROID_MK"_s, HandleExportAndroidMKMode }, + { "PACKAGE_INFO"_s, HandlePackageInfoMode }, { "RUNTIME_DEPENDENCY_SET"_s, HandleRuntimeDependencySetMode }, }; diff --git a/Source/cmInstallPackageInfoExportGenerator.cxx b/Source/cmInstallPackageInfoExportGenerator.cxx new file mode 100644 index 0000000000..4ff045be92 --- /dev/null +++ b/Source/cmInstallPackageInfoExportGenerator.cxx @@ -0,0 +1,36 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmInstallPackageInfoExportGenerator.h" + +#include + +#include + +#include "cmExportInstallFileGenerator.h" +#include "cmExportInstallPackageInfoGenerator.h" +#include "cmListFileCache.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, + 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)) +{ + this->EFGen = cm::make_unique( + this, std::move(packageName), std::move(version), std::move(versionCompat), + std::move(versionSchema), std::move(defaultTargets), + std::move(defaultConfigurations)); +} + +cmInstallPackageInfoExportGenerator::~cmInstallPackageInfoExportGenerator() = + default; diff --git a/Source/cmInstallPackageInfoExportGenerator.h b/Source/cmInstallPackageInfoExportGenerator.h new file mode 100644 index 0000000000..c79df84774 --- /dev/null +++ b/Source/cmInstallPackageInfoExportGenerator.h @@ -0,0 +1,36 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include +#include + +#include "cmInstallExportGenerator.h" + +class cmExportSet; +class cmListFileBacktrace; + +/** \class cmInstallPackageInfoGenerator + * \brief Generate rules for creating CPS package info files. + */ +class cmInstallPackageInfoExportGenerator : public cmInstallExportGenerator +{ +public: + 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, + std::string cxxModulesDirectory, cmListFileBacktrace backtrace); + cmInstallPackageInfoExportGenerator( + cmInstallPackageInfoExportGenerator const&) = delete; + ~cmInstallPackageInfoExportGenerator() override; + + cmInstallPackageInfoExportGenerator& operator=( + cmInstallPackageInfoExportGenerator const&) = delete; + + char const* InstallSubcommand() const override { return "PACKAGE_INFO"; } +}; diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 1f7b8c5596..65295cc081 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -1177,6 +1177,7 @@ add_RunCMake_test(AutoExportDll ) add_RunCMake_test(AndroidMK) +add_RunCMake_test(PackageInfo) if(CMake_TEST_ANDROID_NDK OR CMake_TEST_ANDROID_STANDALONE_TOOLCHAIN) if(NOT "${CMAKE_GENERATOR}" MATCHES "Make|Ninja|Visual Studio 1[456]") diff --git a/Tests/RunCMake/PackageInfo/Appendix-check.cmake b/Tests/RunCMake/PackageInfo/Appendix-check.cmake new file mode 100644 index 0000000000..864e73186f --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Appendix-check.cmake @@ -0,0 +1,16 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/Appendix-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/foo.cps" content) +expect_value("${content}" "foo" "name") +expect_value("${content}" "interface" "components" "mammal" "type") +expect_value("${content}" "1.0" "version") + +file(READ "${out_dir}/foo-dog.cps" content) +expect_value("${content}" "foo" "name") +expect_value("${content}" "interface" "components" "canine" "type") +expect_missing("${content}" "version") + +expect_array("${content}" 1 "components" "canine" "requires") +expect_value("${content}" ":mammal" "components" "canine" "requires" 0) diff --git a/Tests/RunCMake/PackageInfo/Appendix.cmake b/Tests/RunCMake/PackageInfo/Appendix.cmake new file mode 100644 index 0000000000..fe6777803c --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Appendix.cmake @@ -0,0 +1,9 @@ +add_library(mammal INTERFACE) +add_library(canine INTERFACE) +target_link_libraries(canine INTERFACE mammal) + +install(TARGETS mammal EXPORT mammal DESTINATION .) +install(TARGETS canine EXPORT canine DESTINATION .) + +install(PACKAGE_INFO foo DESTINATION cps EXPORT mammal VERSION 1.0) +install(PACKAGE_INFO foo DESTINATION cps EXPORT canine APPENDIX dog) diff --git a/Tests/RunCMake/PackageInfo/Assertions.cmake b/Tests/RunCMake/PackageInfo/Assertions.cmake new file mode 100644 index 0000000000..815761841a --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Assertions.cmake @@ -0,0 +1,34 @@ +macro(_expect entity op actual expected) + if(NOT "${actual}" ${op} "${expected}") + list(JOIN ARGN "." name) + set(RunCMake_TEST_FAILED + "Attribute '${name}' ${entity} '${actual}' does not match expected ${entity} '${expected}'" PARENT_SCOPE) + return() + endif() +endmacro() + +function(expect_value content expected_value) + string(JSON actual_value GET "${content}" ${ARGN}) + _expect("value" STREQUAL "${actual_value}" "${expected_value}" ${ARGN}) +endfunction() + +function(expect_array content expected_length) + string(JSON actual_type TYPE "${content}" ${ARGN}) + _expect("type" STREQUAL "${actual_type}" "ARRAY" ${ARGN}) + + string(JSON actual_length LENGTH "${content}" ${ARGN}) + _expect("length" EQUAL "${actual_length}" "${expected_length}" ${ARGN}) +endfunction() + +function(expect_null content) + string(JSON actual_type TYPE "${content}" ${ARGN}) + _expect("type" STREQUAL "${actual_type}" "NULL" ${ARGN}) +endfunction() + +function(expect_missing content) + string(JSON value ERROR_VARIABLE error GET "${content}" ${ARGN}) + if(NOT value MATCHES "^(.*-)?NOTFOUND$") + set(RunCMake_TEST_FAILED + "Attribute '${ARGN}' is unexpectedly present" PARENT_SCOPE) + endif() +endfunction() diff --git a/Tests/RunCMake/PackageInfo/BadArgs1-result.txt b/Tests/RunCMake/PackageInfo/BadArgs1-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/BadArgs1-stderr.txt b/Tests/RunCMake/PackageInfo/BadArgs1-stderr.txt new file mode 100644 index 0000000000..92ba6fbf0e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs1-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at BadArgs1.cmake:3 \(install\): + install COMPAT_VERSION requires VERSION. diff --git a/Tests/RunCMake/PackageInfo/BadArgs1.cmake b/Tests/RunCMake/PackageInfo/BadArgs1.cmake new file mode 100644 index 0000000000..b99997c67c --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs1.cmake @@ -0,0 +1,3 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO test EXPORT foo COMPAT_VERSION 1.0) diff --git a/Tests/RunCMake/PackageInfo/BadArgs2-result.txt b/Tests/RunCMake/PackageInfo/BadArgs2-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/BadArgs2-stderr.txt b/Tests/RunCMake/PackageInfo/BadArgs2-stderr.txt new file mode 100644 index 0000000000..636335ccd3 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs2-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at BadArgs2.cmake:3 \(install\): + install VERSION_SCHEMA requires VERSION. diff --git a/Tests/RunCMake/PackageInfo/BadArgs2.cmake b/Tests/RunCMake/PackageInfo/BadArgs2.cmake new file mode 100644 index 0000000000..265d93c92b --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs2.cmake @@ -0,0 +1,3 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO test EXPORT foo VERSION_SCHEMA simple) diff --git a/Tests/RunCMake/PackageInfo/BadArgs3-result.txt b/Tests/RunCMake/PackageInfo/BadArgs3-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs3-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/BadArgs3-stderr.txt b/Tests/RunCMake/PackageInfo/BadArgs3-stderr.txt new file mode 100644 index 0000000000..11e1a8c738 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs3-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at BadArgs3.cmake:3 \(install\): + install APPENDIX and VERSION are mutually exclusive. diff --git a/Tests/RunCMake/PackageInfo/BadArgs3.cmake b/Tests/RunCMake/PackageInfo/BadArgs3.cmake new file mode 100644 index 0000000000..5f57f6a384 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs3.cmake @@ -0,0 +1,3 @@ +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/PackageInfo/BadArgs4-result.txt b/Tests/RunCMake/PackageInfo/BadArgs4-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs4-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/BadArgs4-stderr.txt b/Tests/RunCMake/PackageInfo/BadArgs4-stderr.txt new file mode 100644 index 0000000000..067a07bacb --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs4-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at BadArgs4.cmake:3 \(install\): + install APPENDIX and DEFAULT_TARGETS are mutually exclusive. diff --git a/Tests/RunCMake/PackageInfo/BadArgs4.cmake b/Tests/RunCMake/PackageInfo/BadArgs4.cmake new file mode 100644 index 0000000000..426d10b9bb --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs4.cmake @@ -0,0 +1,3 @@ +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/PackageInfo/BadArgs5-result.txt b/Tests/RunCMake/PackageInfo/BadArgs5-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs5-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/BadArgs5-stderr.txt b/Tests/RunCMake/PackageInfo/BadArgs5-stderr.txt new file mode 100644 index 0000000000..4f7d28524f --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs5-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at BadArgs5.cmake:3 \(install\): + install APPENDIX and DEFAULT_CONFIGURATIONS are mutually exclusive. diff --git a/Tests/RunCMake/PackageInfo/BadArgs5.cmake b/Tests/RunCMake/PackageInfo/BadArgs5.cmake new file mode 100644 index 0000000000..356c856b8c --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs5.cmake @@ -0,0 +1,3 @@ +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/PackageInfo/BadDefaultTarget-result.txt b/Tests/RunCMake/PackageInfo/BadDefaultTarget-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadDefaultTarget-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/BadDefaultTarget-stderr.txt b/Tests/RunCMake/PackageInfo/BadDefaultTarget-stderr.txt new file mode 100644 index 0000000000..6467a14f32 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadDefaultTarget-stderr.txt @@ -0,0 +1,2 @@ +CMake Error: Package "test" specifies DEFAULT_TARGETS "dog", which is not a target in the export set "foo". +CMake Error: Package "test" specifies DEFAULT_TARGETS "cat", which is not a target in the export set "foo". diff --git a/Tests/RunCMake/PackageInfo/BadDefaultTarget.cmake b/Tests/RunCMake/PackageInfo/BadDefaultTarget.cmake new file mode 100644 index 0000000000..d3d993a65c --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadDefaultTarget.cmake @@ -0,0 +1,5 @@ +add_library(foo INTERFACE) +add_library(dog INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(TARGETS dog EXPORT dog DESTINATION .) +install(PACKAGE_INFO test EXPORT foo DEFAULT_TARGETS dog cat) diff --git a/Tests/RunCMake/PackageInfo/CMakeLists.txt b/Tests/RunCMake/PackageInfo/CMakeLists.txt new file mode 100644 index 0000000000..dda37d8bc5 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.30) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/PackageInfo/ExperimentalGate-result.txt b/Tests/RunCMake/PackageInfo/ExperimentalGate-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ExperimentalGate-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/ExperimentalGate-stderr.txt b/Tests/RunCMake/PackageInfo/ExperimentalGate-stderr.txt new file mode 100644 index 0000000000..40799b71c6 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ExperimentalGate-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at ExperimentalGate.cmake:5 \(install\): + install does not recognize sub-command PACKAGE_INFO diff --git a/Tests/RunCMake/PackageInfo/ExperimentalGate.cmake b/Tests/RunCMake/PackageInfo/ExperimentalGate.cmake new file mode 100644 index 0000000000..327d3bb252 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ExperimentalGate.cmake @@ -0,0 +1,5 @@ +unset(CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO) + +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo DESTINATION cps EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/ExperimentalWarning-stderr.txt b/Tests/RunCMake/PackageInfo/ExperimentalWarning-stderr.txt new file mode 100644 index 0000000000..960d54114d --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ExperimentalWarning-stderr.txt @@ -0,0 +1,7 @@ +CMake Warning \(dev\) at ExperimentalWarning.cmake:8 \(install\): + CMake's support for exporting package information in the Common Package + Specification format. It is meant only for experimentation and feedback to + CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/PackageInfo/ExperimentalWarning.cmake b/Tests/RunCMake/PackageInfo/ExperimentalWarning.cmake new file mode 100644 index 0000000000..df6604cd60 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ExperimentalWarning.cmake @@ -0,0 +1,8 @@ +set( + CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO + "b80be207-778e-46ba-8080-b23bba22639e" + ) + +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo DESTINATION cps EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/InterfaceProperties-check.cmake b/Tests/RunCMake/PackageInfo/InterfaceProperties-check.cmake new file mode 100644 index 0000000000..2c3272ec3c --- /dev/null +++ b/Tests/RunCMake/PackageInfo/InterfaceProperties-check.cmake @@ -0,0 +1,24 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/InterfaceProperties-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/foo.cps" content) +expect_value("${content}" "foo" "name") + +string(JSON component GET "${content}" "components" "foo") + +expect_value("${component}" "interface" "type") +expect_array("${component}" 1 "includes") +expect_value("${component}" "@prefix@/include/foo" "includes" 0) +expect_array("${component}" 1 "compile_features") +expect_value("${component}" "c++23" "compile_features" 0) +expect_array("${component}" 1 "compile_flags") +expect_value("${component}" "-ffast-math" "compile_flags" 0) +expect_null("${component}" "compile_definitions" "*" "FOO") +expect_value("${component}" "BAR" "compile_definitions" "*" "BAR") +expect_array("${component}" 1 "link_directories") +expect_value("${component}" "/opt/foo/lib" "link_directories" 0) +expect_array("${component}" 1 "link_flags") +expect_value("${component}" "--needed" "link_flags" 0) +expect_array("${component}" 1 "link_libraries") +expect_value("${component}" "/usr/lib/libm.so" "link_libraries" 0) diff --git a/Tests/RunCMake/PackageInfo/InterfaceProperties.cmake b/Tests/RunCMake/PackageInfo/InterfaceProperties.cmake new file mode 100644 index 0000000000..42edc21fff --- /dev/null +++ b/Tests/RunCMake/PackageInfo/InterfaceProperties.cmake @@ -0,0 +1,15 @@ +add_library(foo INTERFACE) + +target_compile_features(foo INTERFACE cxx_std_23) +target_compile_options(foo INTERFACE -ffast-math) +target_compile_definitions(foo INTERFACE -DFOO -DBAR=BAR) +target_include_directories( + foo INTERFACE + $ + ) +target_link_directories(foo INTERFACE /opt/foo/lib) +target_link_options(foo INTERFACE --needed) +target_link_libraries(foo INTERFACE /usr/lib/libm.so) + +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo DESTINATION cps EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/LowerCaseFile-check.cmake b/Tests/RunCMake/PackageInfo/LowerCaseFile-check.cmake new file mode 100644 index 0000000000..d8de372030 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/LowerCaseFile-check.cmake @@ -0,0 +1,9 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/LowerCaseFile-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/lowercase.cps" content) +expect_value("${content}" "LowerCase" "name") + +file(READ "${out_dir}/PreserveCase.cps" content) +expect_value("${content}" "PreserveCase" "name") diff --git a/Tests/RunCMake/PackageInfo/LowerCaseFile.cmake b/Tests/RunCMake/PackageInfo/LowerCaseFile.cmake new file mode 100644 index 0000000000..dc6827fa32 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/LowerCaseFile.cmake @@ -0,0 +1,4 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO LowerCase DESTINATION cps EXPORT foo LOWER_CASE_FILE) +install(PACKAGE_INFO PreserveCase DESTINATION cps EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/Metadata-check.cmake b/Tests/RunCMake/PackageInfo/Metadata-check.cmake new file mode 100644 index 0000000000..8db8c298a7 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Metadata-check.cmake @@ -0,0 +1,16 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/Metadata-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.2.0" "compat_version") +expect_value("${content}" "simple" "version_schema") + +expect_array("${content}" 1 "default_components") +expect_value("${content}" "foo" "default_components" 0) + +expect_array("${content}" 2 "configurations") +expect_value("${content}" "release" "configurations" 0) +expect_value("${content}" "debug" "configurations" 1) diff --git a/Tests/RunCMake/PackageInfo/Metadata.cmake b/Tests/RunCMake/PackageInfo/Metadata.cmake new file mode 100644 index 0000000000..f8fc9b873d --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Metadata.cmake @@ -0,0 +1,12 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install( + PACKAGE_INFO foo + DESTINATION cps + EXPORT foo + VERSION 1.2.3 + VERSION_SCHEMA simple + COMPAT_VERSION 1.2.0 + DEFAULT_TARGETS foo + DEFAULT_CONFIGURATIONS release debug + ) diff --git a/Tests/RunCMake/PackageInfo/Minimal-check.cmake b/Tests/RunCMake/PackageInfo/Minimal-check.cmake new file mode 100644 index 0000000000..9608ed8fa6 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Minimal-check.cmake @@ -0,0 +1,18 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/Minimal-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/foo.cps" content) +expect_value("${content}" "foo" "name") +expect_value("${content}" "interface" "components" "foo" "type") +expect_missing("${content}" "version") +expect_missing("${content}" "configurations") +expect_missing("${content}" "default_targets") +expect_missing("${content}" "components" "foo" "compile_definitions") +expect_missing("${content}" "components" "foo" "compile_features") +expect_missing("${content}" "components" "foo" "compile_flags") +expect_missing("${content}" "components" "foo" "link_directories") +expect_missing("${content}" "components" "foo" "link_features") +expect_missing("${content}" "components" "foo" "link_flags") +expect_missing("${content}" "components" "foo" "link_libraries") +expect_missing("${content}" "components" "foo" "requires") diff --git a/Tests/RunCMake/PackageInfo/Minimal.cmake b/Tests/RunCMake/PackageInfo/Minimal.cmake new file mode 100644 index 0000000000..6c060b91c3 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Minimal.cmake @@ -0,0 +1,3 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo DESTINATION cps EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-result.txt b/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-stderr.txt b/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-stderr.txt new file mode 100644 index 0000000000..c68d51882f --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-stderr.txt @@ -0,0 +1 @@ +CMake Error: install\(PACKAGE_INFO "dog" \.\.\.\) includes target "canine" which requires target "mammal" that is not in any export set. diff --git a/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget.cmake b/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget.cmake new file mode 100644 index 0000000000..a83558262a --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget.cmake @@ -0,0 +1,6 @@ +add_library(mammal INTERFACE) +add_library(canine INTERFACE) +target_link_libraries(canine INTERFACE mammal) + +install(TARGETS canine EXPORT dog DESTINATION .) +install(PACKAGE_INFO dog EXPORT dog) diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-result.txt b/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-stderr.txt b/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-stderr.txt new file mode 100644 index 0000000000..0a74e185ae --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-stderr.txt @@ -0,0 +1,7 @@ +CMake Error in CMakeLists.txt: + Target "test" references target "foo", which does not use the standard + namespace separator. This is not allowed. +.* +CMake Error in CMakeLists.txt: + Target "test" references target "bar_bar", which does not use the standard + namespace separator. This is not allowed. diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget.cmake b/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget.cmake new file mode 100644 index 0000000000..3e3d21d237 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget.cmake @@ -0,0 +1,14 @@ +add_library(foo INTERFACE) +add_library(bar INTERFACE) + +add_library(test INTERFACE) +target_link_libraries(test INTERFACE foo bar) + +install(TARGETS foo EXPORT foo DESTINATION .) +install(TARGETS bar EXPORT bar DESTINATION .) + +install(EXPORT foo DESTINATION .) +install(EXPORT bar DESTINATION . NAMESPACE bar_) + +install(TARGETS test EXPORT test DESTINATION .) +install(PACKAGE_INFO test EXPORT test) diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-result.txt b/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-stderr.txt b/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-stderr.txt new file mode 100644 index 0000000000..cc4e824d87 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-stderr.txt @@ -0,0 +1,3 @@ +CMake Error in CMakeLists.txt: + Target "foo" references imported target "bar" which does not come from any + known package. diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget.cmake b/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget.cmake new file mode 100644 index 0000000000..8addd64c27 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget.cmake @@ -0,0 +1,7 @@ +add_library(bar INTERFACE IMPORTED) + +add_library(foo INTERFACE) +target_link_libraries(foo INTERFACE bar) + +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-result.txt b/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-stderr.txt b/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-stderr.txt new file mode 100644 index 0000000000..0a6872e32d --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-stderr.txt @@ -0,0 +1,4 @@ +CMake Error in CMakeLists.txt: + Target "foo" references target "wrong::lib", which comes from the "broken" + package, but does not belong to the package's canonical namespace. This is + not allowed. diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget.cmake b/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget.cmake new file mode 100644 index 0000000000..7a4f9c0153 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget.cmake @@ -0,0 +1,11 @@ +find_package( + broken REQUIRED CONFIG + NO_DEFAULT_PATH + PATHS ${CMAKE_CURRENT_LIST_DIR} + ) + +add_library(foo INTERFACE) +target_link_libraries(foo INTERFACE wrong::lib) + +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/Requirements-check.cmake b/Tests/RunCMake/PackageInfo/Requirements-check.cmake new file mode 100644 index 0000000000..59a212f648 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Requirements-check.cmake @@ -0,0 +1,20 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/Requirements-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/foo.cps" content) +expect_value("${content}" "foo" "name") +expect_value("${content}" "interface" "components" "libb" "type") + +file(READ "${out_dir}/bar.cps" content) +expect_value("${content}" "bar" "name") +expect_null("${content}" "requires" "foo") +expect_null("${content}" "requires" "test") +expect_value("${content}" "interface" "components" "libc" "type") +expect_value("${content}" "interface" "components" "libd" "type") + +string(JSON component GET "${content}" "components" "libd") +expect_array("${component}" 3 "requires") +expect_value("${component}" "test:liba" "requires" 0) +expect_value("${component}" "foo:libb" "requires" 1) +expect_value("${component}" ":libc" "requires" 2) diff --git a/Tests/RunCMake/PackageInfo/Requirements.cmake b/Tests/RunCMake/PackageInfo/Requirements.cmake new file mode 100644 index 0000000000..a4e947c52e --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Requirements.cmake @@ -0,0 +1,20 @@ +find_package( + test REQUIRED CONFIG + NO_DEFAULT_PATH + PATHS ${CMAKE_CURRENT_LIST_DIR} + ) + +add_library(libb INTERFACE) +add_library(libc INTERFACE) +add_library(libd INTERFACE) + +add_library(foo ALIAS libb) +add_library(bar ALIAS libc) + +target_link_libraries(libd INTERFACE test::liba foo bar) + +install(TARGETS libb EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo DESTINATION cps EXPORT foo) + +install(TARGETS libc libd EXPORT bar DESTINATION .) +install(PACKAGE_INFO bar DESTINATION cps EXPORT bar) diff --git a/Tests/RunCMake/PackageInfo/RunCMakeTest.cmake b/Tests/RunCMake/PackageInfo/RunCMakeTest.cmake new file mode 100644 index 0000000000..54a32d5595 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/RunCMakeTest.cmake @@ -0,0 +1,32 @@ +include(RunCMake) + +# Test experimental gate +run_cmake(ExperimentalGate) +run_cmake(ExperimentalWarning) + +# Enable experimental feature and suppress warnings +set(RunCMake_TEST_OPTIONS + -Wno-dev + "-DCMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO:STRING=b80be207-778e-46ba-8080-b23bba22639e" + ) + +# Test incorrect usage +run_cmake(BadArgs1) +run_cmake(BadArgs2) +run_cmake(BadArgs3) +run_cmake(BadArgs4) +run_cmake(BadArgs5) +run_cmake(BadDefaultTarget) +run_cmake(ReferencesNonExportedTarget) +run_cmake(ReferencesWronglyExportedTarget) +run_cmake(ReferencesWronglyImportedTarget) +run_cmake(ReferencesWronglyNamespacedTarget) + +# Test functionality +run_cmake(Appendix) +run_cmake(InterfaceProperties) +run_cmake(Metadata) +run_cmake(Minimal) +run_cmake(LowerCaseFile) +run_cmake(Requirements) +run_cmake(TargetTypes) diff --git a/Tests/RunCMake/PackageInfo/TargetTypes-check.cmake b/Tests/RunCMake/PackageInfo/TargetTypes-check.cmake new file mode 100644 index 0000000000..34ca5abc5d --- /dev/null +++ b/Tests/RunCMake/PackageInfo/TargetTypes-check.cmake @@ -0,0 +1,11 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/TargetTypes-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/foo.cps" content) +expect_value("${content}" "foo" "name") +expect_value("${content}" "archive" "components" "foo-static" "type") +expect_value("${content}" "dylib" "components" "foo-shared" "type") +expect_value("${content}" "module" "components" "foo-module" "type") +expect_value("${content}" "interface" "components" "bar" "type") +expect_value("${content}" "executable" "components" "test" "type") diff --git a/Tests/RunCMake/PackageInfo/TargetTypes.cmake b/Tests/RunCMake/PackageInfo/TargetTypes.cmake new file mode 100644 index 0000000000..755c3a36a0 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/TargetTypes.cmake @@ -0,0 +1,20 @@ +project(TargetTypes CXX) + +add_library(foo-static STATIC foo.cxx) +add_library(foo-shared SHARED foo.cxx) +add_library(foo-module MODULE foo.cxx) +add_library(bar INTERFACE) +add_executable(test test.cxx) + +install( + TARGETS + foo-static + foo-shared + foo-module + bar + test + EXPORT foo + DESTINATION . + ) + +install(PACKAGE_INFO foo DESTINATION cps EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/broken-config.cmake b/Tests/RunCMake/PackageInfo/broken-config.cmake new file mode 100644 index 0000000000..09e40df242 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/broken-config.cmake @@ -0,0 +1 @@ +add_library(wrong::lib INTERFACE IMPORTED) diff --git a/Tests/RunCMake/PackageInfo/foo.cxx b/Tests/RunCMake/PackageInfo/foo.cxx new file mode 100644 index 0000000000..3695dc91e3 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/foo.cxx @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/Tests/RunCMake/PackageInfo/test-config.cmake b/Tests/RunCMake/PackageInfo/test-config.cmake new file mode 100644 index 0000000000..3c46ea684d --- /dev/null +++ b/Tests/RunCMake/PackageInfo/test-config.cmake @@ -0,0 +1 @@ +add_library(test::liba INTERFACE IMPORTED) diff --git a/Tests/RunCMake/PackageInfo/test.cxx b/Tests/RunCMake/PackageInfo/test.cxx new file mode 100644 index 0000000000..f8b643afbf --- /dev/null +++ b/Tests/RunCMake/PackageInfo/test.cxx @@ -0,0 +1,4 @@ +int main() +{ + return 0; +}