Browse Source

target_sources(): Add FILE_SET mode

Kyle Edwards 4 years ago
parent
commit
d8af2d954f

+ 2 - 2
Source/cmTargetPropCommandBase.cxx

@@ -155,10 +155,10 @@ bool cmTargetPropCommandBase::ProcessContentArgs(
       return false;
     }
   }
-  return this->PopulateTargetProperies(scope, content, prepend, system);
+  return this->PopulateTargetProperties(scope, content, prepend, system);
 }
 
-bool cmTargetPropCommandBase::PopulateTargetProperies(
+bool cmTargetPropCommandBase::PopulateTargetProperties(
   const std::string& scope, const std::vector<std::string>& content,
   bool prepend, bool system)
 {

+ 3 - 3
Source/cmTargetPropCommandBase.h

@@ -40,6 +40,9 @@ protected:
   virtual void HandleInterfaceContent(cmTarget* tgt,
                                       const std::vector<std::string>& content,
                                       bool prepend, bool system);
+  virtual bool PopulateTargetProperties(
+    const std::string& scope, const std::vector<std::string>& content,
+    bool prepend, bool system);
 
 private:
   virtual void HandleMissingTarget(const std::string& name) = 0;
@@ -52,9 +55,6 @@ private:
 
   bool ProcessContentArgs(std::vector<std::string> const& args,
                           unsigned int& argIndex, bool prepend, bool system);
-  bool PopulateTargetProperies(const std::string& scope,
-                               const std::vector<std::string>& content,
-                               bool prepend, bool system);
 
   cmExecutionStatus& Status;
 };

+ 164 - 0
Source/cmTargetSourcesCommand.cxx

@@ -2,9 +2,17 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetSourcesCommand.h"
 
+#include <algorithm>
 #include <sstream>
+#include <utility>
 
+#include <cm/string_view>
+#include <cmext/string_view>
+
+#include "cmArgumentParser.h"
+#include "cmFileSet.h"
 #include "cmGeneratorExpression.h"
+#include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
@@ -15,6 +23,20 @@
 
 namespace {
 
+struct FileSetArgs
+{
+  std::string Type;
+  std::string FileSet;
+  std::vector<std::string> BaseDirs;
+  std::vector<std::string> Files;
+};
+
+auto const FileSetArgsParser = cmArgumentParser<FileSetArgs>()
+                                 .Bind("TYPE"_s, &FileSetArgs::Type)
+                                 .Bind("FILE_SET"_s, &FileSetArgs::FileSet)
+                                 .Bind("BASE_DIRS"_s, &FileSetArgs::BaseDirs)
+                                 .Bind("FILES"_s, &FileSetArgs::Files);
+
 class TargetSourcesImpl : public cmTargetPropCommandBase
 {
 public:
@@ -51,6 +73,17 @@ private:
     return true; // Successfully handled.
   }
 
+  bool PopulateTargetProperties(const std::string& scope,
+                                const std::vector<std::string>& content,
+                                bool prepend, bool system) override
+  {
+    if (!content.empty() && content.front() == "FILE_SET"_s) {
+      return this->HandleFileSetMode(scope, content, prepend, system);
+    }
+    return this->cmTargetPropCommandBase::PopulateTargetProperties(
+      scope, content, prepend, system);
+  }
+
   std::string Join(const std::vector<std::string>& content) override
   {
     return cmJoin(content, ";");
@@ -69,6 +102,10 @@ private:
   std::vector<std::string> ConvertToAbsoluteContent(
     cmTarget* tgt, const std::vector<std::string>& content,
     IsInterface isInterfaceContent, CheckCMP0076 checkCmp0076);
+
+  bool HandleFileSetMode(const std::string& scope,
+                         const std::vector<std::string>& content, bool prepend,
+                         bool system);
 };
 
 std::vector<std::string> TargetSourcesImpl::ConvertToAbsoluteContent(
@@ -147,6 +184,133 @@ std::vector<std::string> TargetSourcesImpl::ConvertToAbsoluteContent(
   return useAbsoluteContent ? absoluteContent : content;
 }
 
+bool TargetSourcesImpl::HandleFileSetMode(
+  const std::string& scope, const std::vector<std::string>& content,
+  bool /*prepend*/, bool /*system*/)
+{
+  std::vector<std::string> unparsed;
+  auto args = FileSetArgsParser.Parse(content, &unparsed);
+
+  if (!unparsed.empty()) {
+    this->SetError(
+      cmStrCat("Unrecognized keyword: \"", unparsed.front(), "\""));
+    return false;
+  }
+
+  if (args.FileSet.empty()) {
+    this->SetError("FILE_SET must not be empty");
+    return false;
+  }
+
+  bool const isDefault = args.Type == args.FileSet ||
+    (args.Type.empty() && args.FileSet[0] >= 'A' && args.FileSet[0] <= 'Z');
+  std::string type = isDefault ? args.FileSet : args.Type;
+
+  auto fileSet = this->Target->GetOrCreateFileSet(args.FileSet, type);
+  if (fileSet.second) {
+    if (!isDefault) {
+      if (args.FileSet[0] >= 'A' && args.FileSet[0] <= 'Z') {
+        this->SetError(
+          "Non-default file set name must not start with a capital letter");
+        return false;
+      }
+    }
+    if (type.empty()) {
+      this->SetError("Must specify a TYPE when creating file set");
+      return false;
+    }
+    if (type != "HEADERS"_s) {
+      this->SetError("File set TYPE may only be \"HEADERS\"");
+      return false;
+    }
+
+    if (args.BaseDirs.empty()) {
+      args.BaseDirs.emplace_back(this->Makefile->GetCurrentSourceDirectory());
+    }
+
+    if (scope == "PRIVATE"_s || scope == "PUBLIC"_s) {
+      this->Target->AppendProperty(cmTarget::GetFileSetsPropertyName(type),
+                                   args.FileSet);
+    }
+    if (scope == "INTERFACE"_s || scope == "PUBLIC"_s) {
+      this->Target->AppendProperty(
+        cmTarget::GetInterfaceFileSetsPropertyName(type), args.FileSet);
+    }
+  } else {
+    type = fileSet.first->GetType();
+    if (!args.Type.empty() && args.Type != type) {
+      this->SetError(cmStrCat(
+        "Type \"", args.Type, "\" for file set \"", fileSet.first->GetName(),
+        "\" does not match original type \"", type, "\""));
+      return false;
+    }
+
+    std::string existingScope = "PRIVATE";
+
+    auto const fileSetsProperty = cmTarget::GetFileSetsPropertyName(type);
+    auto const interfaceFileSetsProperty =
+      cmTarget::GetInterfaceFileSetsPropertyName(type);
+    std::vector<std::string> fileSets;
+    std::vector<std::string> interfaceFileSets;
+    cmExpandList(this->Target->GetSafeProperty(fileSetsProperty), fileSets);
+    cmExpandList(this->Target->GetSafeProperty(interfaceFileSetsProperty),
+                 interfaceFileSets);
+
+    if (std::find(interfaceFileSets.begin(), interfaceFileSets.end(),
+                  args.FileSet) != interfaceFileSets.end()) {
+      existingScope = "INTERFACE";
+    }
+    if (std::find(fileSets.begin(), fileSets.end(), args.FileSet) !=
+        fileSets.end()) {
+      if (existingScope == "INTERFACE"_s) {
+        existingScope = "PUBLIC";
+      }
+    } else if (existingScope != "INTERFACE"_s) {
+      this->SetError(cmStrCat("File set \"", args.FileSet, "\" is not in ",
+                              fileSetsProperty, " or ",
+                              interfaceFileSetsProperty));
+      return false;
+    }
+
+    if (scope != existingScope) {
+      this->SetError(
+        cmStrCat("Scope ", scope, " for file set \"", args.FileSet,
+                 "\" does not match original scope ", existingScope));
+      return false;
+    }
+  }
+
+  auto files = this->Join(this->ConvertToAbsoluteContent(
+    this->Target, args.Files, IsInterface::Yes, CheckCMP0076::No));
+  if (!files.empty()) {
+    fileSet.first->AddFileEntry(
+      BT<std::string>(files, this->Makefile->GetBacktrace()));
+  }
+
+  auto baseDirectories = this->Join(this->ConvertToAbsoluteContent(
+    this->Target, args.BaseDirs, IsInterface::Yes, CheckCMP0076::No));
+  if (!baseDirectories.empty()) {
+    fileSet.first->AddDirectoryEntry(
+      BT<std::string>(baseDirectories, this->Makefile->GetBacktrace()));
+    if (type == "HEADERS"_s) {
+      for (auto const& dir : cmExpandedList(baseDirectories)) {
+        auto interfaceDirectoriesGenex =
+          cmStrCat("$<BUILD_INTERFACE:", dir, ">");
+        if (scope == "PRIVATE"_s || scope == "PUBLIC"_s) {
+          this->Target->AppendProperty("INCLUDE_DIRECTORIES",
+                                       interfaceDirectoriesGenex);
+        }
+        if (scope == "INTERFACE"_s || scope == "PUBLIC"_s) {
+          this->Target->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES",
+                                       interfaceDirectoriesGenex);
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
 } // namespace
 
 bool cmTargetSourcesCommand(std::vector<std::string> const& args,