Kaynağa Gözat

Merge topic 'FILE_SET-properties'

4113364c49 FILE_SET: properties management

Acked-by: Kitware Robot <[email protected]>
Tested-by: buildbot <[email protected]>
Reviewed-by: Vito Gamberini <[email protected]>
Merge-request: !11631
Brad King 1 ay önce
ebeveyn
işleme
810e5d3a7a
39 değiştirilmiş dosya ile 518 ekleme ve 77 silme
  1. 18 7
      Help/command/get_property.rst
  2. 17 6
      Help/command/set_property.rst
  3. 4 0
      Help/release/dev/FILE_SET-properties.rst
  4. 94 4
      Source/cmFileSet.cxx
  5. 33 3
      Source/cmFileSet.h
  6. 73 27
      Source/cmGetPropertyCommand.cxx
  7. 2 1
      Source/cmProperty.h
  8. 144 22
      Source/cmSetPropertyCommand.cxx
  9. 14 0
      Source/cmSetPropertyCommand.h
  10. 1 2
      Source/cmTarget.cxx
  11. 6 4
      Tests/CMakeLib/testDebuggerVariablesHelper.cxx
  12. 1 0
      Tests/RunCMake/get_property/BadFileSet-result.txt
  13. 5 0
      Tests/RunCMake/get_property/BadFileSet-stderr.txt
  14. 3 0
      Tests/RunCMake/get_property/BadFileSet.cmake
  15. 1 0
      Tests/RunCMake/get_property/BadFileSetTarget-result.txt
  16. 4 0
      Tests/RunCMake/get_property/BadFileSetTarget-stderr.txt
  17. 1 0
      Tests/RunCMake/get_property/BadFileSetTarget.cmake
  18. 1 1
      Tests/RunCMake/get_property/BadScope-stderr.txt
  19. 2 0
      Tests/RunCMake/get_property/RunCMakeTest.cmake
  20. 1 0
      Tests/RunCMake/set_property/COMPILE_DEFINITIONS-stdout.txt
  21. 1 0
      Tests/RunCMake/set_property/COMPILE_DEFINITIONS.cmake
  22. 1 0
      Tests/RunCMake/set_property/COMPILE_OPTIONS-stdout.txt
  23. 1 0
      Tests/RunCMake/set_property/COMPILE_OPTIONS.cmake
  24. 16 0
      Tests/RunCMake/set_property/Common.cmake
  25. 1 0
      Tests/RunCMake/set_property/FILE_SET-PROPERTY-missing-result.txt
  26. 10 0
      Tests/RunCMake/set_property/FILE_SET-PROPERTY-missing-stderr.txt
  27. 7 0
      Tests/RunCMake/set_property/FILE_SET-PROPERTY-missing.cmake
  28. 1 0
      Tests/RunCMake/set_property/FILE_SET-TARGET-invalid-result.txt
  29. 4 0
      Tests/RunCMake/set_property/FILE_SET-TARGET-invalid-stderr.txt
  30. 2 0
      Tests/RunCMake/set_property/FILE_SET-TARGET-invalid.cmake
  31. 1 0
      Tests/RunCMake/set_property/FILE_SET-invalid-result.txt
  32. 5 0
      Tests/RunCMake/set_property/FILE_SET-invalid-stderr.txt
  33. 4 0
      Tests/RunCMake/set_property/FILE_SET-invalid.cmake
  34. 30 0
      Tests/RunCMake/set_property/FILE_SET.cmake
  35. 1 0
      Tests/RunCMake/set_property/INCLUDE_DIRECTORIES-stdout.txt
  36. 1 0
      Tests/RunCMake/set_property/INCLUDE_DIRECTORIES.cmake
  37. 5 0
      Tests/RunCMake/set_property/RunCMakeTest.cmake
  38. 1 0
      Tests/RunCMake/set_property/USER_PROP-stdout.txt
  39. 1 0
      Tests/RunCMake/set_property/USER_PROP.cmake

+ 18 - 7
Help/command/get_property.rst

@@ -6,16 +6,17 @@ Get a property.
 .. code-block:: cmake
 
   get_property(<variable>
-               <GLOBAL             |
-                DIRECTORY [<dir>]  |
-                TARGET    <target> |
+               <GLOBAL                                                  |
+                DIRECTORY [<dir>]                                       |
+                TARGET    <target>                                      |
+                FILE_SET  <file_set> TARGET <target>                    |
                 SOURCE    <source>
                           [DIRECTORY <dir> | TARGET_DIRECTORY <target>] |
-                INSTALL   <file>   |
+                INSTALL   <file>                                        |
                 TEST      <test>
-                          [DIRECTORY <dir>] |
-                CACHE     <entry>  |
-                VARIABLE           >
+                          [DIRECTORY <dir>]                             |
+                CACHE     <entry>                                       |
+                VARIABLE>
                PROPERTY <name>
                [SET | DEFINED | BRIEF_DOCS | FULL_DOCS])
 
@@ -42,6 +43,16 @@ It must be one of the following:
   Scope must name one existing target.
   See also the :command:`get_target_property` command.
 
+``FILE_SET``
+  .. versionadded:: 4.3
+
+  Scope must name one existing file set.
+
+  The following option is required:
+
+  ``TARGET <target>``
+    The target to which the file set is attached.
+
 ``SOURCE``
   Scope must name one source file.  By default, the source file's property
   will be read from the current source directory's scope.

+ 17 - 6
Help/command/set_property.rst

@@ -5,16 +5,17 @@ Set a named property in a given scope.
 
 .. code-block:: cmake
 
-  set_property(<GLOBAL                      |
-                DIRECTORY [<dir>]           |
-                TARGET    [<target1> ...]   |
+  set_property(<GLOBAL                                     |
+                DIRECTORY [<dir>]                          |
+                TARGET    [<target1> ...]                  |
+                FILE_SET  [<file_set> ...] TARGET <target> |
                 SOURCE    [<src1> ...]
                           [DIRECTORY <dirs> ...]
                           [TARGET_DIRECTORY <targets> ...] |
-                INSTALL   [<file1> ...]     |
+                INSTALL   [<file1> ...]                    |
                 TEST      [<test1> ...]
-                          [DIRECTORY <dir>] |
-                CACHE     [<entry1> ...]    >
+                          [DIRECTORY <dir>]                |
+                CACHE     [<entry1> ...]>
                [APPEND] [APPEND_STRING]
                PROPERTY <name> [<value1> ...])
 
@@ -41,6 +42,16 @@ It must be one of the following:
 
   :ref:`Alias Targets` do not support setting target properties.
 
+``FILE_SET``
+  .. versionadded:: 4.3
+
+  Scope may name zero or more existing file sets.
+
+  The following option is required:
+
+  ``TARGET <target>``
+    The target to which the file set is attached.
+
 ``SOURCE``
   Scope may name zero or more source files.  By default, source file properties
   are only visible to targets added in the same directory (``CMakeLists.txt``).

+ 4 - 0
Help/release/dev/FILE_SET-properties.rst

@@ -0,0 +1,4 @@
+FILE_SET-properties
+-------------------
+
+* The ``FILE_SET`` type gained the support of properties.

+ 94 - 4
Source/cmFileSet.cxx

@@ -88,9 +88,9 @@ bool cmFileSetTypeCanBeIncluded(std::string const& type)
   return type == "HEADERS"_s;
 }
 
-cmFileSet::cmFileSet(cmake& cmakeInstance, std::string name, std::string type,
+cmFileSet::cmFileSet(cmMakefile* makefile, std::string name, std::string type,
                      cmFileSetVisibility visibility)
-  : CMakeInstance(cmakeInstance)
+  : Makefile(makefile)
   , Name(std::move(name))
   , Type(std::move(type))
   , Visibility(visibility)
@@ -130,7 +130,8 @@ cmFileSet::CompileFileEntries() const
 
   for (auto const& entry : this->FileEntries) {
     for (auto const& ex : cmList{ entry.Value }) {
-      cmGeneratorExpression ge(this->CMakeInstance, entry.Backtrace);
+      cmGeneratorExpression ge(*this->GetMakefile()->GetCMakeInstance(),
+                               entry.Backtrace);
       auto cge = ge.Parse(ex);
       result.push_back(std::move(cge));
     }
@@ -146,7 +147,8 @@ cmFileSet::CompileDirectoryEntries() const
 
   for (auto const& entry : this->DirectoryEntries) {
     for (auto const& ex : cmList{ entry.Value }) {
-      cmGeneratorExpression ge(this->CMakeInstance, entry.Backtrace);
+      cmGeneratorExpression ge(*this->GetMakefile()->GetCMakeInstance(),
+                               entry.Backtrace);
       auto cge = ge.Parse(ex);
       result.push_back(std::move(cge));
     }
@@ -261,3 +263,91 @@ bool cmFileSet::IsValidName(std::string const& name)
   cmsys::RegularExpressionMatch match;
   return regex.find(name.c_str(), match);
 }
+
+std::string const cmFileSet::propCOMPILE_DEFINITIONS = "COMPILE_DEFINITIONS";
+std::string const cmFileSet::propCOMPILE_OPTIONS = "COMPILE_OPTIONS";
+std::string const cmFileSet::propINCLUDE_DIRECTORIES = "INCLUDE_DIRECTORIES";
+
+void cmFileSet::SetProperty(std::string const& prop, cmValue value)
+{
+  if (prop == propINCLUDE_DIRECTORIES) {
+    this->IncludeDirectories.clear();
+    if (value) {
+      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
+      this->IncludeDirectories.emplace_back(value, lfbt);
+    }
+  } else if (prop == propCOMPILE_OPTIONS) {
+    this->CompileOptions.clear();
+    if (value) {
+      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
+      this->CompileOptions.emplace_back(value, lfbt);
+    }
+  } else if (prop == propCOMPILE_DEFINITIONS) {
+    this->CompileDefinitions.clear();
+    if (value) {
+      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
+      this->CompileDefinitions.emplace_back(value, lfbt);
+    }
+  } else {
+    this->Properties.SetProperty(prop, value);
+  }
+}
+
+void cmFileSet::AppendProperty(std::string const& prop,
+                               std::string const& value, bool asString)
+{
+  if (prop == propINCLUDE_DIRECTORIES) {
+    if (!value.empty()) {
+      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
+      this->IncludeDirectories.emplace_back(value, lfbt);
+    }
+  } else if (prop == propCOMPILE_OPTIONS) {
+    if (!value.empty()) {
+      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
+      this->CompileOptions.emplace_back(value, lfbt);
+    }
+  } else if (prop == propCOMPILE_DEFINITIONS) {
+    if (!value.empty()) {
+      cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace();
+      this->CompileDefinitions.emplace_back(value, lfbt);
+    }
+  } else {
+    this->Properties.AppendProperty(prop, value, asString);
+  }
+}
+
+cmValue cmFileSet::GetProperty(std::string const& prop) const
+{
+  // Check for the properties with backtraces.
+  if (prop == propINCLUDE_DIRECTORIES) {
+    if (this->IncludeDirectories.empty()) {
+      return nullptr;
+    }
+
+    static std::string output;
+    output = cmList::to_string(this->IncludeDirectories);
+    return cmValue(output);
+  }
+
+  if (prop == propCOMPILE_OPTIONS) {
+    if (this->CompileOptions.empty()) {
+      return nullptr;
+    }
+
+    static std::string output;
+    output = cmList::to_string(this->CompileOptions);
+    return cmValue(output);
+  }
+
+  if (prop == propCOMPILE_DEFINITIONS) {
+    if (this->CompileDefinitions.empty()) {
+      return nullptr;
+    }
+
+    static std::string output;
+    output = cmList::to_string(this->CompileDefinitions);
+    return cmValue(output);
+  }
+
+  return this->Properties.GetPropertyValue(prop);
+}

+ 33 - 3
Source/cmFileSet.h

@@ -2,6 +2,7 @@
    file LICENSE.rst or https://cmake.org/licensing for details.  */
 #pragma once
 
+#include <cstddef>
 #include <map>
 #include <memory>
 #include <string>
@@ -11,6 +12,8 @@
 #include <cmext/string_view>
 
 #include "cmListFileCache.h"
+#include "cmPropertyMap.h"
+#include "cmValue.h"
 
 namespace cm {
 namespace GenEx {
@@ -22,7 +25,6 @@ class cmCompiledGeneratorExpression;
 struct cmGeneratorExpressionDAGChecker;
 class cmGeneratorTarget;
 class cmMakefile;
-class cmake;
 
 enum class cmFileSetVisibility
 {
@@ -41,13 +43,15 @@ bool cmFileSetTypeCanBeIncluded(std::string const& type);
 class cmFileSet
 {
 public:
-  cmFileSet(cmake& cmakeInstance, std::string name, std::string type,
+  cmFileSet(cmMakefile* makefile, std::string name, std::string type,
             cmFileSetVisibility visibility);
 
   std::string const& GetName() const { return this->Name; }
   std::string const& GetType() const { return this->Type; }
   cmFileSetVisibility GetVisibility() const { return this->Visibility; }
 
+  cmMakefile* GetMakefile() const { return this->Makefile; }
+
   void CopyEntries(cmFileSet const* fs);
 
   void ClearDirectoryEntries();
@@ -84,11 +88,37 @@ public:
 
   static bool IsValidName(std::string const& name);
 
+  //! Set/Get a property of this file set
+  void SetProperty(std::string const& prop, cmValue value);
+  void SetProperty(std::string const& prop, std::nullptr_t)
+  {
+    this->SetProperty(prop, cmValue{ nullptr });
+  }
+  void RemoveProperty(std::string const& prop)
+  {
+    this->SetProperty(prop, cmValue{ nullptr });
+  }
+  void SetProperty(std::string const& prop, std::string const& value)
+  {
+    this->SetProperty(prop, cmValue{ value });
+  }
+  void AppendProperty(std::string const& prop, std::string const& value,
+                      bool asString = false);
+  cmValue GetProperty(std::string const& prop) const;
+
 private:
-  cmake& CMakeInstance;
+  cmMakefile* Makefile;
   std::string Name;
   std::string Type;
   cmFileSetVisibility Visibility;
   std::vector<BT<std::string>> DirectoryEntries;
   std::vector<BT<std::string>> FileEntries;
+  cmPropertyMap Properties;
+  std::vector<BT<std::string>> CompileOptions;
+  std::vector<BT<std::string>> CompileDefinitions;
+  std::vector<BT<std::string>> IncludeDirectories;
+
+  static std::string const propCOMPILE_DEFINITIONS;
+  static std::string const propCOMPILE_OPTIONS;
+  static std::string const propINCLUDE_DIRECTORIES;
 };

+ 73 - 27
Source/cmGetPropertyCommand.cxx

@@ -9,6 +9,7 @@
 #include <cmext/string_view>
 
 #include "cmExecutionStatus.h"
+#include "cmFileSet.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstalledFile.h"
 #include "cmMakefile.h"
@@ -45,6 +46,9 @@ bool HandleDirectoryMode(cmExecutionStatus& status, std::string const& name,
 bool HandleTargetMode(cmExecutionStatus& status, std::string const& name,
                       OutType infoType, std::string const& variable,
                       std::string const& propertyName);
+bool HandleFileSetMode(cmExecutionStatus& status, std::string const& name,
+                       OutType infoType, std::string const& variable,
+                       std::string const& propertyName, cmTarget* target);
 bool HandleSourceMode(cmExecutionStatus& status, std::string const& name,
                       OutType infoType, std::string const& variable,
                       std::string const& propertyName,
@@ -80,6 +84,9 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args,
   std::string name;
   std::string propertyName;
 
+  std::string file_set_target_name;
+  bool file_set_target_option_enabled = false;
+
   std::vector<std::string> source_file_directories;
   std::vector<std::string> source_file_target_directories;
   bool source_file_directory_option_enabled = false;
@@ -96,6 +103,8 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args,
     scope = cmProperty::DIRECTORY;
   } else if (args[1] == "TARGET") {
     scope = cmProperty::TARGET;
+  } else if (args[1] == "FILE_SET") {
+    scope = cmProperty::FILE_SET;
   } else if (args[1] == "SOURCE") {
     scope = cmProperty::SOURCE_FILE;
   } else if (args[1] == "TEST") {
@@ -107,11 +116,11 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args,
   } else if (args[1] == "INSTALL") {
     scope = cmProperty::INSTALL;
   } else {
-    status.SetError(cmStrCat(
-      "given invalid scope ", args[1],
-      ".  "
-      "Valid scopes are "
-      "GLOBAL, DIRECTORY, TARGET, SOURCE, TEST, VARIABLE, CACHE, INSTALL."));
+    status.SetError(cmStrCat("given invalid scope ", args[1],
+                             ".  "
+                             "Valid scopes are "
+                             "GLOBAL, DIRECTORY, TARGET, FILE_SET, SOURCE, "
+                             "TEST, VARIABLE, CACHE, INSTALL."));
     return false;
   }
 
@@ -122,6 +131,7 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args,
     DoingName,
     DoingProperty,
     DoingType,
+    DoingFileSetTarget,
     DoingSourceDirectory,
     DoingSourceTargetDirectory,
     DoingTestDirectory,
@@ -145,6 +155,10 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args,
     } else if (doing == DoingName) {
       doing = DoingNone;
       name = args[i];
+    } else if (doing == DoingNone && scope == cmProperty::FILE_SET &&
+               args[i] == "TARGET") {
+      doing = DoingFileSetTarget;
+      file_set_target_option_enabled = true;
     } else if (doing == DoingNone && scope == cmProperty::SOURCE_FILE &&
                args[i] == "DIRECTORY") {
       doing = DoingSourceDirectory;
@@ -157,6 +171,9 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args,
                args[i] == "DIRECTORY") {
       doing = DoingTestDirectory;
       test_directory_option_enabled = true;
+    } else if (doing == DoingFileSetTarget) {
+      file_set_target_name = args[i];
+      doing = DoingNone;
     } else if (doing == DoingSourceDirectory) {
       source_file_directories.push_back(args[i]);
       doing = DoingNone;
@@ -181,21 +198,6 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  std::vector<cmMakefile*> source_file_directory_makefiles;
-  bool source_file_scopes_handled =
-    SetPropertyCommand::HandleAndValidateSourceFileDirectoryScopes(
-      status, source_file_directory_option_enabled,
-      source_file_target_option_enabled, source_file_directories,
-      source_file_target_directories, source_file_directory_makefiles);
-  cmMakefile* test_directory_makefile;
-  bool test_scopes_handled =
-    SetPropertyCommand::HandleAndValidateTestDirectoryScopes(
-      status, test_directory_option_enabled, test_directory,
-      test_directory_makefile);
-  if (!(source_file_scopes_handled && test_scopes_handled)) {
-    return false;
-  }
-
   // Compute requested output.
   if (infoType == OutBriefDoc) {
     // Lookup brief documentation.
@@ -231,11 +233,6 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args,
     }
   } else {
     // Dispatch property getting.
-    cmMakefile& directory_scope_mf = *(source_file_directory_makefiles[0]);
-    bool source_file_paths_should_be_absolute =
-      source_file_directory_option_enabled ||
-      source_file_target_option_enabled;
-
     switch (scope) {
       case cmProperty::GLOBAL:
         return HandleGlobalMode(status, name, infoType, variable,
@@ -246,13 +243,43 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args,
       case cmProperty::TARGET:
         return HandleTargetMode(status, name, infoType, variable,
                                 propertyName);
-      case cmProperty::SOURCE_FILE:
+      case cmProperty::FILE_SET: {
+        cmTarget* file_set_target;
+        if (!SetPropertyCommand::HandleAndValidateFileSetTargetScopes(
+              status, file_set_target_option_enabled, file_set_target_name,
+              file_set_target)) {
+          return false;
+        }
+        return HandleFileSetMode(status, name, infoType, variable,
+                                 propertyName, file_set_target);
+      }
+      case cmProperty::SOURCE_FILE: {
+        std::vector<cmMakefile*> source_file_directory_makefiles;
+        if (!SetPropertyCommand::HandleAndValidateSourceFileDirectoryScopes(
+              status, source_file_directory_option_enabled,
+              source_file_target_option_enabled, source_file_directories,
+              source_file_target_directories,
+              source_file_directory_makefiles)) {
+          return false;
+        }
+        bool source_file_paths_should_be_absolute =
+          source_file_directory_option_enabled ||
+          source_file_target_option_enabled;
+        cmMakefile& directory_scope_mf = *(source_file_directory_makefiles[0]);
         return HandleSourceMode(status, name, infoType, variable, propertyName,
                                 directory_scope_mf,
                                 source_file_paths_should_be_absolute);
-      case cmProperty::TEST:
+      }
+      case cmProperty::TEST: {
+        cmMakefile* test_directory_makefile;
+        if (!SetPropertyCommand::HandleAndValidateTestDirectoryScopes(
+              status, test_directory_option_enabled, test_directory,
+              test_directory_makefile)) {
+          return false;
+        }
         return HandleTestMode(status, name, infoType, variable, propertyName,
                               *test_directory_makefile);
+      }
       case cmProperty::VARIABLE:
         return HandleVariableMode(status, name, infoType, variable,
                                   propertyName);
@@ -413,6 +440,25 @@ bool HandleTargetMode(cmExecutionStatus& status, std::string const& name,
   return false;
 }
 
+bool HandleFileSetMode(cmExecutionStatus& status, std::string const& name,
+                       OutType infoType, std::string const& variable,
+                       std::string const& propertyName, cmTarget* target)
+{
+  if (name.empty()) {
+    status.SetError("not given name for FILE_SET scope.");
+    return false;
+  }
+
+  if (cmFileSet* fileSet = target->GetFileSet(name)) {
+    cmValue prop = fileSet->GetProperty(propertyName);
+    return StoreResult(infoType, status.GetMakefile(), variable, prop);
+  }
+  status.SetError(cmStrCat("could not find FILE_SET ", name, " for TARGET ",
+                           target->GetName(),
+                           ".  Perhaps it has not yet been created."));
+  return false;
+}
+
 bool HandleSourceMode(cmExecutionStatus& status, std::string const& name,
                       OutType infoType, std::string const& variable,
                       std::string const& propertyName,

+ 2 - 1
Source/cmProperty.h

@@ -17,6 +17,7 @@ public:
     TEST,
     VARIABLE,
     CACHED_VARIABLE,
-    INSTALL
+    INSTALL,
+    FILE_SET
   };
 };

+ 144 - 22
Source/cmSetPropertyCommand.cxx

@@ -10,6 +10,7 @@
 #include <cm/string_view>
 
 #include "cmExecutionStatus.h"
+#include "cmFileSet.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstalledFile.h"
 #include "cmListFileCache.h"
@@ -48,6 +49,14 @@ bool HandleTarget(cmTarget* target, cmMakefile& makefile,
                   std::string const& propertyName,
                   std::string const& propertyValue, bool appendAsString,
                   bool appendMode, bool remove);
+bool HandleFileSetMode(cmExecutionStatus& status,
+                       std::set<std::string> const& names,
+                       std::string const& propertyName,
+                       std::string const& propertyValue, bool appendAsString,
+                       bool appendMode, bool remove, cmTarget* target);
+bool HandleFileSet(cmFileSet* fileSet, std::string const& propertyName,
+                   std::string const& propertyValue, bool appendAsString,
+                   bool appendMode, bool remove);
 bool HandleSourceMode(cmExecutionStatus& status,
                       std::set<std::string> const& names,
                       std::string const& propertyName,
@@ -87,6 +96,54 @@ bool HandleInstall(cmInstalledFile* file, cmMakefile& makefile,
 }
 
 namespace SetPropertyCommand {
+bool HandleFileSetTargetScopes(cmExecutionStatus& status,
+                               std::string& file_set_target_name,
+                               cmTarget*& file_set_target)
+{
+  file_set_target = status.GetMakefile().FindTargetToUse(file_set_target_name);
+  if (!file_set_target) {
+    status.SetError(
+      cmStrCat("given non-existent TARGET ", file_set_target_name));
+    return false;
+  }
+  return true;
+}
+
+bool HandleFileSetTargetScopeValidation(cmExecutionStatus& status,
+                                        bool file_set_target_option_enabled,
+                                        std::string& file_set_target_name)
+{
+  if (!file_set_target_option_enabled) {
+    status.SetError("required TARGET option is missing");
+    return false;
+  }
+
+  // Validate file set target scopes.
+  if (file_set_target_name.empty()) {
+    status.SetError("called with incorrect number of arguments "
+                    "no value provided to the TARGET option");
+    return false;
+  }
+  return true;
+}
+
+bool HandleAndValidateFileSetTargetScopes(cmExecutionStatus& status,
+                                          bool file_set_target_option_enabled,
+                                          std::string& file_set_target_name,
+                                          cmTarget*& file_set_target)
+{
+  bool scope_options_valid =
+    SetPropertyCommand::HandleFileSetTargetScopeValidation(
+      status, file_set_target_option_enabled, file_set_target_name);
+  if (!scope_options_valid) {
+    return false;
+  }
+
+  scope_options_valid = SetPropertyCommand::HandleFileSetTargetScopes(
+    status, file_set_target_name, file_set_target);
+  return scope_options_valid;
+}
+
 bool HandleSourceFileDirectoryScopes(
   cmExecutionStatus& status, std::vector<std::string>& source_file_directories,
   std::vector<std::string>& source_file_target_directories,
@@ -402,6 +459,8 @@ bool cmSetPropertyCommand(std::vector<std::string> const& args,
     scope = cmProperty::DIRECTORY;
   } else if (scopeName == "TARGET") {
     scope = cmProperty::TARGET;
+  } else if (scopeName == "FILE_SET") {
+    scope = cmProperty::FILE_SET;
   } else if (scopeName == "SOURCE") {
     scope = cmProperty::SOURCE_FILE;
   } else if (scopeName == "TEST") {
@@ -412,9 +471,8 @@ bool cmSetPropertyCommand(std::vector<std::string> const& args,
     scope = cmProperty::INSTALL;
   } else {
     status.SetError(cmStrCat("given invalid scope ", scopeName,
-                             ".  "
-                             "Valid scopes are GLOBAL, DIRECTORY, "
-                             "TARGET, SOURCE, TEST, CACHE, INSTALL."));
+                             ".  Valid scopes are GLOBAL, DIRECTORY, TARGET, "
+                             "FILE_SET, SOURCE, TEST, CACHE, INSTALL."));
     return false;
   }
 
@@ -425,6 +483,9 @@ bool cmSetPropertyCommand(std::vector<std::string> const& args,
   std::string propertyName;
   std::string propertyValue;
 
+  std::string file_set_target_name;
+  bool file_set_target_option_enabled = false;
+
   std::vector<std::string> source_file_directories;
   std::vector<std::string> source_file_target_directories;
   bool source_file_directory_option_enabled = false;
@@ -440,6 +501,7 @@ bool cmSetPropertyCommand(std::vector<std::string> const& args,
     DoingNames,
     DoingProperty,
     DoingValues,
+    DoingFileSetTarget,
     DoingSourceDirectory,
     DoingSourceTargetDirectory,
     DoingTestDirectory,
@@ -459,6 +521,10 @@ bool cmSetPropertyCommand(std::vector<std::string> const& args,
       appendMode = true;
       remove = false;
       appendAsString = true;
+    } else if (doing != DoingProperty && doing != DoingValues &&
+               scope == cmProperty::FILE_SET && arg == "TARGET") {
+      doing = DoingFileSetTarget;
+      file_set_target_option_enabled = true;
     } else if (doing != DoingProperty && doing != DoingValues &&
                scope == cmProperty::SOURCE_FILE && arg == "DIRECTORY") {
       doing = DoingSourceDirectory;
@@ -473,6 +539,10 @@ bool cmSetPropertyCommand(std::vector<std::string> const& args,
       test_directory_option_enabled = true;
     } else if (doing == DoingNames) {
       names.insert(arg);
+    } else if (doing == DoingFileSetTarget) {
+      file_set_target_name = arg;
+      file_set_target_option_enabled = true;
+      doing = DoingNone;
     } else if (doing == DoingSourceDirectory) {
       source_file_directories.push_back(arg);
     } else if (doing == DoingSourceTargetDirectory) {
@@ -500,23 +570,6 @@ bool cmSetPropertyCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  std::vector<cmMakefile*> source_file_directory_makefiles;
-  bool source_file_scopes_handled =
-    SetPropertyCommand::HandleAndValidateSourceFileDirectoryScopes(
-      status, source_file_directory_option_enabled,
-      source_file_target_option_enabled, source_file_directories,
-      source_file_target_directories, source_file_directory_makefiles);
-  cmMakefile* test_directory_makefile;
-  bool test_scopes_handled =
-    SetPropertyCommand::HandleAndValidateTestDirectoryScopes(
-      status, test_directory_option_enabled, test_directory,
-      test_directory_makefile);
-  if (!(source_file_scopes_handled && test_scopes_handled)) {
-    return false;
-  }
-  bool source_file_paths_should_be_absolute =
-    source_file_directory_option_enabled || source_file_target_option_enabled;
-
   // Dispatch property setting.
   switch (scope) {
     case cmProperty::GLOBAL:
@@ -528,15 +581,44 @@ bool cmSetPropertyCommand(std::vector<std::string> const& args,
     case cmProperty::TARGET:
       return HandleTargetMode(status, names, propertyName, propertyValue,
                               appendAsString, appendMode, remove);
-    case cmProperty::SOURCE_FILE:
+    case cmProperty::FILE_SET: {
+      cmTarget* file_set_target;
+      if (!SetPropertyCommand::HandleAndValidateFileSetTargetScopes(
+            status, file_set_target_option_enabled, file_set_target_name,
+            file_set_target)) {
+        return false;
+      }
+      return HandleFileSetMode(status, names, propertyName, propertyValue,
+                               appendAsString, appendMode, remove,
+                               file_set_target);
+    }
+    case cmProperty::SOURCE_FILE: {
+      std::vector<cmMakefile*> source_file_directory_makefiles;
+      if (!SetPropertyCommand::HandleAndValidateSourceFileDirectoryScopes(
+            status, source_file_directory_option_enabled,
+            source_file_target_option_enabled, source_file_directories,
+            source_file_target_directories, source_file_directory_makefiles)) {
+        return false;
+      }
+      bool source_file_paths_should_be_absolute =
+        source_file_directory_option_enabled ||
+        source_file_target_option_enabled;
       return HandleSourceMode(status, names, propertyName, propertyValue,
                               appendAsString, appendMode, remove,
                               source_file_directory_makefiles,
                               source_file_paths_should_be_absolute);
-    case cmProperty::TEST:
+    }
+    case cmProperty::TEST: {
+      cmMakefile* test_directory_makefile;
+      if (!SetPropertyCommand::HandleAndValidateTestDirectoryScopes(
+            status, test_directory_option_enabled, test_directory,
+            test_directory_makefile)) {
+        return false;
+      }
       return HandleTestMode(status, names, propertyName, propertyValue,
                             appendAsString, appendMode, remove,
                             test_directory_makefile);
+    }
     case cmProperty::CACHE:
       return HandleCacheMode(status, names, propertyName, propertyValue,
                              appendAsString, appendMode, remove);
@@ -679,6 +761,46 @@ bool HandleTarget(cmTarget* target, cmMakefile& makefile,
   return true;
 }
 
+bool HandleFileSetMode(cmExecutionStatus& status,
+                       std::set<std::string> const& names,
+                       std::string const& propertyName,
+                       std::string const& propertyValue, bool appendAsString,
+                       bool appendMode, bool remove, cmTarget* target)
+{
+  for (std::string const& name : names) {
+    if (cmFileSet* fileSet = target->GetFileSet(name)) {
+      // Handle the current file set.
+      if (!HandleFileSet(fileSet, propertyName, propertyValue, appendAsString,
+                         appendMode, remove)) {
+        return false;
+      }
+    } else {
+      status.SetError(cmStrCat("could not find FILE_SET ", name,
+                               " for TARGET ", target->GetName(),
+                               ".  Perhaps it has not yet been created."));
+      return false;
+    }
+  }
+  return true;
+}
+
+bool HandleFileSet(cmFileSet* fileSet, std::string const& propertyName,
+                   std::string const& propertyValue, bool appendAsString,
+                   bool appendMode, bool remove)
+{
+  // Set or append the property.
+  if (appendMode) {
+    fileSet->AppendProperty(propertyName, propertyValue, appendAsString);
+  } else {
+    if (remove) {
+      fileSet->SetProperty(propertyName, nullptr);
+    } else {
+      fileSet->SetProperty(propertyName, propertyValue);
+    }
+  }
+  return true;
+}
+
 bool HandleSourceMode(cmExecutionStatus& status,
                       std::set<std::string> const& names,
                       std::string const& propertyName,

+ 14 - 0
Source/cmSetPropertyCommand.h

@@ -10,11 +10,25 @@
 class cmMakefile;
 class cmExecutionStatus;
 class cmSourceFile;
+class cmTarget;
 
 bool cmSetPropertyCommand(std::vector<std::string> const& args,
                           cmExecutionStatus& status);
 
 namespace SetPropertyCommand {
+bool HandleFileSetTargetScopes(cmExecutionStatus& status,
+                               std::string& file_set_target_name,
+                               cmTarget*& file_set_target);
+
+bool HandleFileSetTargetScopeValidation(cmExecutionStatus& status,
+                                        bool file_set_target_option_enabled,
+                                        std::string& file_set_target_name);
+
+bool HandleAndValidateFileSetTargetScopes(cmExecutionStatus& status,
+                                          bool file_set_target_option_enabled,
+                                          std::string& file_set_target_name,
+                                          cmTarget*& file_set_target);
+
 bool HandleSourceFileDirectoryScopes(
   cmExecutionStatus& status, std::vector<std::string>& source_file_directories,
   std::vector<std::string>& source_file_target_directories,

+ 1 - 2
Source/cmTarget.cxx

@@ -3138,8 +3138,7 @@ std::pair<cmFileSet*, bool> cmTarget::GetOrCreateFileSet(
   std::string const& name, std::string const& type, cmFileSetVisibility vis)
 {
   auto result = this->impl->FileSets.emplace(
-    name,
-    cmFileSet(*this->GetMakefile()->GetCMakeInstance(), name, type, vis));
+    name, cmFileSet(this->GetMakefile(), name, type, vis));
   if (result.second) {
     auto bt = this->impl->Makefile->GetBacktrace();
     if (type == this->impl->HeadersFileSets.TypeName) {

+ 6 - 4
Tests/CMakeLib/testDebuggerVariablesHelper.cxx

@@ -499,9 +499,10 @@ static bool testCreateFromFileSet()
 {
   auto variablesManager =
     std::make_shared<cmDebugger::cmDebuggerVariablesManager>();
+  auto dummies = CreateDummies("Foo");
 
-  cmake cm(cmState::Role::Internal);
-  cmFileSet fileSet(cm, "Foo", "HEADERS", cmFileSetVisibility::Public);
+  cmFileSet fileSet(dummies.Makefile.get(), "Foo", "HEADERS",
+                    cmFileSetVisibility::Public);
   BT<std::string> directory;
   directory.Value = "c:/";
   fileSet.AddDirectoryEntry(directory);
@@ -543,9 +544,10 @@ static bool testCreateFromFileSets()
 {
   auto variablesManager =
     std::make_shared<cmDebugger::cmDebuggerVariablesManager>();
+  auto dummies = CreateDummies("Foo");
 
-  cmake cm(cmState::Role::Internal);
-  cmFileSet fileSet(cm, "Foo", "HEADERS", cmFileSetVisibility::Public);
+  cmFileSet fileSet(dummies.Makefile.get(), "Foo", "HEADERS",
+                    cmFileSetVisibility::Public);
   BT<std::string> directory;
   directory.Value = "c:/";
   fileSet.AddDirectoryEntry(directory);

+ 1 - 0
Tests/RunCMake/get_property/BadFileSet-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/get_property/BadFileSet-stderr.txt

@@ -0,0 +1,5 @@
+CMake Error at BadFileSet\.cmake:[0-9]+ \(get_property\):
+  get_property could not find FILE_SET FOO for TARGET FOO\.  Perhaps it has
+  not yet been created.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)

+ 3 - 0
Tests/RunCMake/get_property/BadFileSet.cmake

@@ -0,0 +1,3 @@
+add_library(FOO INTERFACE)
+
+get_property(FOO FILE_SET FOO TARGET FOO PROPERTY FOO)

+ 1 - 0
Tests/RunCMake/get_property/BadFileSetTarget-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/get_property/BadFileSetTarget-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at BadFileSetTarget\.cmake:[0-9]+ \(get_property\):
+  get_property given non-existent TARGET FOO
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)

+ 1 - 0
Tests/RunCMake/get_property/BadFileSetTarget.cmake

@@ -0,0 +1 @@
+get_property(FOO FILE_SET FOO TARGET FOO PROPERTY FOO)

+ 1 - 1
Tests/RunCMake/get_property/BadScope-stderr.txt

@@ -1,5 +1,5 @@
 ^CMake Error at BadScope\.cmake:1 \(get_property\):
   get_property given invalid scope FOO\.  Valid scopes are GLOBAL, DIRECTORY,
-  TARGET, SOURCE, TEST, VARIABLE, CACHE, INSTALL\.
+  TARGET, FILE_SET, SOURCE, TEST, VARIABLE, CACHE, INSTALL\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$

+ 2 - 0
Tests/RunCMake/get_property/RunCMakeTest.cmake

@@ -26,6 +26,8 @@ run_cmake(NoTarget)
 run_cmake(NoSource)
 run_cmake(NoProperty)
 run_cmake(NoCache)
+run_cmake(BadFileSet)
+run_cmake(BadFileSetTarget)
 
 # Since we are testing the GENERATOR_IS_MULTI_CONFIG property itself,
 # don't rely on RunCMake_GENERATOR_IS_MULTI_CONFIG being set correctly

+ 1 - 0
Tests/RunCMake/set_property/COMPILE_DEFINITIONS-stdout.txt

@@ -1,2 +1,3 @@
 -- Target COMPILE_DEFINITIONS is 'a;b;c;d;;e'
 -- Directory COMPILE_DEFINITIONS is 'a;b;c;d;;e'
+-- FileSet COMPILE_DEFINITIONS is 'a;b;c;d;;e'

+ 1 - 0
Tests/RunCMake/set_property/COMPILE_DEFINITIONS.cmake

@@ -1,3 +1,4 @@
 include(Common.cmake)
 test_target_property(COMPILE_DEFINITIONS)
 test_directory_property(COMPILE_DEFINITIONS)
+test_file_set_property(COMPILE_DEFINITIONS)

+ 1 - 0
Tests/RunCMake/set_property/COMPILE_OPTIONS-stdout.txt

@@ -1,2 +1,3 @@
 -- Target COMPILE_OPTIONS is 'a;b;c;d;;e'
 -- Directory COMPILE_OPTIONS is 'a;b;c;d;;e'
+-- FileSet COMPILE_OPTIONS is 'a;b;c;d;;e'

+ 1 - 0
Tests/RunCMake/set_property/COMPILE_OPTIONS.cmake

@@ -1,3 +1,4 @@
 include(Common.cmake)
 test_target_property(COMPILE_OPTIONS)
 test_directory_property(COMPILE_OPTIONS)
+test_file_set_property(COMPILE_OPTIONS)

+ 16 - 0
Tests/RunCMake/set_property/Common.cmake

@@ -26,3 +26,19 @@ macro(test_directory_property PROP)
   message(STATUS "Directory ${PROP} is '${val}'")
   set_property(DIRECTORY PROPERTY ${PROP})
 endmacro()
+
+macro(test_file_set_property PROP)
+  add_library(foo INTERFACE)
+  target_sources(foo INTERFACE FILE_SET foo TYPE HEADERS)
+  set_property(FILE_SET foo TARGET foo PROPERTY ${PROP} x)
+  set_property(FILE_SET foo TARGET foo PROPERTY ${PROP})
+  set_property(FILE_SET foo TARGET foo APPEND PROPERTY ${PROP})
+  set_property(FILE_SET foo TARGET foo PROPERTY ${PROP} a)
+  set_property(FILE_SET foo TARGET foo APPEND PROPERTY ${PROP} "")
+  set_property(FILE_SET foo TARGET foo APPEND PROPERTY ${PROP} b c)
+  set_property(FILE_SET foo TARGET foo APPEND PROPERTY ${PROP})
+  set_property(FILE_SET foo TARGET foo APPEND PROPERTY ${PROP} "d;;e")
+  get_property(val FILE_SET foo TARGET foo PROPERTY ${PROP})
+  message(STATUS "FileSet ${PROP} is '${val}'")
+  set_property(FILE_SET foo TARGET foo PROPERTY ${PROP})
+endmacro()

+ 1 - 0
Tests/RunCMake/set_property/FILE_SET-PROPERTY-missing-result.txt

@@ -0,0 +1 @@
+1

+ 10 - 0
Tests/RunCMake/set_property/FILE_SET-PROPERTY-missing-stderr.txt

@@ -0,0 +1,10 @@
+CMake Error at FILE_SET-PROPERTY-missing\.cmake:5 \(set_property\):
+  set_property not given a PROPERTY <name> argument\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+
+
+CMake Error at FILE_SET-PROPERTY-missing\.cmake:7 \(set_property\):
+  set_property not given a PROPERTY <name> argument\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)

+ 7 - 0
Tests/RunCMake/set_property/FILE_SET-PROPERTY-missing.cmake

@@ -0,0 +1,7 @@
+
+add_library(foo INTERFACE)
+target_sources(foo INTERFACE FILE_SET foo TYPE HEADERS)
+
+set_property(FILE_SET foo TARGET foo)
+
+set_property(FILE_SET foo TARGET foo PROPERTY)

+ 1 - 0
Tests/RunCMake/set_property/FILE_SET-TARGET-invalid-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/set_property/FILE_SET-TARGET-invalid-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at FILE_SET-TARGET-invalid\.cmake:[0-9]+ \(set_property\):
+  set_property given non-existent TARGET foo
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)

+ 2 - 0
Tests/RunCMake/set_property/FILE_SET-TARGET-invalid.cmake

@@ -0,0 +1,2 @@
+
+set_property(FILE_SET foo TARGET foo PROPERTY PROP VALUE)

+ 1 - 0
Tests/RunCMake/set_property/FILE_SET-invalid-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/set_property/FILE_SET-invalid-stderr.txt

@@ -0,0 +1,5 @@
+CMake Error at FILE_SET-invalid\.cmake:[0-9]+ \(set_property\):
+  set_property could not find FILE_SET foo for TARGET foo\.  Perhaps it has
+  not yet been created.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)

+ 4 - 0
Tests/RunCMake/set_property/FILE_SET-invalid.cmake

@@ -0,0 +1,4 @@
+
+add_library(foo SHARED)
+
+set_property(FILE_SET foo TARGET foo PROPERTY PROP VALUE)

+ 30 - 0
Tests/RunCMake/set_property/FILE_SET.cmake

@@ -0,0 +1,30 @@
+
+add_library(foo INTERFACE)
+
+target_sources(foo INTERFACE FILE_SET foo1 TYPE HEADERS)
+target_sources(foo INTERFACE FILE_SET foo2 TYPE HEADERS)
+
+# check various supported syntax
+set_property(FILE_SET TARGET foo PROPERTY FOO)
+set_property(FILE_SET TARGET foo PROPERTY FOO BAR)
+set_property(FILE_SET TARGET foo PROPERTY FOO BAR BAR2)
+
+set_property(FILE_SET foo1 TARGET foo PROPERTY FOO)
+set_property(FILE_SET foo1 TARGET foo PROPERTY FOO BAR)
+set_property(FILE_SET foo1 TARGET foo PROPERTY FOO BAR BAR2)
+get_property(value FILE_SET foo1 TARGET foo PROPERTY FOO)
+if(NOT value STREQUAL "BAR;BAR2")
+  message(SEND_ERROR "FILE_SET foo1 TARGET foo: \"${value}\", expected \"BAR;BAR2\"")
+endif()
+
+set_property(FILE_SET foo1 foo2 TARGET foo PROPERTY FOO)
+set_property(FILE_SET foo1 foo2 TARGET foo PROPERTY FOO BAR)
+set_property(FILE_SET foo1 foo2 TARGET foo PROPERTY FOO BAR BAR2)
+get_property(value FILE_SET foo1 TARGET foo PROPERTY FOO)
+if(NOT value STREQUAL "BAR;BAR2")
+  message(SEND_ERROR "FILE_SET foo1 TARGET foo: \"${value}\", expected \"BAR;BAR2\"")
+endif()
+get_property(value FILE_SET foo2 TARGET foo PROPERTY FOO)
+if(NOT value STREQUAL "BAR;BAR2")
+  message(SEND_ERROR "FILE_SET foo2 TARGET foo: \"${value}\", expected \"BAR;BAR2\"")
+endif()

+ 1 - 0
Tests/RunCMake/set_property/INCLUDE_DIRECTORIES-stdout.txt

@@ -1,2 +1,3 @@
 -- Target INCLUDE_DIRECTORIES is 'a;b;c;d;;e'
 -- Directory INCLUDE_DIRECTORIES is 'a;b;c;d;;e'
+-- FileSet INCLUDE_DIRECTORIES is 'a;b;c;d;;e'

+ 1 - 0
Tests/RunCMake/set_property/INCLUDE_DIRECTORIES.cmake

@@ -1,3 +1,4 @@
 include(Common.cmake)
 test_target_property(INCLUDE_DIRECTORIES)
 test_directory_property(INCLUDE_DIRECTORIES)
+test_file_set_property(INCLUDE_DIRECTORIES)

+ 5 - 0
Tests/RunCMake/set_property/RunCMakeTest.cmake

@@ -19,3 +19,8 @@ set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/TEST-build")
 run_cmake(TEST)
 set(RunCMake_TEST_NO_CLEAN 1)
 run_cmake_command(TEST-test ${CMAKE_CTEST_COMMAND} -C Debug)
+
+run_cmake(FILE_SET-invalid)
+run_cmake(FILE_SET-TARGET-invalid)
+run_cmake(FILE_SET-PROPERTY-missing)
+run_cmake(FILE_SET)

+ 1 - 0
Tests/RunCMake/set_property/USER_PROP-stdout.txt

@@ -1,2 +1,3 @@
 -- Target USER_PROP is 'a;b;c;d;;e'
 -- Directory USER_PROP is 'a;b;c;d;;e'
+-- FileSet USER_PROP is 'a;b;c;d;;e'

+ 1 - 0
Tests/RunCMake/set_property/USER_PROP.cmake

@@ -1,3 +1,4 @@
 include(Common.cmake)
 test_target_property(USER_PROP)
 test_directory_property(USER_PROP)
+test_file_set_property(USER_PROP)