summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKyle Edwards <kyle.edwards@kitware.com>2023-11-07 16:44:00 (GMT)
committerKyle Edwards <kyle.edwards@kitware.com>2023-11-13 16:07:52 (GMT)
commitc6e6861e631ecfdc96c79f562a66021cb8971fcc (patch)
tree6cb0de03a6db5d0f997f5d351ac2a03bb060bff7
parent2837f592ab7642e6a769949b4ddc1ffb37e21b9c (diff)
downloadCMake-c6e6861e631ecfdc96c79f562a66021cb8971fcc.zip
CMake-c6e6861e631ecfdc96c79f562a66021cb8971fcc.tar.gz
CMake-c6e6861e631ecfdc96c79f562a66021cb8971fcc.tar.bz2
install(EXPORT): Export find_dependency() calls
Issue: #20511 Co-Authored-by: Brad King <brad.king@kitware.com> Co-Authored-by: Robert Maynard <rmaynard@nvidia.com>
-rw-r--r--Help/command/export.rst55
-rw-r--r--Help/command/install.rst31
-rw-r--r--Help/manual/cmake-properties.7.rst1
-rw-r--r--Help/manual/cmake-variables.7.rst1
-rw-r--r--Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst12
-rw-r--r--Help/release/dev/export-find_dependency-calls.rst14
-rw-r--r--Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst6
-rw-r--r--Source/CMakeLists.txt2
-rw-r--r--Source/cmExportBuildAndroidMKGenerator.h1
-rw-r--r--Source/cmExportBuildFileGenerator.h1
-rw-r--r--Source/cmExportCommand.cxx74
-rw-r--r--Source/cmExportFileGenerator.cxx94
-rw-r--r--Source/cmExportFileGenerator.h13
-rw-r--r--Source/cmExportInstallAndroidMKGenerator.h1
-rw-r--r--Source/cmExportInstallFileGenerator.h8
-rw-r--r--Source/cmExportSet.cxx11
-rw-r--r--Source/cmExportSet.h28
-rw-r--r--Source/cmFindPackageCommand.cxx2
-rw-r--r--Source/cmFindPackageStack.cxx7
-rw-r--r--Source/cmFindPackageStack.h33
-rw-r--r--Source/cmInstallCommand.cxx7
-rw-r--r--Source/cmInstallExportGenerator.cxx4
-rw-r--r--Source/cmInstallExportGenerator.h4
-rw-r--r--Source/cmMakefile.cxx35
-rw-r--r--Source/cmMakefile.h18
-rw-r--r--Source/cmTarget.cxx11
-rw-r--r--Source/cmTarget.h4
-rw-r--r--Tests/ExportImport/Export/CMakeLists.txt5
-rw-r--r--Tests/ExportImport/External/FooConfig.cmake3
-rw-r--r--Tests/ExportImport/Import/CMakeLists.txt1
-rw-r--r--Tests/ExportImport/InitialCache.cmake.in1
-rw-r--r--Tests/RunCMake/export/CMake/FindHasDeps.cmake17
-rw-r--r--Tests/RunCMake/export/CMake/FindP1.cmake2
-rw-r--r--Tests/RunCMake/export/CMake/FindP2.cmake2
-rw-r--r--Tests/RunCMake/export/CMake/FindP3.cmake2
-rw-r--r--Tests/RunCMake/export/CMake/FindP4.cmake2
-rw-r--r--Tests/RunCMake/export/CMake/FindP9.cmake2
-rw-r--r--Tests/RunCMake/export/FindDependencyExport-check.cmake35
-rw-r--r--Tests/RunCMake/export/FindDependencyExport.cmake30
-rw-r--r--Tests/RunCMake/export/FindDependencyExportFetchContent-check.cmake14
-rw-r--r--Tests/RunCMake/export/FindDependencyExportFetchContent.cmake37
-rw-r--r--Tests/RunCMake/export/FindDependencyExportShared-check.cmake4
-rw-r--r--Tests/RunCMake/export/FindDependencyExportShared.cmake14
-rw-r--r--Tests/RunCMake/export/FindDependencyExportStatic-check.cmake13
-rw-r--r--Tests/RunCMake/export/FindDependencyExportStatic.cmake14
-rw-r--r--Tests/RunCMake/export/RunCMakeTest.cmake4
-rw-r--r--Tests/RunCMake/install/EXPORT-FindDependencyExport-all-check.cmake4
-rw-r--r--Tests/RunCMake/install/EXPORT-FindDependencyExport.cmake18
-rw-r--r--Tests/RunCMake/install/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/install/cmake/FindP1.cmake2
-rw-r--r--Tests/RunCMake/install/cmake/FindP2.cmake2
-rw-r--r--Tests/RunCMake/install/cmake/FindP3.cmake2
-rwxr-xr-xbootstrap1
53 files changed, 700 insertions, 10 deletions
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`_ <target>... [...])
export(`EXPORT`_ <export-name> [...])
export(`PACKAGE`_ <PackageName>)
+ export(`SETUP`_ <export-name> [...])
Exporting Targets
^^^^^^^^^^^^^^^^^
@@ -108,7 +109,7 @@ Exporting Targets matching install(EXPORT)
.. code-block:: cmake
export(EXPORT <export-name> [NAMESPACE <namespace>] [FILE <filename>]
- [CXX_MODULES_DIRECTORY <directory>])
+ [CXX_MODULES_DIRECTORY <directory>] [EXPORT_PACKAGE_DEPENDENCIES])
Creates a file ``<filename>`` 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 ``<export-name>``. Target installations may be
associated with the export ``<export-name>`` 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 <export-name> [...])
+
+.. code-block:: cmake
+
+ export(SETUP <export-name>
+ [PACKAGE_DEPENDENCY <dep>
+ [ENABLED (<bool-true>|<bool-false>|AUTO)]
+ [EXTRA_ARGS <args>...]
+ ] [...]
+ )
+
+.. versionadded:: 3.29
+
+Configure the parameters of an export. The arguments are as follows:
+
+``PACKAGE_DEPENDENCY <dep>``
+ Specify a package dependency to configure. This changes how
+ :command:`find_dependency` calls are written during
+ :command:`export(EXPORT)` and :command:`install(EXPORT)`. ``<dep>`` 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:
+
+ ``<bool-true>``
+ 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.
+
+ ``<bool-false>``
+ 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 <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 <directory>]
[EXPORT_LINK_INTERFACE_LIBRARIES]
[COMPONENT <component>]
- [EXCLUDE_FROM_ALL])
+ [EXCLUDE_FROM_ALL]
+ [EXPORT_PACKAGE_DEPENDENCIES])
install(EXPORT_ANDROID_MK <export-name> DESTINATION <dir> [...])
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 <cm/memory>
#include <cm/optional>
+#include <cmext/algorithm>
#include <cmext/string_view>
#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 <FindDirectory.h>
@@ -66,6 +69,9 @@ bool cmExportCommand(std::vector<std::string> const& args,
std::string CxxModulesDirectory;
bool Append = false;
bool ExportOld = false;
+
+ std::vector<std::vector<std::string>> PackageDependencyArgs;
+ bool ExportPackageDependencies = false;
};
auto parser =
@@ -75,7 +81,12 @@ bool cmExportCommand(std::vector<std::string> 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<std::string> 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<std::vector<std::string>> ExtraArgs;
+ };
+
+ auto packageDependencyParser =
+ cmArgumentParser<PackageDependencyArguments>{}
+ .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<std::string> const& args,
ebfg->SetTargets(targets);
}
ebfg->SetExportOld(arguments.ExportOld);
+ ebfg->SetExportPackageDependencies(arguments.ExportPackageDependencies);
// Compute the set of configurations exported.
std::vector<std::string> 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 <algorithm>
#include <array>
#include <cassert>
#include <cstring>
@@ -9,12 +10,15 @@
#include <utility>
#include <cm/memory>
+#include <cm/optional>
#include <cmext/string_view>
#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<std::string, cmExportSet::PackageDependency> 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<std::pair<std::string, cmExportSet::PackageDependency>>
+ packageDependenciesSorted(packageDependencies.begin(),
+ packageDependencies.end());
+ std::sort(
+ packageDependenciesSorted.begin(), packageDependenciesSorted.end(),
+ [](const std::pair<std::string, cmExportSet::PackageDependency>& lhs,
+ const std::pair<std::string, cmExportSet::PackageDependency>& 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<std::string, std::string>;
@@ -88,6 +94,7 @@ protected:
const std::set<std::string>& 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<std::string> MissingTargets;
+ std::set<cmGeneratorTarget const*> 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 <vector>
#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<cmTargetExport>& 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 <string>
#include <vector>
+#include <cm/optional>
+
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<std::string> ExtraArguments;
+ cm::optional<unsigned int> SpecifiedIndex;
+ cm::optional<unsigned int> FindPackageIndex;
+ };
+
+ PackageDependency& GetPackageDependencyForSetup(const std::string& name);
+
+ const std::map<std::string, PackageDependency>& GetPackageDependencies()
+ const
+ {
+ return this->PackageDependencies;
+ }
+
private:
std::vector<std::unique_ptr<cmTargetExport>> TargetExports;
std::string Name;
std::vector<cmInstallExportGenerator const*> Installations;
+ std::map<std::string, PackageDependency> 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<std::string> 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<cmFindPackageCall, cmFindPackageStack>;
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 <memory>
+#include <string>
+
+#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<cmFindPackageCall, cmFindPackageStack>
+{
+ using cmConstStack::cmConstStack;
+ friend class cmConstStack<cmFindPackageCall, cmFindPackageStack>;
+};
+#ifndef cmFindPackageStack_cxx
+extern template class cmConstStack<cmFindPackageCall, cmFindPackageStack>;
+#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<std::string> const& args,
cm::make_unique<cmInstallExportGenerator>(
&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<std::string> 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<std::string> unknownArgs;
ica.Parse(args, &unknownArgs);
@@ -2147,7 +2149,8 @@ bool HandleExportMode(std::vector<std::string> 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<std::string>& GetOutputFiles() const
@@ -1020,6 +1026,15 @@ public:
// searches
std::deque<std::vector<std::string>> 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<BT<GeneratorAction>> 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<std::string, cmFileSet> 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<std::string> const& entry, bool before = false);
void InsertCompileOption(BT<std::string> const& entry, bool before = false);
void InsertCompileDefinition(BT<std::string> 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<Project>` 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 \