瀏覽代碼

install(EXPORT): Export find_dependency() calls

Issue: #20511
Co-Authored-by: Brad King <[email protected]>
Co-Authored-by: Robert Maynard <[email protected]>
Kyle Edwards 1 年之前
父節點
當前提交
c6e6861e63
共有 53 個文件被更改,包括 700 次插入10 次删除
  1. 54 1
      Help/command/export.rst
  2. 30 1
      Help/command/install.rst
  3. 1 0
      Help/manual/cmake-properties.7.rst
  4. 1 0
      Help/manual/cmake-variables.7.rst
  5. 12 0
      Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst
  6. 14 0
      Help/release/dev/export-find_dependency-calls.rst
  7. 6 0
      Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst
  8. 2 0
      Source/CMakeLists.txt
  9. 1 0
      Source/cmExportBuildAndroidMKGenerator.h
  10. 1 0
      Source/cmExportBuildFileGenerator.h
  11. 73 1
      Source/cmExportCommand.cxx
  12. 93 1
      Source/cmExportFileGenerator.cxx
  13. 13 0
      Source/cmExportFileGenerator.h
  14. 1 0
      Source/cmExportInstallAndroidMKGenerator.h
  15. 7 1
      Source/cmExportInstallFileGenerator.h
  16. 11 0
      Source/cmExportSet.cxx
  17. 28 0
      Source/cmExportSet.h
  18. 2 0
      Source/cmFindPackageCommand.cxx
  19. 7 0
      Source/cmFindPackageStack.cxx
  20. 33 0
      Source/cmFindPackageStack.h
  21. 5 2
      Source/cmInstallCommand.cxx
  22. 3 1
      Source/cmInstallExportGenerator.cxx
  23. 3 1
      Source/cmInstallExportGenerator.h
  24. 35 0
      Source/cmMakefile.cxx
  25. 18 0
      Source/cmMakefile.h
  26. 11 0
      Source/cmTarget.cxx
  27. 4 0
      Source/cmTarget.h
  28. 4 1
      Tests/ExportImport/Export/CMakeLists.txt
  29. 3 0
      Tests/ExportImport/External/FooConfig.cmake
  30. 1 0
      Tests/ExportImport/Import/CMakeLists.txt
  31. 1 0
      Tests/ExportImport/InitialCache.cmake.in
  32. 17 0
      Tests/RunCMake/export/CMake/FindHasDeps.cmake
  33. 2 0
      Tests/RunCMake/export/CMake/FindP1.cmake
  34. 2 0
      Tests/RunCMake/export/CMake/FindP2.cmake
  35. 2 0
      Tests/RunCMake/export/CMake/FindP3.cmake
  36. 2 0
      Tests/RunCMake/export/CMake/FindP4.cmake
  37. 2 0
      Tests/RunCMake/export/CMake/FindP9.cmake
  38. 35 0
      Tests/RunCMake/export/FindDependencyExport-check.cmake
  39. 30 0
      Tests/RunCMake/export/FindDependencyExport.cmake
  40. 14 0
      Tests/RunCMake/export/FindDependencyExportFetchContent-check.cmake
  41. 37 0
      Tests/RunCMake/export/FindDependencyExportFetchContent.cmake
  42. 4 0
      Tests/RunCMake/export/FindDependencyExportShared-check.cmake
  43. 14 0
      Tests/RunCMake/export/FindDependencyExportShared.cmake
  44. 13 0
      Tests/RunCMake/export/FindDependencyExportStatic-check.cmake
  45. 14 0
      Tests/RunCMake/export/FindDependencyExportStatic.cmake
  46. 4 0
      Tests/RunCMake/export/RunCMakeTest.cmake
  47. 4 0
      Tests/RunCMake/install/EXPORT-FindDependencyExport-all-check.cmake
  48. 18 0
      Tests/RunCMake/install/EXPORT-FindDependencyExport.cmake
  49. 1 0
      Tests/RunCMake/install/RunCMakeTest.cmake
  50. 2 0
      Tests/RunCMake/install/cmake/FindP1.cmake
  51. 2 0
      Tests/RunCMake/install/cmake/FindP2.cmake
  52. 2 0
      Tests/RunCMake/install/cmake/FindP3.cmake
  53. 1 0
      bootstrap

+ 54 - 1
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.

+ 30 - 1
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
 

+ 1 - 0
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

+ 1 - 0
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

+ 12 - 0
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`.

+ 14 - 0
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.

+ 6 - 0
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`.

+ 2 - 0
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

+ 1 - 0
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;

+ 1 - 0
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;

+ 73 - 1
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 =

+ 93 - 1
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()) {

+ 13 - 0
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,

+ 1 - 0
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;

+ 7 - 1
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.

+ 11 - 0
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) {

+ 28 - 0
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[].

+ 2 - 0
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

+ 7 - 0
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>;

+ 33 - 0
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

+ 5 - 2
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;
 }

+ 3 - 1
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.

+ 3 - 1
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;

+ 35 - 0
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)

+ 18 - 0
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
@@ -659,6 +660,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
    */
@@ -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;

+ 11 - 0
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 &&

+ 4 - 0
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);

+ 4 - 1
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)

+ 3 - 0
Tests/ExportImport/External/FooConfig.cmake

@@ -0,0 +1,3 @@
+if(NOT TARGET Foo::Foo)
+  add_library(Foo::Foo INTERFACE IMPORTED)
+endif()

+ 1 - 0
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()

+ 1 - 0
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")

+ 17 - 0
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)

+ 2 - 0
Tests/RunCMake/export/CMake/FindP1.cmake

@@ -0,0 +1,2 @@
+add_library(l1 IMPORTED INTERFACE)
+set(P1_FOUND TRUE)

+ 2 - 0
Tests/RunCMake/export/CMake/FindP2.cmake

@@ -0,0 +1,2 @@
+add_library(l2 IMPORTED INTERFACE)
+set(P2_FOUND TRUE)

+ 2 - 0
Tests/RunCMake/export/CMake/FindP3.cmake

@@ -0,0 +1,2 @@
+add_library(l3 IMPORTED INTERFACE)
+set(P3_FOUND TRUE)

+ 2 - 0
Tests/RunCMake/export/CMake/FindP4.cmake

@@ -0,0 +1,2 @@
+add_library(l4 IMPORTED INTERFACE)
+set(P4_FOUND TRUE)

+ 2 - 0
Tests/RunCMake/export/CMake/FindP9.cmake

@@ -0,0 +1,2 @@
+add_library(l9 IMPORTED INTERFACE)
+set(P9_FOUND TRUE)

+ 35 - 0
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()

+ 30 - 0
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)

+ 14 - 0
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()

+ 37 - 0
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)

+ 4 - 0
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()

+ 14 - 0
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)

+ 13 - 0
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()

+ 14 - 0
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)

+ 4 - 0
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)

+ 4 - 0
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()

+ 18 - 0
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)

+ 1 - 0
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)

+ 2 - 0
Tests/RunCMake/install/cmake/FindP1.cmake

@@ -0,0 +1,2 @@
+add_library(lib1 IMPORTED INTERFACE)
+set(P1_FOUND TRUE)

+ 2 - 0
Tests/RunCMake/install/cmake/FindP2.cmake

@@ -0,0 +1,2 @@
+add_library(lib2 IMPORTED INTERFACE)
+set(P2_FOUND TRUE)

+ 2 - 0
Tests/RunCMake/install/cmake/FindP3.cmake

@@ -0,0 +1,2 @@
+add_library(lib3 IMPORTED INTERFACE)
+set(P3_FOUND TRUE)

+ 1 - 0
bootstrap

@@ -364,6 +364,7 @@ CMAKE_CXX_SOURCES="\
   cmFindFileCommand \
   cmFindLibraryCommand \
   cmFindPackageCommand \
+  cmFindPackageStack \
   cmFindPathCommand \
   cmFindProgramCommand \
   cmForEachCommand \