From c6e6861e631ecfdc96c79f562a66021cb8971fcc Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Tue, 7 Nov 2023 11:44:00 -0500 Subject: install(EXPORT): Export find_dependency() calls Issue: #20511 Co-Authored-by: Brad King Co-Authored-by: Robert Maynard --- Help/command/export.rst | 55 ++++++++++++- Help/command/install.rst | 31 ++++++- Help/manual/cmake-properties.7.rst | 1 + Help/manual/cmake-variables.7.rst | 1 + Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst | 12 +++ Help/release/dev/export-find_dependency-calls.rst | 14 ++++ Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst | 6 ++ Source/CMakeLists.txt | 2 + Source/cmExportBuildAndroidMKGenerator.h | 1 + Source/cmExportBuildFileGenerator.h | 1 + Source/cmExportCommand.cxx | 74 ++++++++++++++++- Source/cmExportFileGenerator.cxx | 94 +++++++++++++++++++++- Source/cmExportFileGenerator.h | 13 +++ Source/cmExportInstallAndroidMKGenerator.h | 1 + Source/cmExportInstallFileGenerator.h | 8 +- Source/cmExportSet.cxx | 11 +++ Source/cmExportSet.h | 28 +++++++ Source/cmFindPackageCommand.cxx | 2 + Source/cmFindPackageStack.cxx | 7 ++ Source/cmFindPackageStack.h | 33 ++++++++ Source/cmInstallCommand.cxx | 7 +- Source/cmInstallExportGenerator.cxx | 4 +- Source/cmInstallExportGenerator.h | 4 +- Source/cmMakefile.cxx | 35 ++++++++ Source/cmMakefile.h | 18 +++++ Source/cmTarget.cxx | 11 +++ Source/cmTarget.h | 4 + Tests/ExportImport/Export/CMakeLists.txt | 5 +- Tests/ExportImport/External/FooConfig.cmake | 3 + Tests/ExportImport/Import/CMakeLists.txt | 1 + Tests/ExportImport/InitialCache.cmake.in | 1 + Tests/RunCMake/export/CMake/FindHasDeps.cmake | 17 ++++ Tests/RunCMake/export/CMake/FindP1.cmake | 2 + Tests/RunCMake/export/CMake/FindP2.cmake | 2 + Tests/RunCMake/export/CMake/FindP3.cmake | 2 + Tests/RunCMake/export/CMake/FindP4.cmake | 2 + Tests/RunCMake/export/CMake/FindP9.cmake | 2 + .../export/FindDependencyExport-check.cmake | 35 ++++++++ Tests/RunCMake/export/FindDependencyExport.cmake | 30 +++++++ .../FindDependencyExportFetchContent-check.cmake | 14 ++++ .../export/FindDependencyExportFetchContent.cmake | 37 +++++++++ .../export/FindDependencyExportShared-check.cmake | 4 + .../export/FindDependencyExportShared.cmake | 14 ++++ .../export/FindDependencyExportStatic-check.cmake | 13 +++ .../export/FindDependencyExportStatic.cmake | 14 ++++ Tests/RunCMake/export/RunCMakeTest.cmake | 4 + .../EXPORT-FindDependencyExport-all-check.cmake | 4 + .../install/EXPORT-FindDependencyExport.cmake | 18 +++++ Tests/RunCMake/install/RunCMakeTest.cmake | 1 + Tests/RunCMake/install/cmake/FindP1.cmake | 2 + Tests/RunCMake/install/cmake/FindP2.cmake | 2 + Tests/RunCMake/install/cmake/FindP3.cmake | 2 + bootstrap | 1 + 53 files changed, 700 insertions(+), 10 deletions(-) create mode 100644 Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst create mode 100644 Help/release/dev/export-find_dependency-calls.rst create mode 100644 Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst create mode 100644 Source/cmFindPackageStack.cxx create mode 100644 Source/cmFindPackageStack.h create mode 100644 Tests/ExportImport/External/FooConfig.cmake create mode 100644 Tests/RunCMake/export/CMake/FindHasDeps.cmake create mode 100644 Tests/RunCMake/export/CMake/FindP1.cmake create mode 100644 Tests/RunCMake/export/CMake/FindP2.cmake create mode 100644 Tests/RunCMake/export/CMake/FindP3.cmake create mode 100644 Tests/RunCMake/export/CMake/FindP4.cmake create mode 100644 Tests/RunCMake/export/CMake/FindP9.cmake create mode 100644 Tests/RunCMake/export/FindDependencyExport-check.cmake create mode 100644 Tests/RunCMake/export/FindDependencyExport.cmake create mode 100644 Tests/RunCMake/export/FindDependencyExportFetchContent-check.cmake create mode 100644 Tests/RunCMake/export/FindDependencyExportFetchContent.cmake create mode 100644 Tests/RunCMake/export/FindDependencyExportShared-check.cmake create mode 100644 Tests/RunCMake/export/FindDependencyExportShared.cmake create mode 100644 Tests/RunCMake/export/FindDependencyExportStatic-check.cmake create mode 100644 Tests/RunCMake/export/FindDependencyExportStatic.cmake create mode 100644 Tests/RunCMake/install/EXPORT-FindDependencyExport-all-check.cmake create mode 100644 Tests/RunCMake/install/EXPORT-FindDependencyExport.cmake create mode 100644 Tests/RunCMake/install/cmake/FindP1.cmake create mode 100644 Tests/RunCMake/install/cmake/FindP2.cmake create mode 100644 Tests/RunCMake/install/cmake/FindP3.cmake diff --git a/Help/command/export.rst b/Help/command/export.rst index d712901..0440c50 100644 --- a/Help/command/export.rst +++ b/Help/command/export.rst @@ -15,6 +15,7 @@ Synopsis export(`TARGETS`_ ... [...]) export(`EXPORT`_ [...]) export(`PACKAGE`_ ) + export(`SETUP`_ [...]) Exporting Targets ^^^^^^^^^^^^^^^^^ @@ -108,7 +109,7 @@ Exporting Targets matching install(EXPORT) .. code-block:: cmake export(EXPORT [NAMESPACE ] [FILE ] - [CXX_MODULES_DIRECTORY ]) + [CXX_MODULES_DIRECTORY ] [EXPORT_PACKAGE_DEPENDENCIES]) Creates a file ```` that may be included by outside projects to import targets from the current project's build tree. This is the same @@ -118,6 +119,12 @@ the installation export ````. Target installations may be associated with the export ```` using the ``EXPORT`` option of the :command:`install(TARGETS)` command. +``EXPORT_PACKAGE_DEPENDENCIES`` + .. versionadded:: 3.29 + + Specify that :command:`find_dependency` calls should be exported. See + :command:`install(EXPORT)` for details on how this works. + Exporting Packages ^^^^^^^^^^^^^^^^^^ @@ -149,3 +156,49 @@ registry. outside the source and build trees. Set the :variable:`CMAKE_EXPORT_PACKAGE_REGISTRY` variable to add build directories to the CMake user package registry. + +Configuring Exports +^^^^^^^^^^^^^^^^^^^ + +.. signature:: + export(SETUP [...]) + +.. code-block:: cmake + + export(SETUP + [PACKAGE_DEPENDENCY + [ENABLED (||AUTO)] + [EXTRA_ARGS ...] + ] [...] + ) + +.. versionadded:: 3.29 + +Configure the parameters of an export. The arguments are as follows: + +``PACKAGE_DEPENDENCY `` + Specify a package dependency to configure. This changes how + :command:`find_dependency` calls are written during + :command:`export(EXPORT)` and :command:`install(EXPORT)`. ```` is the + name of a package to export. This argument accepts the following additional + arguments: + + ``ENABLED`` + Manually control whether or not the dependency is exported. This accepts + the following values: + + ```` + Any value that CMake recognizes as "true". Always export the dependency, + even if no exported targets depend on it. This can be used to manually + add :command:`find_dependency` calls to the export. + + ```` + Any value that CMake recognizes as "false". Never export the dependency, + even if an exported target depends on it. + + ``AUTO`` + Only export the dependency if an exported target depends on it. + + ``EXTRA_ARGS `` + Specify additional arguments to pass to :command:`find_dependency` after + the ``REQUIRED`` argument. diff --git a/Help/command/install.rst b/Help/command/install.rst index b0698dd..0c2a32a 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -784,7 +784,8 @@ Signatures [CXX_MODULES_DIRECTORY ] [EXPORT_LINK_INTERFACE_LIBRARIES] [COMPONENT ] - [EXCLUDE_FROM_ALL]) + [EXCLUDE_FROM_ALL] + [EXPORT_PACKAGE_DEPENDENCIES]) install(EXPORT_ANDROID_MK DESTINATION [...]) The ``EXPORT`` form generates and installs a CMake file containing code to @@ -848,6 +849,34 @@ Signatures without this information, none of the C++ modules which are part of the targets in the export set will support being imported in consuming targets. + ``EXPORT_PACKAGE_DEPENDENCIES`` + .. versionadded:: 3.29 + + Specify that :command:`find_dependency` calls should be exported. If this + argument is specified, CMake examines all targets in the export set and + gathers their ``INTERFACE`` link targets. If any such targets either were + found with :command:`find_package` or have the + :prop_tgt:`EXPORT_FIND_PACKAGE_NAME` property set, and such package + dependency was not disabled by passing ``ENABLED OFF`` to + :command:`export(SETUP)`, then a :command:`find_dependency` call is + written with the target's corresponding package name, a ``REQUIRED`` + argument, and any additional arguments specified by the ``EXTRA_ARGS`` + argument of :command:`export(SETUP)`. Any package dependencies that were + manually specified by passing ``ENABLED ON`` to :command:`export(SETUP)` + are also added, even if the exported targets don't depend on any targets + from them. + + The :command:`find_dependency` calls are written in the following order: + + 1. Any package dependencies that were listed in :command:`export(SETUP)` + are written in the order they were first specified, regardless of + whether or not they contain ``INTERFACE`` dependencies of the + exported targets. + 2. Any package dependencies that contain ``INTERFACE`` link dependencies + of the exported targets and that were never specified in + :command:`export(SETUP)` are written in the order they were first + found. + The ``EXPORT`` form is useful to help outside projects use targets built and installed by the current project. For example, the code diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 55054f5..276cb13 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -211,6 +211,7 @@ Properties on Targets /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD_CONFIG /prop_tgt/EXPORT_COMPILE_COMMANDS + /prop_tgt/EXPORT_FIND_PACKAGE_NAME /prop_tgt/EXPORT_NAME /prop_tgt/EXPORT_NO_SYSTEM /prop_tgt/EXPORT_PROPERTIES diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 4ce3b2f..a73494e 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -444,6 +444,7 @@ Variables that Control the Build /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG_INIT /variable/CMAKE_EXE_LINKER_FLAGS_INIT + /variable/CMAKE_EXPORT_FIND_PACKAGE_NAME /variable/CMAKE_FOLDER /variable/CMAKE_Fortran_FORMAT /variable/CMAKE_Fortran_MODULE_DIRECTORY diff --git a/Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst b/Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst new file mode 100644 index 0000000..b49f005 --- /dev/null +++ b/Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst @@ -0,0 +1,12 @@ +EXPORT_FIND_PACKAGE_NAME +------------------------ + +.. versionadded:: 3.29 + +Control the package name associated with a dependency target when exporting a +:command:`find_dependency` call in :command:`install(EXPORT)` or +:command:`export(EXPORT)`. This can be used to assign a package name to a +package that is built by CMake and exported, or to override the package in the +:command:`find_package` call that created the target. + +This property is initialized by :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME`. diff --git a/Help/release/dev/export-find_dependency-calls.rst b/Help/release/dev/export-find_dependency-calls.rst new file mode 100644 index 0000000..b893a2f --- /dev/null +++ b/Help/release/dev/export-find_dependency-calls.rst @@ -0,0 +1,14 @@ +export-find_dependency-calls +---------------------------- + +* :command:`install(EXPORT)` and :command:`export(EXPORT)` learned a new + ``EXPORT_PACKAGE_DEPENDENCIES`` argument, which can be used to generate + :command:`find_dependency` calls based on what targets the exported targets + depend on. +* A new :command:`export(SETUP)` signature was created to configure export + sets. This can be used to configure how :command:`find_dependency` calls are + exported. +* A new :prop_tgt:`EXPORT_FIND_PACKAGE_NAME` target property was created to + allow targets to specify what package name to pass when exporting + :command:`find_dependency` calls. This property is initialized with a new + :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME` variable. diff --git a/Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst b/Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst new file mode 100644 index 0000000..b6c8b76 --- /dev/null +++ b/Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst @@ -0,0 +1,6 @@ +CMAKE_EXPORT_FIND_PACKAGE_NAME +------------------------------ + +.. versionadded:: 3.29 + +Initializes the value of :prop_tgt:`EXPORT_FIND_PACKAGE_NAME`. diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 1bc855e..13eade3 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -558,6 +558,8 @@ add_library( cmFindLibraryCommand.h cmFindPackageCommand.cxx cmFindPackageCommand.h + cmFindPackageStack.cxx + cmFindPackageStack.h cmFindPathCommand.cxx cmFindPathCommand.h cmFindProgramCommand.cxx diff --git a/Source/cmExportBuildAndroidMKGenerator.h b/Source/cmExportBuildAndroidMKGenerator.h index 7067488..8918ce1 100644 --- a/Source/cmExportBuildAndroidMKGenerator.h +++ b/Source/cmExportBuildAndroidMKGenerator.h @@ -55,6 +55,7 @@ protected: cmGeneratorTarget const* target, ImportPropertyMap const& properties) override; void GenerateMissingTargetsCheckCode(std::ostream& os) override; + void GenerateFindDependencyCalls(std::ostream&) override {} void GenerateInterfaceProperties( cmGeneratorTarget const* target, std::ostream& os, const ImportPropertyMap& properties) override; diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h index 4636196..c5bb0df 100644 --- a/Source/cmExportBuildFileGenerator.h +++ b/Source/cmExportBuildFileGenerator.h @@ -90,6 +90,7 @@ protected: cmTargetExport* te) override; std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) override; + cmExportSet* GetExportSet() const override { return this->ExportSet; } std::string GetCxxModulesDirectory() const override; void GenerateCxxModuleConfigInformation(std::ostream&) const override; diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index e78b869..cf1f4ad 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -8,6 +8,7 @@ #include #include +#include #include #include "cmsys/RegularExpression.hxx" @@ -24,10 +25,12 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmValue.h" #if defined(__HAIKU__) # include @@ -66,6 +69,9 @@ bool cmExportCommand(std::vector const& args, std::string CxxModulesDirectory; bool Append = false; bool ExportOld = false; + + std::vector> PackageDependencyArgs; + bool ExportPackageDependencies = false; }; auto parser = @@ -75,7 +81,12 @@ bool cmExportCommand(std::vector const& args, .Bind("CXX_MODULES_DIRECTORY"_s, &Arguments::CxxModulesDirectory); if (args[0] == "EXPORT") { - parser.Bind("EXPORT"_s, &Arguments::ExportSetName); + parser.Bind("EXPORT"_s, &Arguments::ExportSetName) + .Bind("EXPORT_PACKAGE_DEPENDENCIES"_s, + &Arguments::ExportPackageDependencies); + } else if (args[0] == "SETUP") { + parser.Bind("SETUP"_s, &Arguments::ExportSetName); + parser.Bind("PACKAGE_DEPENDENCY"_s, &Arguments::PackageDependencyArgs); } else { parser.Bind("TARGETS"_s, &Arguments::Targets); parser.Bind("ANDROID_MK"_s, &Arguments::AndroidMKFile); @@ -91,6 +102,66 @@ bool cmExportCommand(std::vector const& args, return false; } + if (args[0] == "SETUP") { + cmMakefile& mf = status.GetMakefile(); + cmGlobalGenerator* gg = mf.GetGlobalGenerator(); + + cmExportSetMap& setMap = gg->GetExportSets(); + auto& exportSet = setMap[arguments.ExportSetName]; + + struct PackageDependencyArguments + { + std::string Enabled; + ArgumentParser::MaybeEmpty> ExtraArgs; + }; + + auto packageDependencyParser = + cmArgumentParser{} + .Bind("ENABLED"_s, &PackageDependencyArguments::Enabled) + .Bind("EXTRA_ARGS"_s, &PackageDependencyArguments::ExtraArgs); + + for (auto const& packageDependencyArgs : arguments.PackageDependencyArgs) { + if (packageDependencyArgs.empty()) { + continue; + } + + PackageDependencyArguments const packageDependencyArguments = + packageDependencyParser.Parse( + cmMakeRange(packageDependencyArgs).advance(1), &unknownArgs); + + if (!unknownArgs.empty()) { + status.SetError("Unknown argument: \"" + unknownArgs.front() + "\"."); + return false; + } + + auto& packageDependency = + exportSet.GetPackageDependencyForSetup(packageDependencyArgs.front()); + + if (!packageDependencyArguments.Enabled.empty()) { + if (packageDependencyArguments.Enabled == "AUTO") { + packageDependency.Enabled = + cmExportSet::PackageDependencyExportEnabled::Auto; + } else if (cmIsOff(packageDependencyArguments.Enabled)) { + packageDependency.Enabled = + cmExportSet::PackageDependencyExportEnabled::Off; + } else if (cmIsOn(packageDependencyArguments.Enabled)) { + packageDependency.Enabled = + cmExportSet::PackageDependencyExportEnabled::On; + } else { + status.SetError( + cmStrCat("Invalid enable setting for package dependency: \"", + packageDependencyArguments.Enabled, "\"")); + return false; + } + } + + cm::append(packageDependency.ExtraArguments, + packageDependencyArguments.ExtraArgs); + } + + return true; + } + std::string fname; bool android = false; if (!arguments.AndroidMKFile.empty()) { @@ -224,6 +295,7 @@ bool cmExportCommand(std::vector const& args, ebfg->SetTargets(targets); } ebfg->SetExportOld(arguments.ExportOld); + ebfg->SetExportPackageDependencies(arguments.ExportPackageDependencies); // Compute the set of configurations exported. std::vector configurationTypes = diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 7bf9e5d..205bf08 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExportFileGenerator.h" +#include #include #include #include @@ -9,12 +10,15 @@ #include #include +#include #include #include "cmsys/FStream.hxx" #include "cmComputeLinkInformation.h" +#include "cmExportSet.h" #include "cmFileSet.h" +#include "cmFindPackageStack.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmLinkItem.h" @@ -100,7 +104,20 @@ bool cmExportFileGenerator::GenerateImportFile() this->GenerateImportHeaderCode(mainFileWithHeadersAndFootersBuffer); // Create all the imported targets. - bool result = this->GenerateMainFile(mainFileWithHeadersAndFootersBuffer); + std::stringstream mainFileBuffer; + bool result = this->GenerateMainFile(mainFileBuffer); + + // Export find_dependency() calls. Must be done after GenerateMainFile(), + // because that's when target dependencies are gathered, which we need for + // the find_dependency() calls. + if (!this->AppendMode && this->GetExportSet() && + this->ExportPackageDependencies) { + this->SetRequiredCMakeVersion(3, 9, 0); + this->GenerateFindDependencyCalls(mainFileWithHeadersAndFootersBuffer); + } + + // Write cached import code. + mainFileWithHeadersAndFootersBuffer << mainFileBuffer.rdbuf(); // End with the import file footer. this->GenerateImportFooterCode(mainFileWithHeadersAndFootersBuffer); @@ -615,6 +632,12 @@ bool cmExportFileGenerator::AddTargetNamespace(std::string& input, return false; } + cmFindPackageStack const& pkgStack = tgt->Target->GetFindPackageStack(); + if (!pkgStack.Empty() || + tgt->Target->GetProperty("EXPORT_FIND_PACKAGE_NAME")) { + this->ExternalTargets.emplace(tgt); + } + if (tgt->IsImported()) { input = tgt->GetName(); return true; @@ -862,12 +885,14 @@ void cmExportFileGenerator::SetImportDetailProperties( // Export IMPORTED_LINK_DEPENDENT_LIBRARIES to help consuming linkers // find private dependencies of shared libraries. std::size_t oldMissingTargetsSize = this->MissingTargets.size(); + auto oldExternalTargets = this->ExternalTargets; this->SetImportLinkProperty( suffix, target, "IMPORTED_LINK_DEPENDENT_LIBRARIES", iface->SharedDeps, properties, ImportLinkPropertyTargetNames::Yes); // Avoid enforcing shared library private dependencies as public package // dependencies by ignoring missing targets added for them. this->MissingTargets.resize(oldMissingTargetsSize); + this->ExternalTargets = std::move(oldExternalTargets); if (iface->Multiplicity > 0) { std::string prop = @@ -1157,6 +1182,73 @@ void cmExportFileGenerator::GenerateImportPropertyCode( << "\n"; } +void cmExportFileGenerator::GenerateFindDependencyCalls(std::ostream& os) +{ + os << "include(CMakeFindDependencyMacro)\n"; + std::map packageDependencies; + auto* exportSet = this->GetExportSet(); + if (exportSet) { + packageDependencies = exportSet->GetPackageDependencies(); + } + + for (cmGeneratorTarget const* gt : this->ExternalTargets) { + std::string findPackageName; + auto exportFindPackageName = gt->GetProperty("EXPORT_FIND_PACKAGE_NAME"); + cmFindPackageStack pkgStack = gt->Target->GetFindPackageStack(); + if (!exportFindPackageName.IsEmpty()) { + findPackageName = *exportFindPackageName; + } else { + if (!pkgStack.Empty()) { + cmFindPackageCall const& fpc = pkgStack.Top(); + findPackageName = fpc.Name; + } + } + if (!findPackageName.empty()) { + auto& dep = packageDependencies[findPackageName]; + if (!pkgStack.Empty()) { + dep.FindPackageIndex = pkgStack.Top().Index; + } + if (dep.Enabled == cmExportSet::PackageDependencyExportEnabled::Auto) { + dep.Enabled = cmExportSet::PackageDependencyExportEnabled::On; + } + } + } + + std::vector> + packageDependenciesSorted(packageDependencies.begin(), + packageDependencies.end()); + std::sort( + packageDependenciesSorted.begin(), packageDependenciesSorted.end(), + [](const std::pair& lhs, + const std::pair& rhs) + -> bool { + if (lhs.second.SpecifiedIndex) { + if (rhs.second.SpecifiedIndex) { + return lhs.second.SpecifiedIndex < rhs.second.SpecifiedIndex; + } + assert(rhs.second.FindPackageIndex); + return true; + } + assert(lhs.second.FindPackageIndex); + if (rhs.second.SpecifiedIndex) { + return false; + } + assert(rhs.second.FindPackageIndex); + return lhs.second.FindPackageIndex < rhs.second.FindPackageIndex; + }); + + for (auto const& it : packageDependenciesSorted) { + if (it.second.Enabled == cmExportSet::PackageDependencyExportEnabled::On) { + os << "find_dependency(" << it.first << " REQUIRED"; + for (auto const& arg : it.second.ExtraArguments) { + os << " " << cmOutputConverter::EscapeForCMake(arg); + } + os << ")\n"; + } + } + os << "\n\n"; +} + void cmExportFileGenerator::GenerateMissingTargetsCheckCode(std::ostream& os) { if (this->MissingTargets.empty()) { diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index fd7ec99..ccc10d8 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -15,6 +15,7 @@ #include "cmVersion.h" #include "cmVersionConfig.h" +class cmExportSet; class cmFileSet; class cmGeneratorTarget; class cmLocalGenerator; @@ -61,6 +62,11 @@ public: error. */ bool GenerateImportFile(); + void SetExportPackageDependencies(bool exportPackageDependencies) + { + this->ExportPackageDependencies = exportPackageDependencies; + } + protected: using ImportPropertyMap = std::map; @@ -88,6 +94,7 @@ protected: const std::set& importedLocations); virtual void GenerateImportedFileCheckLoop(std::ostream& os); virtual void GenerateMissingTargetsCheckCode(std::ostream& os); + virtual void GenerateFindDependencyCalls(std::ostream& os); virtual void GenerateExpectedTargetsCode(std::ostream& os, const std::string& expectedTargets); @@ -193,6 +200,8 @@ protected: cmFileSet* fileSet, cmTargetExport* te) = 0; + virtual cmExportSet* GetExportSet() const { return nullptr; } + void SetRequiredCMakeVersion(unsigned int major, unsigned int minor, unsigned int patch); @@ -216,10 +225,14 @@ protected: std::vector MissingTargets; + std::set ExternalTargets; + unsigned int RequiredCMakeVersionMajor = 2; unsigned int RequiredCMakeVersionMinor = 8; unsigned int RequiredCMakeVersionPatch = 3; + bool ExportPackageDependencies = false; + private: void PopulateInterfaceProperty(const std::string&, const std::string&, cmGeneratorTarget const* target, diff --git a/Source/cmExportInstallAndroidMKGenerator.h b/Source/cmExportInstallAndroidMKGenerator.h index 1922207..9454cdf 100644 --- a/Source/cmExportInstallAndroidMKGenerator.h +++ b/Source/cmExportInstallAndroidMKGenerator.h @@ -49,6 +49,7 @@ protected: cmGeneratorTarget const* target, ImportPropertyMap const& properties) override; void GenerateMissingTargetsCheckCode(std::ostream& os) override; + void GenerateFindDependencyCalls(std::ostream&) override {} void GenerateInterfaceProperties( cmGeneratorTarget const* target, std::ostream& os, const ImportPropertyMap& properties) override; diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h index e073a31..9de0c8e 100644 --- a/Source/cmExportInstallFileGenerator.h +++ b/Source/cmExportInstallFileGenerator.h @@ -12,12 +12,13 @@ #include #include "cmExportFileGenerator.h" +#include "cmInstallExportGenerator.h" #include "cmStateTypes.h" +class cmExportSet; class cmFileSet; class cmGeneratorTarget; class cmGlobalGenerator; -class cmInstallExportGenerator; class cmInstallTargetGenerator; class cmTargetExport; @@ -121,6 +122,11 @@ protected: void GenerateCxxModuleConfigInformation(std::ostream&) const override; bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&); + cmExportSet* GetExportSet() const override + { + return this->IEGen->GetExportSet(); + } + cmInstallExportGenerator* IEGen; // The import file generated for each configuration. diff --git a/Source/cmExportSet.cxx b/Source/cmExportSet.cxx index c890168..aa32394 100644 --- a/Source/cmExportSet.cxx +++ b/Source/cmExportSet.cxx @@ -20,6 +20,17 @@ cmExportSet::cmExportSet(std::string name) cmExportSet::~cmExportSet() = default; +cmExportSet::PackageDependency& cmExportSet::GetPackageDependencyForSetup( + const std::string& name) +{ + auto& dep = this->PackageDependencies[name]; + if (!dep.SpecifiedIndex) { + dep.SpecifiedIndex = this->NextPackageDependencyIndex; + this->NextPackageDependencyIndex++; + } + return dep; +} + bool cmExportSet::Compute(cmLocalGenerator* lg) { for (std::unique_ptr& tgtExport : this->TargetExports) { diff --git a/Source/cmExportSet.h b/Source/cmExportSet.h index b75a26d..c5fc91f 100644 --- a/Source/cmExportSet.h +++ b/Source/cmExportSet.h @@ -9,6 +9,8 @@ #include #include +#include + class cmInstallExportGenerator; class cmLocalGenerator; class cmTargetExport; @@ -43,10 +45,36 @@ public: return &this->Installations; } + enum class PackageDependencyExportEnabled + { + Auto, + Off, + On, + }; + + struct PackageDependency + { + PackageDependencyExportEnabled Enabled = + PackageDependencyExportEnabled::Auto; + std::vector ExtraArguments; + cm::optional SpecifiedIndex; + cm::optional FindPackageIndex; + }; + + PackageDependency& GetPackageDependencyForSetup(const std::string& name); + + const std::map& GetPackageDependencies() + const + { + return this->PackageDependencies; + } + private: std::vector> TargetExports; std::string Name; std::vector Installations; + std::map PackageDependencies; + unsigned int NextPackageDependencyIndex = 0; }; /// A name -> cmExportSet map with overloaded operator[]. diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 30458cd..9b51b1a 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -1044,6 +1044,8 @@ bool cmFindPackageCommand::InitialPass(std::vector const& args) PushPopRootPathStack pushPopRootPathStack(*this); SetRestoreFindDefinitions setRestoreFindDefinitions(*this, components, componentVarDefs); + cmMakefile::FindPackageStackRAII findPackageStackRAII(this->Makefile, + this->Name); // See if we have been told to delegate to FetchContent or some other // redirected config package first. We have to check all names that diff --git a/Source/cmFindPackageStack.cxx b/Source/cmFindPackageStack.cxx new file mode 100644 index 0000000..1aeb2a7 --- /dev/null +++ b/Source/cmFindPackageStack.cxx @@ -0,0 +1,7 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#define cmFindPackageStack_cxx +#include "cmFindPackageStack.h" + +#include "cmConstStack.tcc" // IWYU pragma: keep +template class cmConstStack; diff --git a/Source/cmFindPackageStack.h b/Source/cmFindPackageStack.h new file mode 100644 index 0000000..2062fbc --- /dev/null +++ b/Source/cmFindPackageStack.h @@ -0,0 +1,33 @@ +/* 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 "cmConstStack.h" + +/** + * Represents one call to find_package. + */ +class cmFindPackageCall +{ +public: + std::string Name; + unsigned int Index; +}; + +/** + * Represents a stack of find_package calls with efficient value semantics. + */ +class cmFindPackageStack + : public cmConstStack +{ + using cmConstStack::cmConstStack; + friend class cmConstStack; +}; +#ifndef cmFindPackageStack_cxx +extern template class cmConstStack; +#endif diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 0fc4011..e2755da 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -2030,7 +2030,7 @@ bool HandleExportAndroidMKMode(std::vector const& args, cm::make_unique( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true, + ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true, false, helper.Makefile->GetBacktrace())); return true; @@ -2054,12 +2054,14 @@ bool HandleExportMode(std::vector const& args, bool exportOld = false; std::string filename; std::string cxx_modules_directory; + bool exportPackageDependencies = false; ica.Bind("EXPORT"_s, exp); ica.Bind("NAMESPACE"_s, name_space); ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld); ica.Bind("FILE"_s, filename); ica.Bind("CXX_MODULES_DIRECTORY"_s, cxx_modules_directory); + ica.Bind("EXPORT_PACKAGE_DEPENDENCIES"_s, exportPackageDependencies); std::vector unknownArgs; ica.Parse(args, &unknownArgs); @@ -2147,7 +2149,8 @@ bool HandleExportMode(std::vector const& args, &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, ica.GetExcludeFromAll(), fname, name_space, cxx_modules_directory, - exportOld, false, helper.Makefile->GetBacktrace())); + exportOld, false, exportPackageDependencies, + helper.Makefile->GetBacktrace())); return true; } diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx index 71d5471..72f0ac7 100644 --- a/Source/cmInstallExportGenerator.cxx +++ b/Source/cmInstallExportGenerator.cxx @@ -27,7 +27,7 @@ cmInstallExportGenerator::cmInstallExportGenerator( std::string const& component, MessageLevel message, bool exclude_from_all, std::string filename, std::string name_space, std::string cxx_modules_directory, bool exportOld, bool android, - cmListFileBacktrace backtrace) + bool exportPackageDependencies, cmListFileBacktrace backtrace) : cmInstallGenerator(destination, configurations, component, message, exclude_from_all, false, std::move(backtrace)) , ExportSet(exportSet) @@ -36,6 +36,7 @@ cmInstallExportGenerator::cmInstallExportGenerator( , Namespace(std::move(name_space)) , CxxModulesDirectory(std::move(cxx_modules_directory)) , ExportOld(exportOld) + , ExportPackageDependencies(exportPackageDependencies) { if (android) { #ifndef CMAKE_BOOTSTRAP @@ -119,6 +120,7 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os) this->EFGen->AddConfiguration(c); } } + this->EFGen->SetExportPackageDependencies(this->ExportPackageDependencies); this->EFGen->GenerateImportFile(); // Perform the main install script generation. diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h index f2d4a05..5f92851 100644 --- a/Source/cmInstallExportGenerator.h +++ b/Source/cmInstallExportGenerator.h @@ -29,7 +29,8 @@ public: bool exclude_from_all, std::string filename, std::string name_space, std::string cxx_modules_directory, bool exportOld, - bool android, cmListFileBacktrace backtrace); + bool android, bool exportPackageDependencies, + cmListFileBacktrace backtrace); cmInstallExportGenerator(const cmInstallExportGenerator&) = delete; ~cmInstallExportGenerator() override; @@ -70,6 +71,7 @@ protected: std::string const Namespace; std::string const CxxModulesDirectory; bool const ExportOld; + bool const ExportPackageDependencies; cmLocalGenerator* LocalGenerator = nullptr; std::string TempDir; diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 9ed422d..e055a34 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -302,6 +302,11 @@ cmListFileBacktrace cmMakefile::GetBacktrace() const return this->Backtrace; } +cmFindPackageStack cmMakefile::GetFindPackageStack() const +{ + return this->FindPackageStack; +} + void cmMakefile::PrintCommandTrace(cmListFileFunction const& lff, cmListFileBacktrace const& bt, CommandMissingFromStack missing) const @@ -4771,6 +4776,36 @@ cmMakefile::MacroPushPop::~MacroPushPop() this->Makefile->PopMacroScope(this->ReportError); } +cmMakefile::FindPackageStackRAII::FindPackageStackRAII(cmMakefile* mf, + std::string const& name) + : Makefile(mf) +{ + this->Makefile->FindPackageStack = + this->Makefile->FindPackageStack.Push(cmFindPackageCall{ + name, + this->Makefile->FindPackageStackNextIndex, + }); + this->Makefile->FindPackageStackNextIndex++; +} + +cmMakefile::FindPackageStackRAII::~FindPackageStackRAII() +{ + this->Makefile->FindPackageStackNextIndex = + this->Makefile->FindPackageStack.Top().Index + 1; + this->Makefile->FindPackageStack = this->Makefile->FindPackageStack.Pop(); + + if (!this->Makefile->FindPackageStack.Empty()) { + auto top = this->Makefile->FindPackageStack.Top(); + this->Makefile->FindPackageStack = this->Makefile->FindPackageStack.Pop(); + + top.Index = this->Makefile->FindPackageStackNextIndex; + this->Makefile->FindPackageStackNextIndex++; + + this->Makefile->FindPackageStack = + this->Makefile->FindPackageStack.Push(top); + } +} + cmMakefile::DebugFindPkgRAII::DebugFindPkgRAII(cmMakefile* mf, std::string const& pkg) : Makefile(mf) diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 24daa72..e5edbae 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -25,6 +25,7 @@ #include "cmAlgorithms.h" #include "cmCustomCommand.h" +#include "cmFindPackageStack.h" #include "cmFunctionBlocker.h" #include "cmListFileCache.h" #include "cmMessageType.h" // IWYU pragma: keep @@ -660,6 +661,11 @@ public: cmListFileBacktrace GetBacktrace() const; /** + * Get the current stack of find_package calls. + */ + cmFindPackageStack GetFindPackageStack() const; + + /** * Get the vector of files created by this makefile */ const std::vector& GetOutputFiles() const @@ -1020,6 +1026,15 @@ public: // searches std::deque> FindPackageRootPathStack; + class FindPackageStackRAII + { + cmMakefile* Makefile; + + public: + FindPackageStackRAII(cmMakefile* mf, std::string const& pkg); + ~FindPackageStackRAII(); + }; + class DebugFindPkgRAII { cmMakefile* Makefile; @@ -1210,6 +1225,9 @@ private: std::vector> GeneratorActions; bool GeneratorActionsInvoked = false; + cmFindPackageStack FindPackageStack; + unsigned int FindPackageStackNextIndex = 0; + bool DebugFindPkg = false; bool CheckSystemVars; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index d93f658..e373319 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -20,6 +20,7 @@ #include "cmAlgorithms.h" #include "cmCustomCommand.h" #include "cmFileSet.h" +#include "cmFindPackageStack.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -585,6 +586,7 @@ TargetProperty const StaticTargetProperties[] = { // Usage requirement properties { "LINK_INTERFACE_LIBRARIES"_s, IC::CanCompileSources }, { "MAP_IMPORTED_CONFIG_"_s, IC::NormalTarget, R::PerConfig }, + { "EXPORT_FIND_PACKAGE_NAME"_s, IC::NormalTarget }, // Metadata { "CROSSCOMPILING_EMULATOR"_s, IC::ExecutableTarget }, @@ -661,6 +663,7 @@ public: TLLCommands; std::map FileSets; cmListFileBacktrace Backtrace; + cmFindPackageStack FindPackageStack; UsageRequirementProperty IncludeDirectories; UsageRequirementProperty CompileOptions; @@ -961,6 +964,9 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, // Save the backtrace of target construction. this->impl->Backtrace = this->impl->Makefile->GetBacktrace(); + if (this->impl->IsImported()) { + this->impl->FindPackageStack = this->impl->Makefile->GetFindPackageStack(); + } if (this->IsNormal()) { // Initialize the INCLUDE_DIRECTORIES property based on the current value @@ -1248,6 +1254,11 @@ cmListFileBacktrace const& cmTarget::GetBacktrace() const return this->impl->Backtrace; } +cmFindPackageStack const& cmTarget::GetFindPackageStack() const +{ + return this->impl->FindPackageStack; +} + bool cmTarget::IsExecutableWithExports() const { return (this->GetType() == cmStateEnums::EXECUTABLE && diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 584856a..385dfe7 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -24,6 +24,7 @@ class cmCustomCommand; class cmFileSet; +class cmFindPackageStack; class cmGlobalGenerator; class cmInstallTargetGenerator; class cmMakefile; @@ -239,6 +240,9 @@ public: //! Get a backtrace from the creation of the target. cmListFileBacktrace const& GetBacktrace() const; + //! Get a find_package call stack from the creation of the target. + cmFindPackageStack const& GetFindPackageStack() const; + void InsertInclude(BT const& entry, bool before = false); void InsertCompileOption(BT const& entry, bool before = false); void InsertCompileDefinition(BT const& entry); diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index 67f2fcb..56b8aaa 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -4,6 +4,8 @@ if(POLICY CMP0129) endif() project(Export C CXX) +find_package(Foo REQUIRED CONFIG NO_DEFAULT_PATH) + # Pretend that RelWithDebInfo should link to debug libraries to test # the DEBUG_CONFIGURATIONS property. set_property(GLOBAL PROPERTY DEBUG_CONFIGURATIONS Debug RelWithDebInfo) @@ -110,6 +112,7 @@ add_library(testLib9ObjIface OBJECT testLib9ObjIface.c) target_compile_definitions(testLib9ObjIface INTERFACE testLib9ObjIface_USED) add_library(testLib9 STATIC testLib9.c) target_link_libraries(testLib9 INTERFACE testLib9ObjIface PUBLIC testLib9ObjPub PRIVATE testLib9ObjPriv) +target_link_libraries(testLib9 PUBLIC Foo::Foo) cmake_policy(POP) # Test using the target_link_libraries command to set the @@ -624,7 +627,7 @@ install( LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) -install(EXPORT exp NAMESPACE exp_ DESTINATION lib/exp) +install(EXPORT exp NAMESPACE exp_ DESTINATION lib/exp EXPORT_PACKAGE_DEPENDENCIES) # Install testLib5.dll outside the export. if(WIN32) diff --git a/Tests/ExportImport/External/FooConfig.cmake b/Tests/ExportImport/External/FooConfig.cmake new file mode 100644 index 0000000..48b6289 --- /dev/null +++ b/Tests/ExportImport/External/FooConfig.cmake @@ -0,0 +1,3 @@ +if(NOT TARGET Foo::Foo) + add_library(Foo::Foo INTERFACE IMPORTED) +endif() diff --git a/Tests/ExportImport/Import/CMakeLists.txt b/Tests/ExportImport/Import/CMakeLists.txt index e6dcd65..83c87a8 100644 --- a/Tests/ExportImport/Import/CMakeLists.txt +++ b/Tests/ExportImport/Import/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required (VERSION 2.7.20090711) cmake_policy(SET CMP0025 NEW) +cmake_policy(SET CMP0028 NEW) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() diff --git a/Tests/ExportImport/InitialCache.cmake.in b/Tests/ExportImport/InitialCache.cmake.in index 44cd179..55aef86 100644 --- a/Tests/ExportImport/InitialCache.cmake.in +++ b/Tests/ExportImport/InitialCache.cmake.in @@ -15,3 +15,4 @@ set(CMAKE_INSTALL_PREFIX "@ExportImport_BINARY_DIR@/Root" CACHE STRING "Installa set(CMAKE_SKIP_RPATH ON CACHE BOOL "No RPATH") set(CMAKE_GNUtoMS "@ExportImport_GNUtoMS@" CACHE BOOL "CMAKE_GNUtoMS") set(CMake_TEST_CUDA "@CMake_TEST_CUDA@" CACHE BOOL "CMake_TEST_CUDA") +set(Foo_DIR "@CMAKE_CURRENT_SOURCE_DIR@/External" CACHE PATH "Foo cmake package directory") diff --git a/Tests/RunCMake/export/CMake/FindHasDeps.cmake b/Tests/RunCMake/export/CMake/FindHasDeps.cmake new file mode 100644 index 0000000..86b2abe --- /dev/null +++ b/Tests/RunCMake/export/CMake/FindHasDeps.cmake @@ -0,0 +1,17 @@ +find_package(Threads REQUIRED) +find_package(P4 REQUIRED) + +add_library(HasDeps::interface IMPORTED INTERFACE) +target_link_libraries(HasDeps::interface INTERFACE Threads::Threads l4) + +add_library(HasDeps::A IMPORTED UNKNOWN) +target_link_libraries(HasDeps::A INTERFACE HasDeps::interface) +file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/a.so") +set_property(TARGET HasDeps::A PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/a.so") + +add_library(HasDeps::B IMPORTED UNKNOWN) +target_link_libraries(HasDeps::B INTERFACE HasDeps::interface) +file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/b.so") +set_property(TARGET HasDeps::B PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/b.so") + +set(HASDEPS_FOUND TRUE) diff --git a/Tests/RunCMake/export/CMake/FindP1.cmake b/Tests/RunCMake/export/CMake/FindP1.cmake new file mode 100644 index 0000000..e33c3d6 --- /dev/null +++ b/Tests/RunCMake/export/CMake/FindP1.cmake @@ -0,0 +1,2 @@ +add_library(l1 IMPORTED INTERFACE) +set(P1_FOUND TRUE) diff --git a/Tests/RunCMake/export/CMake/FindP2.cmake b/Tests/RunCMake/export/CMake/FindP2.cmake new file mode 100644 index 0000000..6a360ce --- /dev/null +++ b/Tests/RunCMake/export/CMake/FindP2.cmake @@ -0,0 +1,2 @@ +add_library(l2 IMPORTED INTERFACE) +set(P2_FOUND TRUE) diff --git a/Tests/RunCMake/export/CMake/FindP3.cmake b/Tests/RunCMake/export/CMake/FindP3.cmake new file mode 100644 index 0000000..72bd829 --- /dev/null +++ b/Tests/RunCMake/export/CMake/FindP3.cmake @@ -0,0 +1,2 @@ +add_library(l3 IMPORTED INTERFACE) +set(P3_FOUND TRUE) diff --git a/Tests/RunCMake/export/CMake/FindP4.cmake b/Tests/RunCMake/export/CMake/FindP4.cmake new file mode 100644 index 0000000..b62a040 --- /dev/null +++ b/Tests/RunCMake/export/CMake/FindP4.cmake @@ -0,0 +1,2 @@ +add_library(l4 IMPORTED INTERFACE) +set(P4_FOUND TRUE) diff --git a/Tests/RunCMake/export/CMake/FindP9.cmake b/Tests/RunCMake/export/CMake/FindP9.cmake new file mode 100644 index 0000000..201b86a --- /dev/null +++ b/Tests/RunCMake/export/CMake/FindP9.cmake @@ -0,0 +1,2 @@ +add_library(l9 IMPORTED INTERFACE) +set(P9_FOUND TRUE) diff --git a/Tests/RunCMake/export/FindDependencyExport-check.cmake b/Tests/RunCMake/export/FindDependencyExport-check.cmake new file mode 100644 index 0000000..0cbb195 --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExport-check.cmake @@ -0,0 +1,35 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/mytargets.cmake" mytargets) +if("${mytargets}" MATCHES "find_dependency\\(P1") + string(APPEND RunCMake_TEST_FAILED "P1 dependency should not be exported but it is\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P2 REQUIRED \"VERSION\" \"1\\.0\"\\)") + string(APPEND RunCMake_TEST_FAILED "P2 dependency should be exported but it is not\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P3 REQUIRED\\)") + string(APPEND RunCMake_TEST_FAILED "P3 dependency should be exported but it is not\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P4 REQUIRED\\)") + string(APPEND RunCMake_TEST_FAILED "P4 dependency should be exported but it is not\n") +endif() +if("${mytargets}" MATCHES "find_dependency\\(P5") + string(APPEND RunCMake_TEST_FAILED "P5 dependency should not be exported but it is\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P6 REQUIRED\\)") + string(APPEND RunCMake_TEST_FAILED "P6 dependency should be exported but it is not\n") +endif() +if("${mytargets}" MATCHES "find_dependency\\(P7") + string(APPEND RunCMake_TEST_FAILED "P7 dependency should not be exported but it is\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P3[^ +]*\\) +find_dependency\\(P2[^ +]*\\) +find_dependency\\(P8[^ +]*\\) +find_dependency\\(P6[^ +]*\\) +find_dependency\\(P9[^ +]*\\) +find_dependency\\(P4") + string(APPEND RunCMake_TEST_FAILED "Dependencies are not in the correct order\n") +endif() diff --git a/Tests/RunCMake/export/FindDependencyExport.cmake b/Tests/RunCMake/export/FindDependencyExport.cmake new file mode 100644 index 0000000..59dd4cc --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExport.cmake @@ -0,0 +1,30 @@ +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake) + +find_package(P1) +find_package(P2) +find_package(P9) +find_package(P4) +find_package(P3) + +add_library(mylib INTERFACE) +target_link_libraries(mylib INTERFACE l1 l2 l3 l4 l9) + +install(TARGETS mylib EXPORT mytargets) +export(SETUP mytargets + PACKAGE_DEPENDENCY P1 + ENABLED OFF + PACKAGE_DEPENDENCY P3 + ENABLED AUTO + PACKAGE_DEPENDENCY P2 + ENABLED ON + EXTRA_ARGS VERSION 1.0 + PACKAGE_DEPENDENCY P5 + ENABLED FALSE + PACKAGE_DEPENDENCY P8 + ENABLED TRUE + PACKAGE_DEPENDENCY P6 + ENABLED 1 + PACKAGE_DEPENDENCY P7 + ENABLED AUTO + ) +export(EXPORT mytargets EXPORT_PACKAGE_DEPENDENCIES FILE mytargets.cmake) diff --git a/Tests/RunCMake/export/FindDependencyExportFetchContent-check.cmake b/Tests/RunCMake/export/FindDependencyExportFetchContent-check.cmake new file mode 100644 index 0000000..353bb08 --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExportFetchContent-check.cmake @@ -0,0 +1,14 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/my_private_targets.cmake" my_private_targets) +if(NOT "${my_private_targets}" MATCHES "find_dependency\\(HasDeps") + string(APPEND RunCMake_TEST_FAILED "HasDeps dependency should be exported but it is not\n") +endif() + +file(READ "${RunCMake_TEST_BINARY_DIR}/my_static_targets.cmake" my_static_targets) +if(NOT "${my_static_targets}" MATCHES "find_dependency\\(MyPrivate") + string(APPEND RunCMake_TEST_FAILED "HasDeps dependency should be exported but it is not\n") +endif() + +file(READ "${RunCMake_TEST_BINARY_DIR}/my_shared_targets.cmake" my_shared_targets) +if(NOT "${my_shared_targets}" MATCHES "find_dependency\\(MyPrivate") + string(APPEND RunCMake_TEST_FAILED "MyStatic dependency should be exported but it is not\n") +endif() diff --git a/Tests/RunCMake/export/FindDependencyExportFetchContent.cmake b/Tests/RunCMake/export/FindDependencyExportFetchContent.cmake new file mode 100644 index 0000000..9e6c565 --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExportFetchContent.cmake @@ -0,0 +1,37 @@ +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake) + +enable_language(CXX) + +find_package(HasDeps) + +# replicates FetchContent where a dependency is brought +# in via source. In these cases we need to extend the `install` +# `export` commands to allow markup on what `Find` will +# map to the export set +add_library(my_private_lib STATIC empty.cpp) +target_link_libraries(my_private_lib PUBLIC HasDeps::A) +set_target_properties(my_private_lib PROPERTIES EXPORT_FIND_PACKAGE_NAME "MyPrivate") + +install(TARGETS my_private_lib EXPORT my_private_targets) +install(EXPORT my_private_targets + FILE my_private.cmake + DESTINATION lib) +export(EXPORT my_private_targets EXPORT_PACKAGE_DEPENDENCIES FILE my_private_targets.cmake) + +add_library(my_static_lib STATIC empty.cpp) +target_link_libraries(my_static_lib PRIVATE my_private_lib) + +install(TARGETS my_static_lib EXPORT my_static_targets) +install(EXPORT my_static_targets + FILE my_static.cmake + DESTINATION lib) +export(EXPORT my_static_targets EXPORT_PACKAGE_DEPENDENCIES FILE my_static_targets.cmake) + +add_library(my_shared_lib SHARED empty.cpp) +target_link_libraries(my_shared_lib PUBLIC my_private_lib) + +install(TARGETS my_shared_lib EXPORT my_shared_targets) +install(EXPORT my_shared_targets + FILE my_shared.cmake + DESTINATION lib) +export(EXPORT my_shared_targets EXPORT_PACKAGE_DEPENDENCIES FILE my_shared_targets.cmake) diff --git a/Tests/RunCMake/export/FindDependencyExportShared-check.cmake b/Tests/RunCMake/export/FindDependencyExportShared-check.cmake new file mode 100644 index 0000000..d7a32d1 --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExportShared-check.cmake @@ -0,0 +1,4 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/mytargets.cmake" mytargets) +if("${mytargets}" MATCHES "find_dependency") + string(APPEND RunCMake_TEST_FAILED "No dependencies should not be exported\n") +endif() diff --git a/Tests/RunCMake/export/FindDependencyExportShared.cmake b/Tests/RunCMake/export/FindDependencyExportShared.cmake new file mode 100644 index 0000000..6e662a1 --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExportShared.cmake @@ -0,0 +1,14 @@ +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake) + +enable_language(CXX) + +find_package(P1) +find_package(P2) +find_package(P3) +find_package(P4) + +add_library(mylib SHARED empty.cpp) +target_link_libraries(mylib PRIVATE l1 l2 l3 l4) + +install(TARGETS mylib EXPORT mytargets) +export(EXPORT mytargets EXPORT_PACKAGE_DEPENDENCIES FILE mytargets.cmake) diff --git a/Tests/RunCMake/export/FindDependencyExportStatic-check.cmake b/Tests/RunCMake/export/FindDependencyExportStatic-check.cmake new file mode 100644 index 0000000..b78bccb --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExportStatic-check.cmake @@ -0,0 +1,13 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/mytargets.cmake" mytargets) +if(NOT "${mytargets}" MATCHES "find_dependency\\(P1") + string(APPEND RunCMake_TEST_FAILED "P1 dependency should be exported but it is not\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P2") + string(APPEND RunCMake_TEST_FAILED "P2 dependency should be exported but it is not\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P3") + string(APPEND RunCMake_TEST_FAILED "P3 dependency should be exported but it is not\n") +endif() +if(NOT "${mytargets}" MATCHES "find_dependency\\(P4") + string(APPEND RunCMake_TEST_FAILED "P4 dependency should be exported but it is not\n") +endif() diff --git a/Tests/RunCMake/export/FindDependencyExportStatic.cmake b/Tests/RunCMake/export/FindDependencyExportStatic.cmake new file mode 100644 index 0000000..2d32eb2 --- /dev/null +++ b/Tests/RunCMake/export/FindDependencyExportStatic.cmake @@ -0,0 +1,14 @@ +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake) + +enable_language(CXX) + +find_package(P1) +find_package(P2) +find_package(P3) +find_package(P4) + +add_library(mylib STATIC empty.cpp) +target_link_libraries(mylib PRIVATE l1 l2 l3 l4) + +install(TARGETS mylib EXPORT mytargets) +export(EXPORT mytargets EXPORT_PACKAGE_DEPENDENCIES FILE mytargets.cmake) diff --git a/Tests/RunCMake/export/RunCMakeTest.cmake b/Tests/RunCMake/export/RunCMakeTest.cmake index ee00b27..3e1b7d6 100644 --- a/Tests/RunCMake/export/RunCMakeTest.cmake +++ b/Tests/RunCMake/export/RunCMakeTest.cmake @@ -19,3 +19,7 @@ run_cmake(UnknownExport) run_cmake(NamelinkOnlyExport) run_cmake(SeparateNamelinkExport) run_cmake(TryCompileExport) +run_cmake(FindDependencyExport) +run_cmake(FindDependencyExportStatic) +run_cmake(FindDependencyExportShared) +run_cmake(FindDependencyExportFetchContent) diff --git a/Tests/RunCMake/install/EXPORT-FindDependencyExport-all-check.cmake b/Tests/RunCMake/install/EXPORT-FindDependencyExport-all-check.cmake new file mode 100644 index 0000000..6a8b2e5 --- /dev/null +++ b/Tests/RunCMake/install/EXPORT-FindDependencyExport-all-check.cmake @@ -0,0 +1,4 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/root-all/lib/cmake/mylib/mylib-targets.cmake" contents) +if(NOT contents MATCHES "include\\(CMakeFindDependencyMacro\\)\nfind_dependency\\(P2 REQUIRED\\)\nfind_dependency\\(P1 REQUIRED\\)\n") + set(RunCMake_TEST_FAILED "Dependencies were not properly exported") +endif() diff --git a/Tests/RunCMake/install/EXPORT-FindDependencyExport.cmake b/Tests/RunCMake/install/EXPORT-FindDependencyExport.cmake new file mode 100644 index 0000000..071414b --- /dev/null +++ b/Tests/RunCMake/install/EXPORT-FindDependencyExport.cmake @@ -0,0 +1,18 @@ +enable_language(C) + +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +find_package(P1 REQUIRED) +find_package(P2 REQUIRED) +find_package(P3 REQUIRED) + +add_library(mylib INTERFACE) +target_link_libraries(mylib INTERFACE lib1 lib2 lib3) +install(TARGETS mylib EXPORT mylib-targets) +export(SETUP mylib-targets + PACKAGE_DEPENDENCY P2 + ENABLED AUTO + PACKAGE_DEPENDENCY P3 + ENABLED OFF + ) +install(EXPORT mylib-targets EXPORT_PACKAGE_DEPENDENCIES FILE mylib-targets.cmake DESTINATION lib/cmake/mylib) diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index efafdd1..6f63550 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -176,6 +176,7 @@ run_install_test(TARGETS-Parts) run_install_test(FILES-PERMISSIONS) run_install_test(TARGETS-RPATH) run_install_test(InstallRequiredSystemLibraries) +run_install_test(EXPORT-FindDependencyExport) set(RunCMake_TEST_OPTIONS "-DCMAKE_POLICY_DEFAULT_CMP0087:STRING=NEW") run_install_test(SCRIPT) diff --git a/Tests/RunCMake/install/cmake/FindP1.cmake b/Tests/RunCMake/install/cmake/FindP1.cmake new file mode 100644 index 0000000..c772836 --- /dev/null +++ b/Tests/RunCMake/install/cmake/FindP1.cmake @@ -0,0 +1,2 @@ +add_library(lib1 IMPORTED INTERFACE) +set(P1_FOUND TRUE) diff --git a/Tests/RunCMake/install/cmake/FindP2.cmake b/Tests/RunCMake/install/cmake/FindP2.cmake new file mode 100644 index 0000000..d81b35d --- /dev/null +++ b/Tests/RunCMake/install/cmake/FindP2.cmake @@ -0,0 +1,2 @@ +add_library(lib2 IMPORTED INTERFACE) +set(P2_FOUND TRUE) diff --git a/Tests/RunCMake/install/cmake/FindP3.cmake b/Tests/RunCMake/install/cmake/FindP3.cmake new file mode 100644 index 0000000..2818c3b --- /dev/null +++ b/Tests/RunCMake/install/cmake/FindP3.cmake @@ -0,0 +1,2 @@ +add_library(lib3 IMPORTED INTERFACE) +set(P3_FOUND TRUE) diff --git a/bootstrap b/bootstrap index 115c354..9664567 100755 --- a/bootstrap +++ b/bootstrap @@ -364,6 +364,7 @@ CMAKE_CXX_SOURCES="\ cmFindFileCommand \ cmFindLibraryCommand \ cmFindPackageCommand \ + cmFindPackageStack \ cmFindPathCommand \ cmFindProgramCommand \ cmForEachCommand \ -- cgit v0.12