Просмотр исходного кода

install(TARGETS): Add FILE_SET mode

Kyle Edwards 4 лет назад
Родитель
Сommit
9b479124cc

+ 2 - 0
Source/CMakeLists.txt

@@ -318,6 +318,8 @@ set(SRCS
   cmInstallExportGenerator.cxx
   cmInstallExportGenerator.cxx
   cmInstalledFile.h
   cmInstalledFile.h
   cmInstalledFile.cxx
   cmInstalledFile.cxx
+  cmInstallFileSetGenerator.h
+  cmInstallFileSetGenerator.cxx
   cmInstallFilesGenerator.h
   cmInstallFilesGenerator.h
   cmInstallFilesGenerator.cxx
   cmInstallFilesGenerator.cxx
   cmInstallImportedRuntimeArtifactsGenerator.h
   cmInstallImportedRuntimeArtifactsGenerator.h

+ 112 - 7
Source/cmInstallCommand.cxx

@@ -6,11 +6,13 @@
 #include <cassert>
 #include <cassert>
 #include <cstddef>
 #include <cstddef>
 #include <iterator>
 #include <iterator>
+#include <map>
 #include <set>
 #include <set>
 #include <sstream>
 #include <sstream>
 #include <utility>
 #include <utility>
 
 
 #include <cm/memory>
 #include <cm/memory>
+#include <cm/string_view>
 #include <cmext/string_view>
 #include <cmext/string_view>
 
 
 #include "cmsys/Glob.hxx"
 #include "cmsys/Glob.hxx"
@@ -18,11 +20,13 @@
 #include "cmArgumentParser.h"
 #include "cmArgumentParser.h"
 #include "cmExecutionStatus.h"
 #include "cmExecutionStatus.h"
 #include "cmExportSet.h"
 #include "cmExportSet.h"
+#include "cmFileSet.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstallCommandArguments.h"
 #include "cmInstallCommandArguments.h"
 #include "cmInstallDirectoryGenerator.h"
 #include "cmInstallDirectoryGenerator.h"
 #include "cmInstallExportGenerator.h"
 #include "cmInstallExportGenerator.h"
+#include "cmInstallFileSetGenerator.h"
 #include "cmInstallFilesGenerator.h"
 #include "cmInstallFilesGenerator.h"
 #include "cmInstallGenerator.h"
 #include "cmInstallGenerator.h"
 #include "cmInstallGetRuntimeDependenciesGenerator.h"
 #include "cmInstallGetRuntimeDependenciesGenerator.h"
@@ -89,6 +93,9 @@ public:
   bool MakeFilesFullPath(const char* modeName,
   bool MakeFilesFullPath(const char* modeName,
                          const std::vector<std::string>& relFiles,
                          const std::vector<std::string>& relFiles,
                          std::vector<std::string>& absFiles);
                          std::vector<std::string>& absFiles);
+  bool MakeFilesFullPath(const char* modeName, const std::string& basePath,
+                         const std::vector<std::string>& relFiles,
+                         std::vector<std::string>& absFiles);
   bool CheckCMP0006(bool& failure) const;
   bool CheckCMP0006(bool& failure) const;
 
 
   std::string GetDestination(const cmInstallCommandArguments* args,
   std::string GetDestination(const cmInstallCommandArguments* args,
@@ -177,6 +184,19 @@ std::unique_ptr<cmInstallFilesGenerator> CreateInstallFilesGenerator(
                                      args.GetDestination());
                                      args.GetDestination());
 }
 }
 
 
+std::unique_ptr<cmInstallFileSetGenerator> CreateInstallFileSetGenerator(
+  Helper& helper, cmTarget& target, cmFileSet* fileSet,
+  const std::string& destination, const cmInstallCommandArguments& args)
+{
+  cmInstallGenerator::MessageLevel message =
+    cmInstallGenerator::SelectMessageLevel(helper.Makefile);
+  return cm::make_unique<cmInstallFileSetGenerator>(
+    target.GetName(), fileSet, destination, args.GetPermissions(),
+    args.GetConfigurations(), args.GetComponent(), message,
+    args.GetExcludeFromAll(), args.GetOptional(),
+    helper.Makefile->GetBacktrace());
+}
+
 void AddInstallRuntimeDependenciesGenerator(
 void AddInstallRuntimeDependenciesGenerator(
   Helper& helper, cmInstallRuntimeDependencySet* runtimeDependencySet,
   Helper& helper, cmInstallRuntimeDependencySet* runtimeDependencySet,
   const cmInstallCommandArguments& runtimeArgs,
   const cmInstallCommandArguments& runtimeArgs,
@@ -390,6 +410,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     std::vector<std::string> PrivateHeader;
     std::vector<std::string> PrivateHeader;
     std::vector<std::string> PublicHeader;
     std::vector<std::string> PublicHeader;
     std::vector<std::string> Resource;
     std::vector<std::string> Resource;
+    std::vector<std::vector<std::string>> FileSets;
   };
   };
 
 
   static auto const argHelper =
   static auto const argHelper =
@@ -403,7 +424,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
       .Bind("INCLUDES"_s, &ArgVectors::Includes)
       .Bind("INCLUDES"_s, &ArgVectors::Includes)
       .Bind("PRIVATE_HEADER"_s, &ArgVectors::PrivateHeader)
       .Bind("PRIVATE_HEADER"_s, &ArgVectors::PrivateHeader)
       .Bind("PUBLIC_HEADER"_s, &ArgVectors::PublicHeader)
       .Bind("PUBLIC_HEADER"_s, &ArgVectors::PublicHeader)
-      .Bind("RESOURCE"_s, &ArgVectors::Resource);
+      .Bind("RESOURCE"_s, &ArgVectors::Resource)
+      .Bind("FILE_SET"_s, &ArgVectors::FileSets);
 
 
   std::vector<std::string> genericArgVector;
   std::vector<std::string> genericArgVector;
   ArgVectors const argVectors = argHelper.Parse(args, &genericArgVector);
   ArgVectors const argVectors = argHelper.Parse(args, &genericArgVector);
@@ -442,6 +464,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   cmInstallCommandArguments publicHeaderArgs(helper.DefaultComponentName);
   cmInstallCommandArguments publicHeaderArgs(helper.DefaultComponentName);
   cmInstallCommandArguments resourceArgs(helper.DefaultComponentName);
   cmInstallCommandArguments resourceArgs(helper.DefaultComponentName);
   cmInstallCommandIncludesArgument includesArgs;
   cmInstallCommandIncludesArgument includesArgs;
+  std::vector<cmInstallCommandFileSetArguments> fileSetArgs(
+    argVectors.FileSets.size(), { helper.DefaultComponentName });
 
 
   // now parse the args for specific parts of the target (e.g. LIBRARY,
   // now parse the args for specific parts of the target (e.g. LIBRARY,
   // RUNTIME, ARCHIVE etc.
   // RUNTIME, ARCHIVE etc.
@@ -455,6 +479,15 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   publicHeaderArgs.Parse(argVectors.PublicHeader, &unknownArgs);
   publicHeaderArgs.Parse(argVectors.PublicHeader, &unknownArgs);
   resourceArgs.Parse(argVectors.Resource, &unknownArgs);
   resourceArgs.Parse(argVectors.Resource, &unknownArgs);
   includesArgs.Parse(&argVectors.Includes, &unknownArgs);
   includesArgs.Parse(&argVectors.Includes, &unknownArgs);
+  for (std::size_t i = 0; i < argVectors.FileSets.size(); i++) {
+    // We have to create a separate object for the parsing because
+    // cmArgumentParser<void>::Bind() binds to a specific address, but the
+    // objects in the vector can move around. So we parse in an object with a
+    // fixed address and then copy the data into the vector.
+    cmInstallCommandFileSetArguments fileSetArg(helper.DefaultComponentName);
+    fileSetArg.Parse(argVectors.FileSets[i], &unknownArgs);
+    fileSetArgs[i] = std::move(fileSetArg);
+  }
 
 
   if (!unknownArgs.empty()) {
   if (!unknownArgs.empty()) {
     // Unknown argument.
     // Unknown argument.
@@ -473,6 +506,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   privateHeaderArgs.SetGenericArguments(&genericArgs);
   privateHeaderArgs.SetGenericArguments(&genericArgs);
   publicHeaderArgs.SetGenericArguments(&genericArgs);
   publicHeaderArgs.SetGenericArguments(&genericArgs);
   resourceArgs.SetGenericArguments(&genericArgs);
   resourceArgs.SetGenericArguments(&genericArgs);
+  for (auto& fileSetArg : fileSetArgs) {
+    fileSetArg.SetGenericArguments(&genericArgs);
+  }
 
 
   success = success && archiveArgs.Finalize();
   success = success && archiveArgs.Finalize();
   success = success && libraryArgs.Finalize();
   success = success && libraryArgs.Finalize();
@@ -483,6 +519,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   success = success && privateHeaderArgs.Finalize();
   success = success && privateHeaderArgs.Finalize();
   success = success && publicHeaderArgs.Finalize();
   success = success && publicHeaderArgs.Finalize();
   success = success && resourceArgs.Finalize();
   success = success && resourceArgs.Finalize();
+  for (auto& fileSetArg : fileSetArgs) {
+    success = success && fileSetArg.Finalize();
+  }
 
 
   if (!success) {
   if (!success) {
     return false;
     return false;
@@ -493,7 +532,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   if (archiveArgs.GetNamelinkOnly() || runtimeArgs.GetNamelinkOnly() ||
   if (archiveArgs.GetNamelinkOnly() || runtimeArgs.GetNamelinkOnly() ||
       objectArgs.GetNamelinkOnly() || frameworkArgs.GetNamelinkOnly() ||
       objectArgs.GetNamelinkOnly() || frameworkArgs.GetNamelinkOnly() ||
       bundleArgs.GetNamelinkOnly() || privateHeaderArgs.GetNamelinkOnly() ||
       bundleArgs.GetNamelinkOnly() || privateHeaderArgs.GetNamelinkOnly() ||
-      publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly()) {
+      publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly() ||
+      std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
+                  [](const cmInstallCommandFileSetArguments& fileSetArg)
+                    -> bool { return fileSetArg.GetNamelinkOnly(); })) {
     status.SetError(
     status.SetError(
       "TARGETS given NAMELINK_ONLY option not in LIBRARY group.  "
       "TARGETS given NAMELINK_ONLY option not in LIBRARY group.  "
       "The NAMELINK_ONLY option may be specified only following LIBRARY.");
       "The NAMELINK_ONLY option may be specified only following LIBRARY.");
@@ -502,7 +544,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   if (archiveArgs.GetNamelinkSkip() || runtimeArgs.GetNamelinkSkip() ||
   if (archiveArgs.GetNamelinkSkip() || runtimeArgs.GetNamelinkSkip() ||
       objectArgs.GetNamelinkSkip() || frameworkArgs.GetNamelinkSkip() ||
       objectArgs.GetNamelinkSkip() || frameworkArgs.GetNamelinkSkip() ||
       bundleArgs.GetNamelinkSkip() || privateHeaderArgs.GetNamelinkSkip() ||
       bundleArgs.GetNamelinkSkip() || privateHeaderArgs.GetNamelinkSkip() ||
-      publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip()) {
+      publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip() ||
+      std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
+                  [](const cmInstallCommandFileSetArguments& fileSetArg)
+                    -> bool { return fileSetArg.GetNamelinkSkip(); })) {
     status.SetError(
     status.SetError(
       "TARGETS given NAMELINK_SKIP option not in LIBRARY group.  "
       "TARGETS given NAMELINK_SKIP option not in LIBRARY group.  "
       "The NAMELINK_SKIP option may be specified only following LIBRARY.");
       "The NAMELINK_SKIP option may be specified only following LIBRARY.");
@@ -515,7 +560,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
       bundleArgs.HasNamelinkComponent() ||
       bundleArgs.HasNamelinkComponent() ||
       privateHeaderArgs.HasNamelinkComponent() ||
       privateHeaderArgs.HasNamelinkComponent() ||
       publicHeaderArgs.HasNamelinkComponent() ||
       publicHeaderArgs.HasNamelinkComponent() ||
-      resourceArgs.HasNamelinkComponent()) {
+      resourceArgs.HasNamelinkComponent() ||
+      std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
+                  [](const cmInstallCommandFileSetArguments& fileSetArg)
+                    -> bool { return fileSetArg.HasNamelinkComponent(); })) {
     status.SetError(
     status.SetError(
       "TARGETS given NAMELINK_COMPONENT option not in LIBRARY group.  "
       "TARGETS given NAMELINK_COMPONENT option not in LIBRARY group.  "
       "The NAMELINK_COMPONENT option may be specified only following "
       "The NAMELINK_COMPONENT option may be specified only following "
@@ -531,12 +579,21 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
       !libraryArgs.GetType().empty() || !runtimeArgs.GetType().empty() ||
       !libraryArgs.GetType().empty() || !runtimeArgs.GetType().empty() ||
       !objectArgs.GetType().empty() || !frameworkArgs.GetType().empty() ||
       !objectArgs.GetType().empty() || !frameworkArgs.GetType().empty() ||
       !bundleArgs.GetType().empty() || !privateHeaderArgs.GetType().empty() ||
       !bundleArgs.GetType().empty() || !privateHeaderArgs.GetType().empty() ||
-      !publicHeaderArgs.GetType().empty() || !resourceArgs.GetType().empty()) {
+      !publicHeaderArgs.GetType().empty() || !resourceArgs.GetType().empty() ||
+      std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
+                  [](const cmInstallCommandFileSetArguments& fileSetArg)
+                    -> bool { return !fileSetArg.GetType().empty(); })) {
     status.SetError(
     status.SetError(
       "TARGETS given TYPE option. The TYPE option may only be specified in "
       "TARGETS given TYPE option. The TYPE option may only be specified in "
       " install(FILES) and install(DIRECTORIES).");
       " install(FILES) and install(DIRECTORIES).");
     return false;
     return false;
   }
   }
+  if (std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
+                  [](const cmInstallCommandFileSetArguments& fileSetArg)
+                    -> bool { return fileSetArg.GetFileSet().empty(); })) {
+    status.SetError("TARGETS given FILE_SET option without file set name.");
+    return false;
+  }
 
 
   cmInstallRuntimeDependencySet* runtimeDependencySet = nullptr;
   cmInstallRuntimeDependencySet* runtimeDependencySet = nullptr;
   if (withRuntimeDependencies) {
   if (withRuntimeDependencies) {
@@ -647,6 +704,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   bool installsPrivateHeader = false;
   bool installsPrivateHeader = false;
   bool installsPublicHeader = false;
   bool installsPublicHeader = false;
   bool installsResource = false;
   bool installsResource = false;
+  std::vector<bool> installsFileSet(fileSetArgs.size(), false);
 
 
   // Generate install script code to install the given targets.
   // Generate install script code to install the given targets.
   for (cmTarget* ti : targets) {
   for (cmTarget* ti : targets) {
@@ -662,6 +720,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     std::unique_ptr<cmInstallFilesGenerator> privateHeaderGenerator;
     std::unique_ptr<cmInstallFilesGenerator> privateHeaderGenerator;
     std::unique_ptr<cmInstallFilesGenerator> publicHeaderGenerator;
     std::unique_ptr<cmInstallFilesGenerator> publicHeaderGenerator;
     std::unique_ptr<cmInstallFilesGenerator> resourceGenerator;
     std::unique_ptr<cmInstallFilesGenerator> resourceGenerator;
+    std::vector<std::unique_ptr<cmInstallFileSetGenerator>> fileSetGenerators;
 
 
     // Avoid selecting default destinations for PUBLIC_HEADER and
     // Avoid selecting default destinations for PUBLIC_HEADER and
     // PRIVATE_HEADER if any artifacts are specified.
     // PRIVATE_HEADER if any artifacts are specified.
@@ -991,6 +1050,35 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
       }
       }
     }
     }
 
 
+    if (!namelinkOnly) {
+      for (std::size_t i = 0; i < fileSetArgs.size(); i++) {
+        auto* fileSet = target.GetFileSet(fileSetArgs[i].GetFileSet());
+        auto interfaceFileSetEntries = cmExpandedList(target.GetSafeProperty(
+          cmTarget::GetInterfaceFileSetsPropertyName(fileSet->GetType())));
+        if (fileSet &&
+            std::find(
+              interfaceFileSetEntries.begin(), interfaceFileSetEntries.end(),
+              fileSetArgs[i].GetFileSet()) != interfaceFileSetEntries.end()) {
+          std::string destination;
+          if (fileSet->GetType() == "HEADERS"_s) {
+            destination = helper.GetIncludeDestination(&fileSetArgs[i]);
+          } else {
+            destination = fileSetArgs[i].GetDestination();
+            if (destination.empty()) {
+              status.SetError(
+                cmStrCat("TARGETS given no FILE_SET DESTINATION for target \"",
+                         target.GetName(), "\" file set \"",
+                         fileSet->GetName(), "\"."));
+              return false;
+            }
+          }
+          fileSetGenerators.push_back(CreateInstallFileSetGenerator(
+            helper, target, fileSet, destination, fileSetArgs[i]));
+          installsFileSet[i] = true;
+        }
+      }
+    }
+
     // Add this install rule to an export if one was specified.
     // Add this install rule to an export if one was specified.
     addTargetExport();
     addTargetExport();
 
 
@@ -1016,6 +1104,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     helper.Makefile->AddInstallGenerator(std::move(privateHeaderGenerator));
     helper.Makefile->AddInstallGenerator(std::move(privateHeaderGenerator));
     helper.Makefile->AddInstallGenerator(std::move(publicHeaderGenerator));
     helper.Makefile->AddInstallGenerator(std::move(publicHeaderGenerator));
     helper.Makefile->AddInstallGenerator(std::move(resourceGenerator));
     helper.Makefile->AddInstallGenerator(std::move(resourceGenerator));
+    for (auto& gen : fileSetGenerators) {
+      helper.Makefile->AddInstallGenerator(std::move(gen));
+    }
   }
   }
 
 
   if (withRuntimeDependencies && !runtimeDependencySet->Empty()) {
   if (withRuntimeDependencies && !runtimeDependencySet->Empty()) {
@@ -1067,6 +1158,12 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
     helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
       resourceArgs.GetComponent());
       resourceArgs.GetComponent());
   }
   }
+  for (std::size_t i = 0; i < fileSetArgs.size(); i++) {
+    if (installsFileSet[i]) {
+      helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
+        fileSetArgs[i].GetComponent());
+    }
+  }
 
 
   return true;
   return true;
 }
 }
@@ -2062,13 +2159,21 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args,
 bool Helper::MakeFilesFullPath(const char* modeName,
 bool Helper::MakeFilesFullPath(const char* modeName,
                                const std::vector<std::string>& relFiles,
                                const std::vector<std::string>& relFiles,
                                std::vector<std::string>& absFiles)
                                std::vector<std::string>& absFiles)
+{
+  return this->MakeFilesFullPath(
+    modeName, this->Makefile->GetCurrentSourceDirectory(), relFiles, absFiles);
+}
+
+bool Helper::MakeFilesFullPath(const char* modeName,
+                               const std::string& basePath,
+                               const std::vector<std::string>& relFiles,
+                               std::vector<std::string>& absFiles)
 {
 {
   for (std::string const& relFile : relFiles) {
   for (std::string const& relFile : relFiles) {
     std::string file = relFile;
     std::string file = relFile;
     std::string::size_type gpos = cmGeneratorExpression::Find(file);
     std::string::size_type gpos = cmGeneratorExpression::Find(file);
     if (gpos != 0 && !cmSystemTools::FileIsFullPath(file)) {
     if (gpos != 0 && !cmSystemTools::FileIsFullPath(file)) {
-      file =
-        cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', relFile);
+      file = cmStrCat(basePath, '/', relFile);
     }
     }
 
 
     // Make sure the file is not a directory.
     // Make sure the file is not a directory.

+ 19 - 0
Source/cmInstallCommandArguments.cxx

@@ -152,6 +152,11 @@ const std::string& cmInstallCommandArguments::GetType() const
   return this->Type;
   return this->Type;
 }
 }
 
 
+const std::string& cmInstallCommandArguments::GetDefaultComponent() const
+{
+  return this->DefaultComponentName;
+}
+
 const std::vector<std::string>& cmInstallCommandArguments::GetConfigurations()
 const std::vector<std::string>& cmInstallCommandArguments::GetConfigurations()
   const
   const
 {
 {
@@ -220,3 +225,17 @@ void cmInstallCommandIncludesArgument::Parse(
     this->IncludeDirs.push_back(std::move(dir));
     this->IncludeDirs.push_back(std::move(dir));
   }
   }
 }
 }
+
+cmInstallCommandFileSetArguments::cmInstallCommandFileSetArguments(
+  std::string defaultComponent)
+  : cmInstallCommandArguments(std::move(defaultComponent))
+{
+  this->Bind("FILE_SET"_s, this->FileSet);
+}
+
+void cmInstallCommandFileSetArguments::Parse(
+  std::vector<std::string> args, std::vector<std::string>* unconsumedArgs)
+{
+  args.insert(args.begin(), "FILE_SET");
+  this->cmInstallCommandArguments::Parse(args, unconsumedArgs);
+}

+ 16 - 0
Source/cmInstallCommandArguments.h

@@ -34,6 +34,8 @@ public:
   bool HasNamelinkComponent() const;
   bool HasNamelinkComponent() const;
   const std::string& GetType() const;
   const std::string& GetType() const;
 
 
+  const std::string& GetDefaultComponent() const;
+
   static bool CheckPermissions(const std::string& onePerm, std::string& perm);
   static bool CheckPermissions(const std::string& onePerm, std::string& perm);
 
 
 private:
 private:
@@ -71,3 +73,17 @@ public:
 private:
 private:
   std::vector<std::string> IncludeDirs;
   std::vector<std::string> IncludeDirs;
 };
 };
+
+class cmInstallCommandFileSetArguments : public cmInstallCommandArguments
+{
+public:
+  cmInstallCommandFileSetArguments(std::string defaultComponent);
+
+  void Parse(std::vector<std::string> args,
+             std::vector<std::string>* unconsumedArgs);
+
+  const std::string& GetFileSet() const { return this->FileSet; }
+
+private:
+  std::string FileSet;
+};

+ 88 - 0
Source/cmInstallFileSetGenerator.cxx

@@ -0,0 +1,88 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmInstallFileSetGenerator.h"
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "cmFileSet.h"
+#include "cmGeneratorExpression.h"
+#include "cmGlobalGenerator.h"
+#include "cmInstallType.h"
+#include "cmLocalGenerator.h"
+#include "cmStringAlgorithms.h"
+
+cmInstallFileSetGenerator::cmInstallFileSetGenerator(
+  std::string targetName, cmFileSet* fileSet, std::string const& dest,
+  std::string file_permissions, std::vector<std::string> const& configurations,
+  std::string const& component, MessageLevel message, bool exclude_from_all,
+  bool optional, cmListFileBacktrace backtrace)
+  : cmInstallGenerator(dest, configurations, component, message,
+                       exclude_from_all, false, std::move(backtrace))
+  , TargetName(std::move(targetName))
+  , FileSet(fileSet)
+  , FilePermissions(std::move(file_permissions))
+  , Optional(optional)
+{
+  this->ActionsPerConfig = true;
+}
+
+cmInstallFileSetGenerator::~cmInstallFileSetGenerator() = default;
+
+bool cmInstallFileSetGenerator::Compute(cmLocalGenerator* lg)
+{
+  this->LocalGenerator = lg;
+
+  // Lookup this target in the current directory.
+  this->Target = lg->FindLocalNonAliasGeneratorTarget(this->TargetName);
+  if (!this->Target) {
+    // If no local target has been found, find it in the global scope.
+    this->Target =
+      lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName);
+  }
+
+  return true;
+}
+
+std::string cmInstallFileSetGenerator::GetDestination(
+  std::string const& config) const
+{
+  return cmGeneratorExpression::Evaluate(this->Destination,
+                                         this->LocalGenerator, config);
+}
+
+void cmInstallFileSetGenerator::GenerateScriptForConfig(
+  std::ostream& os, const std::string& config, Indent indent)
+{
+  for (auto const& dirEntry : this->CalculateFilesPerDir(config)) {
+    std::string destSub;
+    if (!dirEntry.first.empty()) {
+      destSub = cmStrCat('/', dirEntry.first);
+    }
+    this->AddInstallRule(os, cmStrCat(this->GetDestination(config), destSub),
+                         cmInstallType_FILES, dirEntry.second,
+                         this->GetOptional(), this->FilePermissions.c_str(),
+                         nullptr, nullptr, nullptr, indent);
+  }
+}
+
+std::map<std::string, std::vector<std::string>>
+cmInstallFileSetGenerator::CalculateFilesPerDir(
+  const std::string& config) const
+{
+  std::map<std::string, std::vector<std::string>> result;
+
+  auto dirCges = this->FileSet->CompileDirectoryEntries();
+  auto dirs = this->FileSet->EvaluateDirectoryEntries(
+    dirCges, this->LocalGenerator, config, this->Target);
+
+  auto fileCges = this->FileSet->CompileFileEntries();
+  for (auto const& fileCge : fileCges) {
+    this->FileSet->EvaluateFileEntry(
+      dirs, result, fileCge, this->LocalGenerator, config, this->Target);
+  }
+
+  return result;
+}

+ 52 - 0
Source/cmInstallFileSetGenerator.h

@@ -0,0 +1,52 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <iosfwd>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "cmInstallGenerator.h"
+#include "cmListFileCache.h"
+#include "cmScriptGenerator.h"
+
+class cmGeneratorTarget;
+class cmFileSet;
+class cmLocalGenerator;
+
+class cmInstallFileSetGenerator : public cmInstallGenerator
+{
+public:
+  cmInstallFileSetGenerator(std::string targetName, cmFileSet* fileSet,
+                            std::string const& dest,
+                            std::string file_permissions,
+                            std::vector<std::string> const& configurations,
+                            std::string const& component, MessageLevel message,
+                            bool exclude_from_all, bool optional,
+                            cmListFileBacktrace backtrace);
+  ~cmInstallFileSetGenerator() override;
+
+  bool Compute(cmLocalGenerator* lg) override;
+
+  std::string GetDestination(std::string const& config) const;
+  std::string GetDestination() const { return this->Destination; }
+  bool GetOptional() const { return this->Optional; }
+  cmFileSet* GetFileSet() const { return this->FileSet; }
+  cmGeneratorTarget* GetTarget() const { return this->Target; }
+
+protected:
+  void GenerateScriptForConfig(std::ostream& os, const std::string& config,
+                               Indent indent) override;
+
+private:
+  std::string TargetName;
+  cmLocalGenerator* LocalGenerator;
+  cmFileSet* const FileSet;
+  std::string const FilePermissions;
+  bool const Optional;
+  cmGeneratorTarget* Target;
+
+  std::map<std::string, std::vector<std::string>> CalculateFilesPerDir(
+    const std::string& config) const;
+};

+ 1 - 0
bootstrap

@@ -387,6 +387,7 @@ CMAKE_CXX_SOURCES="\
   cmInstallCommandArguments \
   cmInstallCommandArguments \
   cmInstallDirectoryGenerator \
   cmInstallDirectoryGenerator \
   cmInstallExportGenerator \
   cmInstallExportGenerator \
+  cmInstallFileSetGenerator \
   cmInstallFilesCommand \
   cmInstallFilesCommand \
   cmInstallFilesGenerator \
   cmInstallFilesGenerator \
   cmInstallGenerator \
   cmInstallGenerator \