Browse Source

Merge topic 'file-set-repr-improvements' into release-3.23

5fa15ec9f3 Help: Document that target_sources defines [INTERFACE_]HEADER_SETS
c5d4812f20 cmTarget: make HEADER_SETS and INTERFACE_HEADER_SETS read-only
05783b168d cmFileSet: store visibility with the fileset

Acked-by: Kitware Robot <[email protected]>
Merge-request: !7168
Brad King 3 years ago
parent
commit
56a11b2f64

+ 6 - 7
Help/prop_tgt/HEADER_SETS.rst

@@ -3,14 +3,13 @@ HEADER_SETS
 
 .. versionadded:: 3.23
 
-List of the target's ``PRIVATE`` and ``PUBLIC`` header sets (i.e. all
-file sets with the type ``HEADERS``). Files listed in these file sets
-are treated as source files for the purpose of IDE integration.
-The files also have their :prop_sf:`HEADER_FILE_ONLY` property set to
-``TRUE``.
+Read-only list of the target's ``PRIVATE`` and ``PUBLIC`` header sets (i.e.
+all file sets with the type ``HEADERS``). Files listed in these file sets are
+treated as source files for the purpose of IDE integration. The files also
+have their :prop_sf:`HEADER_FILE_ONLY` property set to ``TRUE``.
 
-This property is normally only set by :command:`target_sources(FILE_SET)`
-rather than being manipulated directly.
+Header sets may be defined using the :command:`target_sources` command
+``FILE_SET`` option with type ``HEADERS``.
 
 See also :prop_tgt:`HEADER_SET_<NAME>`, :prop_tgt:`HEADER_SET` and
 :prop_tgt:`INTERFACE_HEADER_SETS`.

+ 4 - 4
Help/prop_tgt/INTERFACE_HEADER_SETS.rst

@@ -3,12 +3,12 @@ INTERFACE_HEADER_SETS
 
 .. versionadded:: 3.23
 
-List of the target's ``INTERFACE`` and ``PUBLIC`` header sets (i.e. all
-file sets with the type ``HEADERS``). Files listed in these header sets
+Read-only list of the target's ``INTERFACE`` and ``PUBLIC`` header sets (i.e.
+all file sets with the type ``HEADERS``). Files listed in these header sets
 can be installed with :command:`install(TARGETS)` and exported with
 :command:`install(EXPORT)` and :command:`export`.
 
-This property is normally only set by :command:`target_sources(FILE_SET)`
-rather than being manipulated directly.
+Header sets may be defined using the :command:`target_sources` command
+``FILE_SET`` option with type ``HEADERS``.
 
 See also :prop_tgt:`HEADER_SETS`.

+ 6 - 2
Help/release/3.23.rst

@@ -114,8 +114,8 @@ Variables
 Properties
 ----------
 
-* The :prop_tgt:`HEADER_SETS` and :prop_tgt:`INTERFACE_HEADER_SETS` target
-  properties were added to list header sets associated with a target.
+* The :prop_tgt:`HEADER_SETS` and :prop_tgt:`INTERFACE_HEADER_SETS` read-only
+  target properties were added to list header sets associated with a target.
 
 * The :prop_tgt:`HEADER_SET` and :prop_tgt:`HEADER_SET_<NAME>` target
   properties were added to list files in the default header set
@@ -278,3 +278,7 @@ Changes made since CMake 3.23.0 include the following.
   targets.  Pending further work in a future version of CMake, it is now
   an error to add a ``FILE_SET`` of type ``HEADERS`` to such targets on
   Apple platforms.
+
+* The :prop_tgt:`HEADER_SETS` and :prop_tgt:`INTERFACE_HEADER_SETS` target
+  properties added in CMake 3.23.0 are now read-only records of the header
+  sets created by the :command:`target_sources` command.

+ 61 - 1
Source/cmFileSet.cxx

@@ -7,19 +7,79 @@
 #include <utility>
 #include <vector>
 
+#include <cmext/string_view>
+
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmGeneratorExpression.h"
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
+#include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-cmFileSet::cmFileSet(std::string name, std::string type)
+cm::static_string_view cmFileSetVisibilityToName(cmFileSetVisibility vis)
+{
+  switch (vis) {
+    case cmFileSetVisibility::Interface:
+      return "INTERFACE"_s;
+    case cmFileSetVisibility::Public:
+      return "PUBLIC"_s;
+    case cmFileSetVisibility::Private:
+      return "PRIVATE"_s;
+  }
+  return ""_s;
+}
+
+cmFileSetVisibility cmFileSetVisibilityFromName(cm::string_view name,
+                                                cmMakefile* mf)
+{
+  if (name == "INTERFACE"_s) {
+    return cmFileSetVisibility::Interface;
+  }
+  if (name == "PUBLIC"_s) {
+    return cmFileSetVisibility::Public;
+  }
+  if (name == "PRIVATE"_s) {
+    return cmFileSetVisibility::Private;
+  }
+  mf->IssueMessage(
+    MessageType::FATAL_ERROR,
+    cmStrCat("File set visibility \"", name, "\" is not valid."));
+  return cmFileSetVisibility::Private;
+}
+
+bool cmFileSetVisibilityIsForSelf(cmFileSetVisibility vis)
+{
+  switch (vis) {
+    case cmFileSetVisibility::Interface:
+      return false;
+    case cmFileSetVisibility::Public:
+    case cmFileSetVisibility::Private:
+      return true;
+  }
+  return false;
+}
+
+bool cmFileSetVisibilityIsForInterface(cmFileSetVisibility vis)
+{
+  switch (vis) {
+    case cmFileSetVisibility::Interface:
+    case cmFileSetVisibility::Public:
+      return true;
+    case cmFileSetVisibility::Private:
+      return false;
+  }
+  return false;
+}
+
+cmFileSet::cmFileSet(std::string name, std::string type,
+                     cmFileSetVisibility visibility)
   : Name(std::move(name))
   , Type(std::move(type))
+  , Visibility(visibility)
 {
 }
 

+ 20 - 1
Source/cmFileSet.h

@@ -7,20 +7,38 @@
 #include <string>
 #include <vector>
 
+#include <cm/string_view>
+#include <cmext/string_view>
+
 #include "cmListFileCache.h"
 
 class cmCompiledGeneratorExpression;
 struct cmGeneratorExpressionDAGChecker;
 class cmGeneratorTarget;
 class cmLocalGenerator;
+class cmMakefile;
+
+enum class cmFileSetVisibility
+{
+  Private,
+  Public,
+  Interface,
+};
+cm::static_string_view cmFileSetVisibilityToName(cmFileSetVisibility vis);
+cmFileSetVisibility cmFileSetVisibilityFromName(cm::string_view name,
+                                                cmMakefile* mf);
+bool cmFileSetVisibilityIsForSelf(cmFileSetVisibility vis);
+bool cmFileSetVisibilityIsForInterface(cmFileSetVisibility vis);
 
 class cmFileSet
 {
 public:
-  cmFileSet(std::string name, std::string type);
+  cmFileSet(std::string name, std::string type,
+            cmFileSetVisibility visibility);
 
   const std::string& GetName() const { return this->Name; }
   const std::string& GetType() const { return this->Type; }
+  cmFileSetVisibility GetVisibility() const { return this->Visibility; }
 
   void ClearDirectoryEntries();
   void AddDirectoryEntry(BT<std::string> directories);
@@ -61,6 +79,7 @@ public:
 private:
   std::string Name;
   std::string Type;
+  cmFileSetVisibility Visibility;
   std::vector<BT<std::string>> DirectoryEntries;
   std::vector<BT<std::string>> FileEntries;
 };

+ 42 - 55
Source/cmTarget.cxx

@@ -1456,37 +1456,14 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value)
         BT<std::string>(value, this->impl->Makefile->GetBacktrace()));
     }
   } else if (prop == propHEADER_SETS) {
-    if (value) {
-      for (auto const& name : cmExpandedList(value)) {
-        if (!this->GetFileSet(name)) {
-          this->impl->Makefile->IssueMessage(
-            MessageType::FATAL_ERROR,
-            cmStrCat("Header set \"", name, "\" has not yet been created."));
-          return;
-        }
-      }
-    }
-    this->impl->HeaderSetsEntries.clear();
-    if (!StringIsEmpty(value)) {
-      this->impl->HeaderSetsEntries.emplace_back(
-        value, this->impl->Makefile->GetBacktrace());
-    }
+    this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
+                                       "HEADER_SETS property is read-only\n");
+    return;
   } else if (prop == propINTERFACE_HEADER_SETS) {
-    if (value) {
-      for (auto const& name : cmExpandedList(value)) {
-        if (!this->GetFileSet(name)) {
-          this->impl->Makefile->IssueMessage(
-            MessageType::FATAL_ERROR,
-            cmStrCat("Header set \"", name, "\" has not yet been created."));
-          return;
-        }
-      }
-    }
-    this->impl->InterfaceHeaderSetsEntries.clear();
-    if (!StringIsEmpty(value)) {
-      this->impl->InterfaceHeaderSetsEntries.emplace_back(
-        value, this->impl->Makefile->GetBacktrace());
-    }
+    this->impl->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      "INTERFACE_HEADER_SETS property is read-only\n");
+    return;
   } else {
     this->impl->Properties.SetProperty(prop, value);
   }
@@ -1641,27 +1618,14 @@ void cmTarget::AppendProperty(const std::string& prop,
     fileSet->AddFileEntry(
       BT<std::string>(value, this->impl->Makefile->GetBacktrace()));
   } else if (prop == "HEADER_SETS") {
-    for (auto const& name : cmExpandedList(value)) {
-      if (!this->GetFileSet(name)) {
-        this->impl->Makefile->IssueMessage(
-          MessageType::FATAL_ERROR,
-          cmStrCat("Header set \"", name, "\" has not yet been created."));
-        return;
-      }
-    }
-    this->impl->HeaderSetsEntries.emplace_back(
-      value, this->impl->Makefile->GetBacktrace());
+    this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
+                                       "HEADER_SETS property is read-only\n");
+    return;
   } else if (prop == "INTERFACE_HEADER_SETS") {
-    for (auto const& name : cmExpandedList(value)) {
-      if (!this->GetFileSet(name)) {
-        this->impl->Makefile->IssueMessage(
-          MessageType::FATAL_ERROR,
-          cmStrCat("Header set \"", name, "\" has not yet been created."));
-        return;
-      }
-    }
-    this->impl->InterfaceHeaderSetsEntries.emplace_back(
-      value, this->impl->Makefile->GetBacktrace());
+    this->impl->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      "INTERFACE_HEADER_SETS property is read-only\n");
+    return;
   } else {
     this->impl->Properties.AppendProperty(prop, value, asString);
   }
@@ -2038,13 +2002,26 @@ cmValue cmTarget::GetProperty(const std::string& prop) const
       return cmValue(output);
     }
     if (prop == propHEADER_SETS) {
+      std::vector<std::string> set_names;
+      for (auto const& file_set : this->impl->FileSets) {
+        if (cmFileSetVisibilityIsForSelf(file_set.second.GetVisibility())) {
+          set_names.push_back(file_set.second.GetName());
+        }
+      }
       static std::string output;
-      output = cmJoin(this->impl->HeaderSetsEntries, ";"_s);
+      output = cmJoin(set_names, ";"_s);
       return cmValue(output);
     }
     if (prop == propINTERFACE_HEADER_SETS) {
+      std::vector<std::string> set_names;
+      for (auto const& file_set : this->impl->FileSets) {
+        if (cmFileSetVisibilityIsForInterface(
+              file_set.second.GetVisibility())) {
+          set_names.push_back(file_set.second.GetName());
+        }
+      }
       static std::string output;
-      output = cmJoin(this->impl->InterfaceHeaderSetsEntries, ";"_s);
+      output = cmJoin(set_names, ";"_s);
       return cmValue(output);
     }
   }
@@ -2342,10 +2319,20 @@ cmFileSet* cmTarget::GetFileSet(const std::string& name)
 }
 
 std::pair<cmFileSet*, bool> cmTarget::GetOrCreateFileSet(
-  const std::string& name, const std::string& type)
+  const std::string& name, const std::string& type, cmFileSetVisibility vis)
 {
-  auto result =
-    this->impl->FileSets.emplace(std::make_pair(name, cmFileSet(name, type)));
+  auto result = this->impl->FileSets.emplace(
+    std::make_pair(name, cmFileSet(name, type, vis)));
+  if (result.second) {
+    if (cmFileSetVisibilityIsForSelf(vis)) {
+      this->impl->HeaderSetsEntries.emplace_back(
+        name, this->impl->Makefile->GetBacktrace());
+    }
+    if (cmFileSetVisibilityIsForInterface(vis)) {
+      this->impl->InterfaceHeaderSetsEntries.emplace_back(
+        name, this->impl->Makefile->GetBacktrace());
+    }
+  }
   return std::make_pair(&result.first->second, result.second);
 }
 

+ 3 - 2
Source/cmTarget.h

@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "cmAlgorithms.h"
+#include "cmFileSet.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
@@ -19,7 +20,6 @@
 #include "cmValue.h"
 
 class cmCustomCommand;
-class cmFileSet;
 class cmGlobalGenerator;
 class cmInstallTargetGenerator;
 class cmListFileBacktrace;
@@ -285,7 +285,8 @@ public:
   const cmFileSet* GetFileSet(const std::string& name) const;
   cmFileSet* GetFileSet(const std::string& name);
   std::pair<cmFileSet*, bool> GetOrCreateFileSet(const std::string& name,
-                                                 const std::string& type);
+                                                 const std::string& type,
+                                                 cmFileSetVisibility vis);
 
   std::vector<std::string> GetAllInterfaceFileSets() const;
 

+ 10 - 42
Source/cmTargetSourcesCommand.cxx

@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetSourcesCommand.h"
 
-#include <algorithm>
 #include <sstream>
 #include <utility>
 
@@ -239,7 +238,11 @@ bool TargetSourcesImpl::HandleOneFileSet(
     (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);
+  cmFileSetVisibility visibility =
+    cmFileSetVisibilityFromName(scope, this->Makefile);
+
+  auto fileSet =
+    this->Target->GetOrCreateFileSet(args.FileSet, type, visibility);
   if (fileSet.second) {
     if (!isDefault) {
       if (!cmFileSet::IsValidName(args.FileSet)) {
@@ -261,15 +264,6 @@ bool TargetSourcesImpl::HandleOneFileSet(
     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) {
@@ -279,37 +273,11 @@ bool TargetSourcesImpl::HandleOneFileSet(
       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) {
+    if (visibility != fileSet.first->GetVisibility()) {
       this->SetError(
         cmStrCat("Scope ", scope, " for file set \"", args.FileSet,
-                 "\" does not match original scope ", existingScope));
+                 "\" does not match original scope ",
+                 cmFileSetVisibilityToName(fileSet.first->GetVisibility())));
       return false;
     }
   }
@@ -330,11 +298,11 @@ bool TargetSourcesImpl::HandleOneFileSet(
       for (auto const& dir : cmExpandedList(baseDirectories)) {
         auto interfaceDirectoriesGenex =
           cmStrCat("$<BUILD_INTERFACE:", dir, ">");
-        if (scope == "PRIVATE"_s || scope == "PUBLIC"_s) {
+        if (cmFileSetVisibilityIsForSelf(visibility)) {
           this->Target->AppendProperty("INCLUDE_DIRECTORIES",
                                        interfaceDirectoriesGenex);
         }
-        if (scope == "INTERFACE"_s || scope == "PUBLIC"_s) {
+        if (cmFileSetVisibilityIsForInterface(visibility)) {
           this->Target->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES",
                                        interfaceDirectoriesGenex);
         }

+ 2 - 2
Tests/RunCMake/target_sources/FileSetImport.cmake

@@ -17,7 +17,7 @@ include("${export_build_dir}/export.cmake")
 include("${export_build_dir}/install/lib/cmake/export.cmake")
 
 assert_prop_eq(export::lib1 HEADER_SETS "")
-assert_prop_eq(export::lib1 INTERFACE_HEADER_SETS "HEADERS;b;c;d;e;f;g;dir3")
+assert_prop_eq(export::lib1 INTERFACE_HEADER_SETS "HEADERS;b;c;d;dir3;e;f;g")
 assert_prop_eq(export::lib1 HEADER_SET "${CMAKE_CURRENT_SOURCE_DIR}/error.c")
 assert_prop_eq(export::lib1 HEADER_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")
 assert_prop_eq(export::lib1 HEADER_SET_b "${CMAKE_CURRENT_SOURCE_DIR}/h2.h")
@@ -35,7 +35,7 @@ assert_prop_eq(export::lib1 HEADER_DIRS_g "${CMAKE_CURRENT_SOURCE_DIR}/dir1;${CM
 assert_prop_eq(export::lib1 INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_SOURCE_DIR};$<1:${CMAKE_CURRENT_SOURCE_DIR}/dir>;${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_SOURCE_DIR}/$<IF:$<CONFIG:Debug>,debug,release>;${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_SOURCE_DIR}/dir1;${CMAKE_CURRENT_SOURCE_DIR}/dir2;${CMAKE_CURRENT_SOURCE_DIR}/dir3;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>;$<BUILD_INTERFACE:$<1:${CMAKE_CURRENT_SOURCE_DIR}/dir>>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/$<IF:$<CONFIG:Debug>,debug,release>>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/dir1>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/dir2>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/dir3>")
 
 assert_prop_eq(install::lib1 HEADER_SETS "")
-assert_prop_eq(install::lib1 INTERFACE_HEADER_SETS "HEADERS;b;c;d;e;f;g;dir3")
+assert_prop_eq(install::lib1 INTERFACE_HEADER_SETS "HEADERS;b;c;d;dir3;e;f;g")
 assert_prop_eq(install::lib1 HEADER_SET "${export_build_dir}/install/include/error.c")
 assert_prop_eq(install::lib1 HEADER_DIRS "${export_build_dir}/install/include")
 assert_prop_eq(install::lib1 HEADER_SET_b "${export_build_dir}/install/include/h2.h")

+ 0 - 4
Tests/RunCMake/target_sources/FileSetNoExistInterface-stderr.txt

@@ -1,4 +0,0 @@
-^CMake Error at FileSetNoExistInterface\.cmake:[0-9]+ \(set_property\):
-  Header set "a" has not yet been created\.
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)$

+ 0 - 4
Tests/RunCMake/target_sources/FileSetNoExistPrivate-stderr.txt

@@ -1,4 +0,0 @@
-^CMake Error at FileSetNoExistPrivate\.cmake:[0-9]+ \(set_property\):
-  Header set "a" has not yet been created\.
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)$

+ 0 - 7
Tests/RunCMake/target_sources/FileSetNoExistPrivate.cmake

@@ -1,7 +0,0 @@
-enable_language(C)
-
-add_library(lib1 STATIC empty.c)
-set_property(TARGET lib1 PROPERTY HEADER_SETS "a")
-
-# Error happens at configure-time, so this doesn't help.
-target_sources(lib1 PRIVATE FILE_SET a TYPE HEADERS)

+ 0 - 1
Tests/RunCMake/target_sources/FileSetNoScope-result.txt

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

+ 0 - 4
Tests/RunCMake/target_sources/FileSetNoScope-stderr.txt

@@ -1,4 +0,0 @@
-^CMake Error at FileSetNoScope\.cmake:[0-9]+ \(target_sources\):
-  target_sources File set "a" is not in HEADER_SETS or INTERFACE_HEADER_SETS
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)$

+ 0 - 6
Tests/RunCMake/target_sources/FileSetNoScope.cmake

@@ -1,6 +0,0 @@
-enable_language(C)
-
-add_library(lib1 STATIC empty.c)
-target_sources(lib1 PRIVATE FILE_SET a TYPE HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES h1.h)
-set_property(TARGET lib1 PROPERTY HEADER_SETS)
-target_sources(lib1 PRIVATE FILE_SET a TYPE HEADERS FILES h2.h)

+ 2 - 2
Tests/RunCMake/target_sources/FileSetProperties.cmake

@@ -57,14 +57,14 @@ assert_prop_eq(lib1 INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURC
 assert_prop_eq(lib1 INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")
 
 target_sources(lib1 PUBLIC FILE_SET HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES h1.h)
-assert_prop_eq(lib1 INTERFACE_HEADER_SETS "a;c;d;HEADERS")
+assert_prop_eq(lib1 INTERFACE_HEADER_SETS "HEADERS;a;c;d")
 assert_prop_eq(lib1 HEADER_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")
 assert_prop_eq(lib1 HEADER_SET "${CMAKE_CURRENT_SOURCE_DIR}/h1.h")
 assert_prop_eq(lib1 INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/dir>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")
 assert_prop_eq(lib1 INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")
 
 target_sources(lib1 PUBLIC FILE_SET HEADERS FILES h2.h)
-assert_prop_eq(lib1 INTERFACE_HEADER_SETS "a;c;d;HEADERS")
+assert_prop_eq(lib1 INTERFACE_HEADER_SETS "HEADERS;a;c;d")
 assert_prop_eq(lib1 HEADER_DIRS "${CMAKE_CURRENT_SOURCE_DIR}")
 assert_prop_eq(lib1 HEADER_SET "${CMAKE_CURRENT_SOURCE_DIR}/h1.h;${CMAKE_CURRENT_SOURCE_DIR}/h2.h")
 assert_prop_eq(lib1 INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/.>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/dir>;$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")

+ 0 - 0
Tests/RunCMake/target_sources/FileSetNoExistInterface-result.txt → Tests/RunCMake/target_sources/FileSetReadOnlyInterface-result.txt


+ 5 - 0
Tests/RunCMake/target_sources/FileSetReadOnlyInterface-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at FileSetReadOnlyInterface\.cmake:[0-9]+ \(set_property\):
+  INTERFACE_HEADER_SETS property is read-only
+
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$

+ 0 - 3
Tests/RunCMake/target_sources/FileSetNoExistInterface.cmake → Tests/RunCMake/target_sources/FileSetReadOnlyInterface.cmake

@@ -2,6 +2,3 @@ enable_language(C)
 
 add_library(lib1 STATIC empty.c)
 set_property(TARGET lib1 PROPERTY INTERFACE_HEADER_SETS "a")
-
-# Error happens at configure-time, so this doesn't help.
-target_sources(lib1 INTERFACE FILE_SET a TYPE HEADERS)

+ 0 - 0
Tests/RunCMake/target_sources/FileSetNoExistPrivate-result.txt → Tests/RunCMake/target_sources/FileSetReadOnlyPrivate-result.txt


+ 5 - 0
Tests/RunCMake/target_sources/FileSetReadOnlyPrivate-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at FileSetReadOnlyPrivate\.cmake:[0-9]+ \(set_property\):
+  HEADER_SETS property is read-only
+
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$

+ 4 - 0
Tests/RunCMake/target_sources/FileSetReadOnlyPrivate.cmake

@@ -0,0 +1,4 @@
+enable_language(C)
+
+add_library(lib1 STATIC empty.c)
+set_property(TARGET lib1 PROPERTY HEADER_SETS "a")

+ 2 - 3
Tests/RunCMake/target_sources/RunCMakeTest.cmake

@@ -33,9 +33,8 @@ run_cmake(FileSetWrongBaseDirsRelative)
 run_cmake(FileSetOverlappingBaseDirs)
 run_cmake(FileSetInstallMissingSetsPrivate)
 run_cmake(FileSetInstallMissingSetsInterface)
-run_cmake(FileSetNoScope)
-run_cmake(FileSetNoExistPrivate)
-run_cmake(FileSetNoExistInterface)
+run_cmake(FileSetReadOnlyPrivate)
+run_cmake(FileSetReadOnlyInterface)
 run_cmake(FileSetNoExistInstall)
 run_cmake(FileSetDirectories)
 run_cmake(FileSetCustomTarget)