Procházet zdrojové kódy

Merge topic 'verify-private-header-sets'

b61637f90f File sets: Add verification of private file sets
61df2e15a2 Help: Verification target also sets CXX_SCAN_FOR_MODULES to false
909b54023a File sets: Specify verify file set target names in one place
20219414c3 File sets: Always verify interface files sets of executable targets
c7a1d5ee58 Tests: Rename VerifyHeaderSets tests to VerifyInterfaceHeaderSets

Acked-by: Kitware Robot <[email protected]>
Tested-by: buildbot <[email protected]>
Merge-request: !11565
Brad King před 1 týdnem
rodič
revize
18c98682f5
91 změnil soubory, kde provedl 772 přidání a 246 odebrání
  1. 1 0
      Help/manual/cmake-policies.7.rst
  2. 2 0
      Help/manual/cmake-properties.7.rst
  3. 1 0
      Help/manual/cmake-variables.7.rst
  4. 27 0
      Help/policy/CMP0209.rst
  5. 7 5
      Help/prop_tgt/INTERFACE_HEADER_SETS_TO_VERIFY.rst
  6. 15 0
      Help/prop_tgt/PRIVATE_HEADER_SETS_TO_VERIFY.rst
  7. 17 32
      Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst
  8. 34 0
      Help/prop_tgt/VERIFY_PRIVATE_HEADER_SETS.rst
  9. 34 0
      Help/prop_tgt/include/VERIFY_XXX_HEADER_SETS.rst
  10. 13 0
      Help/release/dev/verify-private-header-sets.rst
  11. 5 34
      Help/variable/CMAKE_VERIFY_INTERFACE_HEADER_SETS.rst
  12. 10 0
      Help/variable/CMAKE_VERIFY_PRIVATE_HEADER_SETS.rst
  13. 39 0
      Help/variable/include/CMAKE_VERIFY_XXX_HEADER_SETS.rst
  14. 29 12
      Modules/FetchContent.cmake
  15. 215 122
      Source/cmGeneratorTarget.cxx
  16. 1 0
      Source/cmGeneratorTarget.h
  17. 27 4
      Source/cmGlobalGenerator.cxx
  18. 5 1
      Source/cmPolicies.h
  19. 1 0
      Source/cmTarget.cxx
  20. 7 3
      Tests/RunCMake/FetchContent/VerifyHeaderSet-stdout.txt
  21. 9 4
      Tests/RunCMake/FetchContent/VerifyHeaderSet.cmake
  22. 3 2
      Tests/RunCMake/FetchContent/VerifyHeaderSet/CMakeLists.txt
  23. 1 0
      Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
  24. 10 0
      Tests/RunCMake/VerifyHeaderSets/AllVerifyPrivateHeaderSets-all_verify_private_header_sets-Debug-build-check.cmake
  25. 4 0
      Tests/RunCMake/VerifyHeaderSets/AllVerifyPrivateHeaderSets.cmake
  26. 74 24
      Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake
  27. 2 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209-NEW.cmake
  28. 2 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209-OLD.cmake
  29. 10 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209-WARN-stderr.txt
  30. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209-WARN.cmake
  31. 16 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209.cmake
  32. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-a_h-Debug-build-result.txt
  33. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-a_h-Debug-build-stderr.txt
  34. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-a_h-Debug-build-stdout.txt
  35. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-check.cmake
  36. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-config-Debug-build-result.txt
  37. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-config-Debug-build-stderr.txt
  38. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-config-Debug-build-stdout.txt
  39. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-config-Release-build-result.txt
  40. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-config-Release-build-stderr.txt
  41. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-config-Release-build-stdout.txt
  42. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-dir_c_h-Debug-build-result.txt
  43. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-dir_c_h-Debug-build-stderr.txt
  44. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-dir_c_h-Debug-build-stdout.txt
  45. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-dir_cxx_h-Debug-build-result.txt
  46. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-dir_cxx_h-Debug-build-stderr.txt
  47. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-dir_cxx_h-Debug-build-stdout.txt
  48. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-exe-Debug-build-result.txt
  49. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-exe-Debug-build-stderr.txt
  50. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt
  51. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-stderr.txt
  52. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-result.txt
  53. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-stderr.txt
  54. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-property_off-Debug-build-result.txt
  55. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-property_off-Debug-build-stderr.txt
  56. 7 3
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets.cmake
  57. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSetsNonexistent-result.txt
  58. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSetsNonexistent-stderr.txt
  59. 0 0
      Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSetsNonexistent.cmake
  60. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-a_h-Debug-build-result.txt
  61. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-a_h-Debug-build-stderr.txt
  62. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-a_h-Debug-build-stdout.txt
  63. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-config-Debug-build-result.txt
  64. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-config-Debug-build-stderr.txt
  65. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-config-Debug-build-stdout.txt
  66. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-config-Release-build-result.txt
  67. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-config-Release-build-stderr.txt
  68. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-config-Release-build-stdout.txt
  69. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-dir_c_h-Debug-build-result.txt
  70. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-dir_c_h-Debug-build-stderr.txt
  71. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-dir_c_h-Debug-build-stdout.txt
  72. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-dir_cxx_h-Debug-build-result.txt
  73. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-dir_cxx_h-Debug-build-stderr.txt
  74. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-dir_cxx_h-Debug-build-stdout.txt
  75. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-result.txt
  76. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-stderr.txt
  77. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-result.txt
  78. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-stderr.txt
  79. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-result.txt
  80. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-stderr.txt
  81. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-property_off-Debug-build-result.txt
  82. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-property_off-Debug-build-stderr.txt
  83. 88 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets.cmake
  84. 1 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSetsNonexistent-result.txt
  85. 9 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSetsNonexistent-stderr.txt
  86. 5 0
      Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSetsNonexistent.cmake
  87. 3 0
      Tests/RunCMake/VerifyHeaderSets/dir3/CMakeLists.txt
  88. 5 0
      Tests/RunCMake/VerifyHeaderSets/dir3/lib3.h
  89. 3 0
      Tests/RunCMake/VerifyHeaderSets/dir4/CMakeLists.txt
  90. 5 0
      Tests/RunCMake/VerifyHeaderSets/dir4/lib4.h
  91. 1 0
      Tests/RunCMake/property_init/Always.cmake

+ 1 - 0
Help/manual/cmake-policies.7.rst

@@ -100,6 +100,7 @@ Policies Introduced by CMake 4.3
 .. toctree::
 .. toctree::
    :maxdepth: 1
    :maxdepth: 1
 
 
+   CMP0209: Verify interface header sets checks executables without exports. </policy/CMP0209>
    CMP0208: export(EXPORT) does not allow empty arguments. </policy/CMP0208>
    CMP0208: export(EXPORT) does not allow empty arguments. </policy/CMP0208>
    CMP0207: file(GET_RUNTIME_DEPENDENCIES) normalizes paths before matching. </policy/CMP0207>
    CMP0207: file(GET_RUNTIME_DEPENDENCIES) normalizes paths before matching. </policy/CMP0207>
    CMP0206: The CPack Archive Generator defaults to UID 0 and GID 0. </policy/CMP0206>
    CMP0206: The CPack Archive Generator defaults to UID 0 and GID 0. </policy/CMP0206>

+ 2 - 0
Help/manual/cmake-properties.7.rst

@@ -393,6 +393,7 @@ Properties on Targets
    /prop_tgt/PRECOMPILE_HEADERS_REUSE_FROM
    /prop_tgt/PRECOMPILE_HEADERS_REUSE_FROM
    /prop_tgt/PREFIX
    /prop_tgt/PREFIX
    /prop_tgt/PRIVATE_HEADER
    /prop_tgt/PRIVATE_HEADER
+   /prop_tgt/PRIVATE_HEADER_SETS_TO_VERIFY
    /prop_tgt/PROJECT_LABEL
    /prop_tgt/PROJECT_LABEL
    /prop_tgt/PUBLIC_HEADER
    /prop_tgt/PUBLIC_HEADER
    /prop_tgt/RESOURCE
    /prop_tgt/RESOURCE
@@ -433,6 +434,7 @@ Properties on Targets
    /prop_tgt/UNITY_BUILD_RELOCATABLE
    /prop_tgt/UNITY_BUILD_RELOCATABLE
    /prop_tgt/UNITY_BUILD_UNIQUE_ID
    /prop_tgt/UNITY_BUILD_UNIQUE_ID
    /prop_tgt/VERIFY_INTERFACE_HEADER_SETS
    /prop_tgt/VERIFY_INTERFACE_HEADER_SETS
+   /prop_tgt/VERIFY_PRIVATE_HEADER_SETS
    /prop_tgt/VERSION
    /prop_tgt/VERSION
    /prop_tgt/VISIBILITY_INLINES_HIDDEN
    /prop_tgt/VISIBILITY_INLINES_HIDDEN
    /prop_tgt/VS_CONFIGURATION_TYPE
    /prop_tgt/VS_CONFIGURATION_TYPE

+ 1 - 0
Help/manual/cmake-variables.7.rst

@@ -598,6 +598,7 @@ Variables that Control the Build
    /variable/CMAKE_UNITY_BUILD_RELOCATABLE
    /variable/CMAKE_UNITY_BUILD_RELOCATABLE
    /variable/CMAKE_UNITY_BUILD_UNIQUE_ID
    /variable/CMAKE_UNITY_BUILD_UNIQUE_ID
    /variable/CMAKE_VERIFY_INTERFACE_HEADER_SETS
    /variable/CMAKE_VERIFY_INTERFACE_HEADER_SETS
+   /variable/CMAKE_VERIFY_PRIVATE_HEADER_SETS
    /variable/CMAKE_VISIBILITY_INLINES_HIDDEN
    /variable/CMAKE_VISIBILITY_INLINES_HIDDEN
    /variable/CMAKE_VS_DEBUGGER_COMMAND
    /variable/CMAKE_VS_DEBUGGER_COMMAND
    /variable/CMAKE_VS_DEBUGGER_COMMAND_ARGUMENTS
    /variable/CMAKE_VS_DEBUGGER_COMMAND_ARGUMENTS

+ 27 - 0
Help/policy/CMP0209.rst

@@ -0,0 +1,27 @@
+CMP0209
+-------
+
+.. versionadded:: 4.3
+
+Verify interface header sets checks executables without exports.
+
+When :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` is set to true on an executable
+target, CMake 4.2 and below would only perform those checks if the target's
+:prop_tgt:`ENABLE_EXPORTS` property was true.  The reasoning behind this
+exclusion was that no other target could consume the headers of an executable
+target if that executable target didn't export symbols.  Since then, other
+use cases have emerged for verifying header file sets where exporting symbols
+is no longer a requirement.  Therefore, CMake 4.3 and above prefer to verify
+interface file sets of executable targets regardless of whether they export
+symbols or not.
+
+The ``OLD`` behavior of this policy only verifies interface file sets of an
+executable target if its :prop_tgt:`ENABLE_EXPORTS` property is set to true.
+The ``NEW`` behavior always verifies interface file sets of an executable
+target when :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` is set to true.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.3
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: include/STANDARD_ADVICE.rst
+
+.. include:: include/DEPRECATED.rst

+ 7 - 5
Help/prop_tgt/INTERFACE_HEADER_SETS_TO_VERIFY.rst

@@ -4,10 +4,12 @@ INTERFACE_HEADER_SETS_TO_VERIFY
 .. versionadded:: 3.24
 .. versionadded:: 3.24
 
 
 Used to specify which ``PUBLIC`` and ``INTERFACE`` header sets of a target
 Used to specify which ``PUBLIC`` and ``INTERFACE`` header sets of a target
-should be verified.
+should be verified as interface headers.
 
 
 This property contains a semicolon-separated list of header sets which
 This property contains a semicolon-separated list of header sets which
-should be verified if :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` is set to
-``TRUE``. If the list is empty, all ``PUBLIC`` and ``INTERFACE`` header sets
-are verified. (If the project does not want to verify any header sets on the
-target, simply set :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` to ``FALSE``.)
+should be verified if :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` is set to true.
+If the list is empty, all ``PUBLIC`` and ``INTERFACE`` header sets are
+verified. If the project does not want to verify any interface header sets on
+the target, set :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` to false.
+
+See also :prop_tgt:`PRIVATE_HEADER_SETS_TO_VERIFY`.

+ 15 - 0
Help/prop_tgt/PRIVATE_HEADER_SETS_TO_VERIFY.rst

@@ -0,0 +1,15 @@
+PRIVATE_HEADER_SETS_TO_VERIFY
+-----------------------------
+
+.. versionadded:: 4.3
+
+Used to specify which ``PUBLIC`` and ``PRIVATE`` header sets of a target
+should be verified as private headers.
+
+This property contains a semicolon-separated list of header sets which
+should be verified if :prop_tgt:`VERIFY_PRIVATE_HEADER_SETS` is set to true.
+If the list is empty, all ``PUBLIC`` and ``PRIVATE`` header sets are verified.
+If the project does not want to verify any private header sets on the target,
+set :prop_tgt:`VERIFY_PRIVATE_HEADER_SETS` to false.
+
+See also :prop_tgt:`INTERFACE_HEADER_SETS_TO_VERIFY`.

+ 17 - 32
Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst

@@ -7,35 +7,20 @@ Used to verify that all headers in a target's ``PUBLIC`` and ``INTERFACE``
 header sets can be included on their own.
 header sets can be included on their own.
 
 
 When this property is set to true, and the target is an object library, static
 When this property is set to true, and the target is an object library, static
-library, shared library, interface library, or executable with exports enabled,
-and the target has one or more ``PUBLIC`` or ``INTERFACE`` header sets, an
-object library target named ``<target_name>_verify_interface_header_sets`` is
-created. This verification target has one source file per header in the
-``PUBLIC`` and ``INTERFACE`` header sets. Each source file only includes its
-associated header file. The verification target links against the original
-target to get all of its usage requirements. The verification target has its
-:prop_tgt:`EXCLUDE_FROM_ALL` and :prop_tgt:`DISABLE_PRECOMPILE_HEADERS`
-properties set to true, and its :prop_tgt:`AUTOMOC`, :prop_tgt:`AUTORCC`,
-:prop_tgt:`AUTOUIC`, and :prop_tgt:`UNITY_BUILD` properties set to false.
-
-If the header's :prop_sf:`LANGUAGE` property is set, the value of that property
-is used to determine the language with which to compile the header file.
-Otherwise, if the target has any C++ sources, the header is compiled as C++.
-Otherwise, if the target has any C sources, the header is compiled as C.
-Otherwise, if C++ is enabled globally, the header is compiled as C++.
-Otherwise, if C is enabled globally, the header is compiled as C. Otherwise,
-the header file is not compiled.
-
-If the header's :prop_sf:`SKIP_LINTING` property is set to true, the file is
-not compiled.
-
-If any verification targets are created, a top-level target called
-``all_verify_interface_header_sets`` is created which depends on all
-verification targets.
-
-This property is initialized by the value of the
-:variable:`CMAKE_VERIFY_INTERFACE_HEADER_SETS` variable if it is set when
-a target is created.
-
-If the project wishes to control which header sets are verified by this
-property, it can set :prop_tgt:`INTERFACE_HEADER_SETS_TO_VERIFY`.
+library, shared library, interface library, or executable (subject to policy
+:policy:`CMP0209`) and the target has one or more ``PUBLIC`` or ``INTERFACE``
+header sets, an object library target named
+``<target_name>_verify_interface_header_sets`` is created. This verification
+target has one source file per header in the ``PUBLIC`` and ``INTERFACE``
+header sets. Each source file only includes its associated header file.
+The verification target links against the original target to get all of its
+usage requirements.
+
+.. |xxx| replace:: interface
+.. |THIS_PROPERTY| replace:: ``VERIFY_INTERFACE_HEADER_SETS``
+.. |COMPLEMENTARY_PROPERTY| replace:: :prop_tgt:`VERIFY_PRIVATE_HEADER_SETS`
+.. |THIS_ALL_TARGET| replace:: ``all_verify_interface_header_sets``
+.. |COMPLEMENTARY_ALL_TARGET| replace:: ``all_verify_private_header_sets``
+.. |INIT_VARIABLE| replace:: :variable:`CMAKE_VERIFY_INTERFACE_HEADER_SETS`
+.. |SETS_TO_VERIFY_PROPERTY| replace:: :prop_tgt:`INTERFACE_HEADER_SETS_TO_VERIFY`
+.. include:: include/VERIFY_XXX_HEADER_SETS.rst

+ 34 - 0
Help/prop_tgt/VERIFY_PRIVATE_HEADER_SETS.rst

@@ -0,0 +1,34 @@
+VERIFY_PRIVATE_HEADER_SETS
+--------------------------
+
+.. versionadded:: 4.3
+
+Used to verify that all headers in a target's ``PUBLIC`` and ``PRIVATE``
+header sets can be included on their own.
+
+When this property is set to true, and the target is an object library, static
+library, shared library, module library, interface library, or executable, and
+the target has one or more ``PUBLIC`` or ``PRIVATE`` header sets, an object
+library target named ``<target_name>_verify_private_header_sets`` is created.
+This verification target has one source file per header in the ``PUBLIC`` and
+``PRIVATE`` header sets. Each source file only includes its associated header
+file.
+
+Properties affecting compilation are copied from the original target to the
+verification target so that the headers will be interpreted the same way by
+the compiler as when compiling the original target's sources.  There are some
+caveats with this approach.  It cannot replicate the same conditions if any
+of those properties or properties inherited through build requirements from
+transitive dependencies contain
+:ref:`target-dependent generator expressions <Target-Dependent Expressions>`
+that do not specify the target for the expansion.  Such expressions can expand
+to different contents depending on the target they are being used on.
+
+.. |xxx| replace:: private
+.. |THIS_PROPERTY| replace:: ``VERIFY_PRIVATE_HEADER_SETS``
+.. |COMPLEMENTARY_PROPERTY| replace:: :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS`
+.. |THIS_ALL_TARGET| replace:: ``all_verify_private_header_sets``
+.. |COMPLEMENTARY_ALL_TARGET| replace:: ``all_verify_interface_header_sets``
+.. |INIT_VARIABLE| replace:: :variable:`CMAKE_VERIFY_PRIVATE_HEADER_SETS`
+.. |SETS_TO_VERIFY_PROPERTY| replace:: :prop_tgt:`PRIVATE_HEADER_SETS_TO_VERIFY`
+.. include:: include/VERIFY_XXX_HEADER_SETS.rst

+ 34 - 0
Help/prop_tgt/include/VERIFY_XXX_HEADER_SETS.rst

@@ -0,0 +1,34 @@
+The verification target has its
+:prop_tgt:`EXCLUDE_FROM_ALL` and :prop_tgt:`DISABLE_PRECOMPILE_HEADERS`
+properties set to true, and its :prop_tgt:`AUTOMOC`, :prop_tgt:`AUTORCC`,
+:prop_tgt:`AUTOUIC`, :prop_tgt:`UNITY_BUILD`, and
+:prop_tgt:`CXX_SCAN_FOR_MODULES` properties set to false.
+
+If the header's :prop_sf:`LANGUAGE` property is set, the value of that property
+is used to determine the language with which to compile the header file.
+Otherwise, if the target has any C++ sources, the header is compiled as C++.
+Otherwise, if the target has any C sources, the header is compiled as C.
+Otherwise, if C++ is enabled globally, the header is compiled as C++.
+Otherwise, if C is enabled globally, the header is compiled as C. Otherwise,
+the header file is not compiled.
+
+If the header's :prop_sf:`SKIP_LINTING` property is set to true, the file is
+not compiled.
+
+If |THIS_PROPERTY| and |COMPLEMENTARY_PROPERTY| are both set to true, headers
+belonging to ``PUBLIC`` file sets will be verified twice, but with different
+conditions.  The compiler flags used for private and interface contexts can be
+different, leading to the compiler interpreting the contents of the header
+differently.
+
+If any |xxx| file set verification targets are created, a top-level target
+called |THIS_ALL_TARGET| is created which depends on all |xxx| verification
+targets.  Another target called ``all_verify_header_sets`` is also created
+which depends on |THIS_ALL_TARGET|, and on |COMPLEMENTARY_ALL_TARGET| if it
+exists (see |COMPLEMENTARY_PROPERTY|).
+
+This property is initialized by the value of the |INIT_VARIABLE| variable if
+it is set when a target is created.
+
+If the project wishes to control which header sets are verified by this
+property, it can set |SETS_TO_VERIFY_PROPERTY|.

+ 13 - 0
Help/release/dev/verify-private-header-sets.rst

@@ -0,0 +1,13 @@
+verify-private-header-sets
+--------------------------
+
+* When :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` is set to true on an executable
+  target, that target's interface file sets are verified regardless of its
+  :prop_tgt:`ENABLE_EXPORTS` property. See policy :policy:`CMP0209`.
+* The :variable:`CMAKE_VERIFY_PRIVATE_HEADER_SETS` variable and corresponding
+  :prop_tgt:`VERIFY_PRIVATE_HEADER_SETS` target property were added to
+  enable build rules that verify all headers in private file sets can be used
+  on their own.
+* The :prop_tgt:`PRIVATE_HEADER_SETS_TO_VERIFY` target property was added to
+  customize which private file sets to verify when the target's
+  :prop_tgt:`VERIFY_PRIVATE_HEADER_SETS` property is true.

+ 5 - 34
Help/variable/CMAKE_VERIFY_INTERFACE_HEADER_SETS.rst

@@ -3,37 +3,8 @@ CMAKE_VERIFY_INTERFACE_HEADER_SETS
 
 
 .. versionadded:: 3.24
 .. versionadded:: 3.24
 
 
-This variable is used to initialize the
-:prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` property of targets when they are
-created.  Setting it to true enables header set verification.
-
-Projects should not normally set this variable, it is intended as a developer
-control to be set on the :manual:`cmake(1)` command line or other
-equivalent methods.  The developer must have the ability to enable or
-disable header set verification according to the capabilities of their own
-machine and compiler.
-
-Verification of a dependency's header sets is not typically of interest
-to developers.  Therefore, :command:`FetchContent_MakeAvailable` explicitly
-sets ``CMAKE_VERIFY_INTERFACE_HEADER_SETS`` to false for the duration of its
-call, but restores its original value before returning.  If a project brings
-a dependency directly into the main build (e.g. calling
-:command:`add_subdirectory` on a vendored project from a git submodule), it
-should also do likewise.  For example:
-
-.. code:: cmake
-
-  # Save original setting so we can restore it later
-  set(want_header_set_verification ${CMAKE_VERIFY_INTERFACE_HEADER_SETS})
-
-  # Include the vendored dependency with header set verification disabled
-  set(CMAKE_VERIFY_INTERFACE_HEADER_SETS OFF)
-  add_subdirectory(...)   # Vendored sources, e.g. from git submodules
-
-  # Add the project's own sources. Restore the developer's original choice
-  # for whether to enable header set verification.
-  set(CMAKE_VERIFY_INTERFACE_HEADER_SETS ${want_header_set_verification})
-  add_subdirectory(src)
-
-By default, this variable is not set, which will result in header set
-verification being disabled.
+.. |VERIFY_XXX_HEADER_SETS| replace:: :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS`
+.. |CMAKE_VERIFY_XXX_HEADER_SETS| replace:: ``CMAKE_VERIFY_INTERFACE_HEADER_SETS``
+.. |COMPLEMENTARY_CMAKE_VERIFY_XXX_HEADER_SETS| replace:: :variable:`CMAKE_VERIFY_PRIVATE_HEADER_SETS`
+.. |xxx| replace:: interface
+.. include:: include/CMAKE_VERIFY_XXX_HEADER_SETS.rst

+ 10 - 0
Help/variable/CMAKE_VERIFY_PRIVATE_HEADER_SETS.rst

@@ -0,0 +1,10 @@
+CMAKE_VERIFY_PRIVATE_HEADER_SETS
+--------------------------------
+
+.. versionadded:: 4.3
+
+.. |VERIFY_XXX_HEADER_SETS| replace:: :prop_tgt:`VERIFY_PRIVATE_HEADER_SETS`
+.. |CMAKE_VERIFY_XXX_HEADER_SETS| replace:: ``CMAKE_VERIFY_PRIVATE_HEADER_SETS``
+.. |COMPLEMENTARY_CMAKE_VERIFY_XXX_HEADER_SETS| replace:: :variable:`CMAKE_VERIFY_INTERFACE_HEADER_SETS`
+.. |xxx| replace:: private
+.. include:: include/CMAKE_VERIFY_XXX_HEADER_SETS.rst

+ 39 - 0
Help/variable/include/CMAKE_VERIFY_XXX_HEADER_SETS.rst

@@ -0,0 +1,39 @@
+This variable is used to initialize the |VERIFY_XXX_HEADER_SETS| property of
+targets when they are created.  Setting it to true enables |xxx| header set
+verification.
+
+Projects should not normally set this variable, it is intended as a developer
+control to be set on the :manual:`cmake(1)` command line or other
+equivalent methods.  The developer must have the ability to enable or
+disable header set verification according to the capabilities of their own
+machine and compiler.
+
+Verification of a dependency's header sets is not typically of interest to
+developers.  Therefore, :command:`FetchContent_MakeAvailable` explicitly sets
+|CMAKE_VERIFY_XXX_HEADER_SETS| and |COMPLEMENTARY_CMAKE_VERIFY_XXX_HEADER_SETS|
+to false for the duration of its call, but restores their original values
+before returning.  If a project brings a dependency directly into the main
+build (e.g. calling :command:`add_subdirectory` on a vendored project from a
+git submodule), it should also do likewise.  For example:
+
+.. code:: cmake
+
+  # Save original setting so we can restore it later
+  set(want_interface_header_set_verification ${CMAKE_VERIFY_INTERFACE_HEADER_SETS})
+  set(want_private_header_set_verification ${CMAKE_VERIFY_PRIVATE_HEADER_SETS})
+
+  # Include the vendored dependency with header set verification disabled
+  set(CMAKE_VERIFY_INTERFACE_HEADER_SETS OFF)
+  set(CMAKE_VERIFY_PRIVATE_HEADER_SETS OFF)
+  add_subdirectory(...)   # Vendored sources, e.g. from git submodules
+
+  # Add the project's own sources. Restore the developer's original choice
+  # for whether to enable header set verification.
+  set(CMAKE_VERIFY_INTERFACE_HEADER_SETS ${want_interface_header_set_verification})
+  set(CMAKE_VERIFY_PRIVATE_HEADER_SETS ${want_private_header_set_verification})
+  add_subdirectory(src)
+
+By default, this variable is not set, which will result in |xxx| header set
+verification being disabled.
+
+See also |COMPLEMENTARY_CMAKE_VERIFY_XXX_HEADER_SETS|.

+ 29 - 12
Modules/FetchContent.cmake

@@ -397,17 +397,20 @@ Commands
     FetchContent_Declare(other ...)
     FetchContent_Declare(other ...)
     FetchContent_MakeAvailable(uses_other other)
     FetchContent_MakeAvailable(uses_other other)
 
 
-  Note that :variable:`CMAKE_VERIFY_INTERFACE_HEADER_SETS` is explicitly set
-  to false upon entry to ``FetchContent_MakeAvailable()``, and is restored to
-  its original value before the command returns.  Developers typically only
+  Note that :variable:`CMAKE_VERIFY_INTERFACE_HEADER_SETS` and
+  :variable:`CMAKE_VERIFY_PRIVATE_HEADER_SETS` are explicitly set to false
+  upon entry to ``FetchContent_MakeAvailable()``, and are restored to their
+  original values before the command returns.  Developers typically only
   want to verify header sets from the main project, not those from any
   want to verify header sets from the main project, not those from any
   dependencies.  This local manipulation of the
   dependencies.  This local manipulation of the
-  :variable:`CMAKE_VERIFY_INTERFACE_HEADER_SETS` variable provides that
+  :variable:`CMAKE_VERIFY_INTERFACE_HEADER_SETS` and
+  :variable:`CMAKE_VERIFY_PRIVATE_HEADER_SETS` variables provides that
   intuitive behavior.  You can use variables like
   intuitive behavior.  You can use variables like
   :variable:`CMAKE_PROJECT_INCLUDE` or
   :variable:`CMAKE_PROJECT_INCLUDE` or
   :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE` to turn verification back
   :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE` to turn verification back
   on for all or some dependencies.  You can also set the
   on for all or some dependencies.  You can also set the
-  :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` property of individual targets.
+  :prop_tgt:`VERIFY_INTERFACE_HEADER_SETS` and
+  :prop_tgt:`VERIFY_PRIVATE_HEADER_SETS` properties of individual targets.
 
 
 .. command:: FetchContent_Populate
 .. command:: FetchContent_Populate
 
 
@@ -2240,12 +2243,14 @@ endfunction()
 # calls will be available to the caller.
 # calls will be available to the caller.
 macro(FetchContent_MakeAvailable)
 macro(FetchContent_MakeAvailable)
 
 
-  # We must append an item, even if the variable is unset, so prefix its value.
-  # We will strip that prefix when we pop the value at the end of the macro.
+  # We must append these, even if the variables are unset, so prefix the values.
+  # We will strip that prefix when we pop the values at the end of the macro.
   list(APPEND __cmake_fcCurrentVarsStack
   list(APPEND __cmake_fcCurrentVarsStack
     "__fcprefix__${CMAKE_VERIFY_INTERFACE_HEADER_SETS}"
     "__fcprefix__${CMAKE_VERIFY_INTERFACE_HEADER_SETS}"
+    "__fcprefix__${CMAKE_VERIFY_PRIVATE_HEADER_SETS}"
   )
   )
   set(CMAKE_VERIFY_INTERFACE_HEADER_SETS FALSE)
   set(CMAKE_VERIFY_INTERFACE_HEADER_SETS FALSE)
+  set(CMAKE_VERIFY_PRIVATE_HEADER_SETS FALSE)
 
 
   get_property(__cmake_providerCommand GLOBAL PROPERTY
   get_property(__cmake_providerCommand GLOBAL PROPERTY
     __FETCHCONTENT_MAKEAVAILABLE_SERIAL_PROVIDER
     __FETCHCONTENT_MAKEAVAILABLE_SERIAL_PROVIDER
@@ -2453,18 +2458,30 @@ macro(FetchContent_MakeAvailable)
   endforeach()
   endforeach()
 
 
   # Prefix will be "__fcprefix__"
   # Prefix will be "__fcprefix__"
-  list(POP_BACK __cmake_fcCurrentVarsStack __cmake_original_verify_setting)
-  string(SUBSTRING "${__cmake_original_verify_setting}"
-    12 -1 __cmake_original_verify_setting
+  list(POP_BACK __cmake_fcCurrentVarsStack
+    __cmake_original_verify_private_setting
+    __cmake_original_verify_interface_setting
+  )
+  string(SUBSTRING "${__cmake_original_verify_private_setting}"
+    12 -1 __cmake_original_verify_private_setting
+  )
+  string(SUBSTRING "${__cmake_original_verify_interface_setting}"
+    12 -1 __cmake_original_verify_interface_setting
+  )
+  set(CMAKE_VERIFY_PRIVATE_HEADER_SETS
+    ${__cmake_original_verify_private_setting}
+  )
+  set(CMAKE_VERIFY_INTERFACE_HEADER_SETS
+    ${__cmake_original_verify_interface_setting}
   )
   )
-  set(CMAKE_VERIFY_INTERFACE_HEADER_SETS ${__cmake_original_verify_setting})
 
 
   # clear local variables to prevent leaking into the caller's scope
   # clear local variables to prevent leaking into the caller's scope
   unset(__cmake_contentName)
   unset(__cmake_contentName)
   unset(__cmake_contentNameLower)
   unset(__cmake_contentNameLower)
   unset(__cmake_contentNameUpper)
   unset(__cmake_contentNameUpper)
   unset(__cmake_providerCommand)
   unset(__cmake_providerCommand)
-  unset(__cmake_original_verify_setting)
+  unset(__cmake_original_verify_interface_setting)
+  unset(__cmake_original_verify_private_setting)
 
 
 endmacro()
 endmacro()
 
 

+ 215 - 122
Source/cmGeneratorTarget.cxx

@@ -9,6 +9,7 @@
 #include <cstddef>
 #include <cstddef>
 #include <cstdio>
 #include <cstdio>
 #include <cstring>
 #include <cstring>
+#include <initializer_list>
 #include <sstream>
 #include <sstream>
 #include <unordered_set>
 #include <unordered_set>
 #include <utility>
 #include <utility>
@@ -5709,145 +5710,236 @@ cmGeneratorTarget::ManagedType cmGeneratorTarget::GetManagedType(
 
 
 bool cmGeneratorTarget::AddHeaderSetVerification()
 bool cmGeneratorTarget::AddHeaderSetVerification()
 {
 {
-  if (!this->GetPropertyAsBool("VERIFY_INTERFACE_HEADER_SETS")) {
-    return true;
-  }
-
-  if (this->GetType() != cmStateEnums::STATIC_LIBRARY &&
-      this->GetType() != cmStateEnums::SHARED_LIBRARY &&
-      this->GetType() != cmStateEnums::UNKNOWN_LIBRARY &&
-      this->GetType() != cmStateEnums::OBJECT_LIBRARY &&
-      this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
-      !this->IsExecutableWithExports()) {
-    return true;
-  }
-
-  auto verifyValue = this->GetProperty("INTERFACE_HEADER_SETS_TO_VERIFY");
-  bool const all = verifyValue.IsEmpty();
-  std::set<std::string> verifySet;
-  if (!all) {
-    cmList verifyList{ verifyValue };
-    verifySet.insert(verifyList.begin(), verifyList.end());
-  }
-
-  cmTarget* verifyTarget = nullptr;
-  cmTarget* allVerifyTarget =
-    this->GlobalGenerator->GetMakefiles().front()->FindTargetToUse(
-      "all_verify_interface_header_sets",
-      { cmStateEnums::TargetDomain::NATIVE });
+  for (bool const isInterface : { false, true }) {
+    if (!this->GetPropertyAsBool(isInterface ? "VERIFY_INTERFACE_HEADER_SETS"
+                                             : "VERIFY_PRIVATE_HEADER_SETS")) {
+      continue;
+    }
 
 
-  auto interfaceFileSetEntries = this->Target->GetInterfaceHeaderSetsEntries();
+    if (this->GetType() != cmStateEnums::STATIC_LIBRARY &&
+        this->GetType() != cmStateEnums::SHARED_LIBRARY &&
+        (this->GetType() != cmStateEnums::MODULE_LIBRARY || isInterface) &&
+        this->GetType() != cmStateEnums::UNKNOWN_LIBRARY &&
+        this->GetType() != cmStateEnums::OBJECT_LIBRARY &&
+        this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
+        this->GetType() != cmStateEnums::EXECUTABLE) {
+      continue;
+    }
 
 
-  std::set<cmFileSet*> fileSets;
-  for (auto const& entry : interfaceFileSetEntries) {
-    for (auto const& name : cmList{ entry.Value }) {
-      if (all || verifySet.count(name)) {
-        fileSets.insert(this->Target->GetFileSet(name));
-        verifySet.erase(name);
+    char const* headerSetsProperty = isInterface
+      ? "INTERFACE_HEADER_SETS_TO_VERIFY"
+      : "PRIVATE_HEADER_SETS_TO_VERIFY";
+
+    auto verifyValue = this->GetProperty(headerSetsProperty);
+    bool const all = verifyValue.IsEmpty();
+    std::set<std::string> verifySet;
+    if (!all) {
+      cmList verifyList{ verifyValue };
+      verifySet.insert(verifyList.begin(), verifyList.end());
+    }
+
+    cmTarget* verifyTarget = nullptr;
+    std::string const verifyTargetName =
+      cmStrCat(this->GetName(),
+               isInterface ? "_verify_interface_header_sets"
+                           : "_verify_private_header_sets");
+
+    char const* allVerifyTargetName = isInterface
+      ? "all_verify_interface_header_sets"
+      : "all_verify_private_header_sets";
+    cmTarget* allVerifyTarget =
+      this->GlobalGenerator->GetMakefiles().front()->FindTargetToUse(
+        allVerifyTargetName, { cmStateEnums::TargetDomain::NATIVE });
+
+    auto fileSetEntries = isInterface
+      ? this->Target->GetInterfaceHeaderSetsEntries()
+      : this->Target->GetHeaderSetsEntries();
+
+    std::set<cmFileSet*> fileSets;
+    for (auto const& entry : fileSetEntries) {
+      for (auto const& name : cmList{ entry.Value }) {
+        if (all || verifySet.count(name)) {
+          fileSets.insert(this->Target->GetFileSet(name));
+          verifySet.erase(name);
+        }
       }
       }
     }
     }
-  }
-  if (!verifySet.empty()) {
-    this->Makefile->IssueMessage(
-      MessageType::FATAL_ERROR,
-      cmStrCat("Property INTERFACE_HEADER_SETS_TO_VERIFY of target \"",
-               this->GetName(),
-               "\" contained the following header sets that are nonexistent "
-               "or not INTERFACE:\n  ",
-               cmJoin(verifySet, "\n  ")));
-    return false;
-  }
 
 
-  cm::optional<std::set<std::string>> languages;
-  for (auto* fileSet : fileSets) {
-    auto dirCges = fileSet->CompileDirectoryEntries();
-    auto fileCges = fileSet->CompileFileEntries();
-
-    static auto const contextSensitive =
-      [](std::unique_ptr<cmCompiledGeneratorExpression> const& cge) {
-        return cge->GetHadContextSensitiveCondition();
-      };
-    bool dirCgesContextSensitive = false;
-    bool fileCgesContextSensitive = false;
-
-    std::vector<std::string> dirs;
-    std::map<std::string, std::vector<std::string>> filesPerDir;
-    bool first = true;
-    for (auto const& config : this->Makefile->GetGeneratorConfigs(
-           cmMakefile::GeneratorConfigQuery::IncludeEmptyConfig)) {
-      cm::GenEx::Context context(this->LocalGenerator, config);
-      if (first || dirCgesContextSensitive) {
-        dirs = fileSet->EvaluateDirectoryEntries(dirCges, context, this);
-        dirCgesContextSensitive =
-          std::any_of(dirCges.begin(), dirCges.end(), contextSensitive);
-      }
-      if (first || fileCgesContextSensitive) {
-        filesPerDir.clear();
-        for (auto const& fileCge : fileCges) {
-          fileSet->EvaluateFileEntry(dirs, filesPerDir, fileCge, context,
-                                     this);
-          if (fileCge->GetHadContextSensitiveCondition()) {
-            fileCgesContextSensitive = true;
-          }
+    if (isInterface) {
+      cmPolicies::PolicyStatus const cmp0209 = this->GetPolicyStatusCMP0209();
+      if (cmp0209 != cmPolicies::NEW &&
+          this->GetType() == cmStateEnums::EXECUTABLE &&
+          !this->GetPropertyAsBool("ENABLE_EXPORTS")) {
+        if (cmp0209 == cmPolicies::WARN && !fileSets.empty()) {
+          this->Makefile->IssueMessage(
+            MessageType::AUTHOR_WARNING,
+            cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0209),
+                     "\n"
+                     "Executable target \"",
+                     this->GetName(),
+                     "\" has interface header file sets, but it does not "
+                     "enable exports. Those headers would be verified under "
+                     "CMP0209 NEW behavior.\n"));
         }
         }
+        continue;
       }
       }
+    }
 
 
-      for (auto const& files : filesPerDir) {
-        for (auto const& file : files.second) {
-          std::string filename = this->GenerateHeaderSetVerificationFile(
-            *this->Makefile->GetOrCreateSource(file), files.first, languages);
-          if (filename.empty()) {
-            continue;
-          }
+    if (!verifySet.empty()) {
+      this->Makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Property ", headerSetsProperty, " of target \"",
+                 this->GetName(),
+                 "\" contained the following header sets that are nonexistent "
+                 "or not ",
+                 isInterface ? "INTERFACE" : "PRIVATE", ":\n  ",
+                 cmJoin(verifySet, "\n  ")));
+      return false;
+    }
 
 
-          if (!verifyTarget) {
-            {
-              cmMakefile::PolicyPushPop polScope(this->Makefile);
-              this->Makefile->SetPolicy(cmPolicies::CMP0119, cmPolicies::NEW);
-              verifyTarget = this->Makefile->AddLibrary(
-                cmStrCat(this->GetName(), "_verify_interface_header_sets"),
-                cmStateEnums::OBJECT_LIBRARY, {}, true);
+    cm::optional<std::set<std::string>> languages;
+    for (auto* fileSet : fileSets) {
+      auto dirCges = fileSet->CompileDirectoryEntries();
+      auto fileCges = fileSet->CompileFileEntries();
+
+      static auto const contextSensitive =
+        [](std::unique_ptr<cmCompiledGeneratorExpression> const& cge) {
+          return cge->GetHadContextSensitiveCondition();
+        };
+      bool dirCgesContextSensitive = false;
+      bool fileCgesContextSensitive = false;
+
+      std::vector<std::string> dirs;
+      std::map<std::string, std::vector<std::string>> filesPerDir;
+      bool first = true;
+      for (auto const& config : this->Makefile->GetGeneratorConfigs(
+             cmMakefile::GeneratorConfigQuery::IncludeEmptyConfig)) {
+        cm::GenEx::Context context(this->LocalGenerator, config);
+        if (first || dirCgesContextSensitive) {
+          dirs = fileSet->EvaluateDirectoryEntries(dirCges, context, this);
+          dirCgesContextSensitive =
+            std::any_of(dirCges.begin(), dirCges.end(), contextSensitive);
+        }
+        if (first || fileCgesContextSensitive) {
+          filesPerDir.clear();
+          for (auto const& fileCge : fileCges) {
+            fileSet->EvaluateFileEntry(dirs, filesPerDir, fileCge, context,
+                                       this);
+            if (fileCge->GetHadContextSensitiveCondition()) {
+              fileCgesContextSensitive = true;
             }
             }
+          }
+        }
 
 
-            verifyTarget->AddLinkLibrary(
-              *this->Makefile, this->GetName(),
-              cmTargetLinkLibraryType::GENERAL_LibraryType);
-            verifyTarget->SetProperty("AUTOMOC", "OFF");
-            verifyTarget->SetProperty("AUTORCC", "OFF");
-            verifyTarget->SetProperty("AUTOUIC", "OFF");
-            verifyTarget->SetProperty("DISABLE_PRECOMPILE_HEADERS", "ON");
-            verifyTarget->SetProperty("UNITY_BUILD", "OFF");
-            verifyTarget->SetProperty("CXX_SCAN_FOR_MODULES", "OFF");
-            verifyTarget->FinalizeTargetConfiguration(
-              this->Makefile->GetCompileDefinitionsEntries());
-
-            if (!allVerifyTarget) {
-              allVerifyTarget = this->GlobalGenerator->GetMakefiles()
-                                  .front()
-                                  ->AddNewUtilityTarget(
-                                    "all_verify_interface_header_sets", true);
+        for (auto const& files : filesPerDir) {
+          for (auto const& file : files.second) {
+            std::string filename = this->GenerateHeaderSetVerificationFile(
+              *this->Makefile->GetOrCreateSource(file), files.first,
+              verifyTargetName, languages);
+            if (filename.empty()) {
+              continue;
             }
             }
 
 
-            allVerifyTarget->AddUtility(verifyTarget->GetName(), false);
-          }
+            if (!verifyTarget) {
+              {
+                cmMakefile::PolicyPushPop polScope(this->Makefile);
+                this->Makefile->SetPolicy(cmPolicies::CMP0119,
+                                          cmPolicies::NEW);
+                verifyTarget = this->Makefile->AddLibrary(
+                  verifyTargetName, cmStateEnums::OBJECT_LIBRARY, {}, true);
+              }
+
+              if (isInterface) {
+                // Link to the original target so that we pick up its
+                // interface compile options just like a consumer would.
+                // This also ensures any generated headers in the original
+                // target will be created.
+                verifyTarget->AddLinkLibrary(
+                  *this->Makefile, this->GetName(),
+                  cmTargetLinkLibraryType::GENERAL_LibraryType);
+              } else {
+                // For private file sets, we need to simulate compiling the
+                // same way as the original target. That includes linking to
+                // the same things so we pick up the same transitive
+                // properties. For the <LANG>_... properties, we don't care if
+                // we set them for languages this target won't eventually use.
+                // The verify header sets feature currently only supports the
+                // C and C++ languages, so we just always set those here for
+                // simplicity rather than working out all languages the target
+                // has to compile for.
+                static std::vector<std::string> propertiesToCopy = {
+                  "COMPILE_DEFINITIONS", "COMPILE_FEATURES",
+                  "COMPILE_FLAGS",       "COMPILE_OPTIONS",
+                  "DEFINE_SYMBOL",       "INCLUDE_DIRECTORIES",
+                  "LINK_LIBRARIES",      "C_STANDARD",
+                  "C_STANDARD_REQUIRED", "C_EXTENSIONS",
+                  "CXX_STANDARD",        "CXX_STANDARD_REQUIRED",
+                  "CXX_EXTENSIONS"
+                };
+                for (std::string const& prop : propertiesToCopy) {
+                  cmValue propValue = this->Target->GetProperty(prop);
+                  if (propValue.IsSet()) {
+                    verifyTarget->SetProperty(prop, propValue);
+                  }
+                }
+                // The original target might have generated headers. Since
+                // we only link to the original target for compilation,
+                // there's nothing to force such generation to happen yet.
+                // Our verify target must depend on the original target to
+                // ensure such generated files will be created.
+                verifyTarget->AddUtility(this->GetName(), false,
+                                         this->Makefile);
+                verifyTarget->AddCodegenDependency(this->GetName());
+              }
+
+              verifyTarget->SetProperty("AUTOMOC", "OFF");
+              verifyTarget->SetProperty("AUTORCC", "OFF");
+              verifyTarget->SetProperty("AUTOUIC", "OFF");
+              verifyTarget->SetProperty("DISABLE_PRECOMPILE_HEADERS", "ON");
+              verifyTarget->SetProperty("UNITY_BUILD", "OFF");
+              verifyTarget->SetProperty("CXX_SCAN_FOR_MODULES", "OFF");
+
+              if (isInterface) {
+                verifyTarget->FinalizeTargetConfiguration(
+                  this->Makefile->GetCompileDefinitionsEntries());
+              } else {
+                // Private verification only needs to add the directory scope
+                // definitions here
+                for (auto const& def :
+                     this->Makefile->GetCompileDefinitionsEntries()) {
+                  verifyTarget->InsertCompileDefinition(def);
+                }
+              }
+
+              if (!allVerifyTarget) {
+                allVerifyTarget =
+                  this->GlobalGenerator->GetMakefiles()
+                    .front()
+                    ->AddNewUtilityTarget(allVerifyTargetName, true);
+              }
+
+              allVerifyTarget->AddUtility(verifyTargetName, false);
+            }
 
 
-          if (fileCgesContextSensitive) {
-            filename = cmStrCat("$<$<CONFIG:", config, ">:", filename, '>');
+            if (fileCgesContextSensitive) {
+              filename = cmStrCat("$<$<CONFIG:", config, ">:", filename, '>');
+            }
+            verifyTarget->AddSource(filename);
           }
           }
-          verifyTarget->AddSource(filename);
         }
         }
-      }
 
 
-      if (!dirCgesContextSensitive && !fileCgesContextSensitive) {
-        break;
+        if (!dirCgesContextSensitive && !fileCgesContextSensitive) {
+          break;
+        }
+        first = false;
       }
       }
-      first = false;
     }
     }
-  }
 
 
-  if (verifyTarget) {
-    this->LocalGenerator->AddGeneratorTarget(
-      cm::make_unique<cmGeneratorTarget>(verifyTarget, this->LocalGenerator));
+    if (verifyTarget) {
+      this->LocalGenerator->AddGeneratorTarget(
+        cm::make_unique<cmGeneratorTarget>(verifyTarget,
+                                           this->LocalGenerator));
+    }
   }
   }
 
 
   return true;
   return true;
@@ -5855,6 +5947,7 @@ bool cmGeneratorTarget::AddHeaderSetVerification()
 
 
 std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
 std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
   cmSourceFile& source, std::string const& dir,
   cmSourceFile& source, std::string const& dir,
+  std::string const& verifyTargetName,
   cm::optional<std::set<std::string>>& languages) const
   cm::optional<std::set<std::string>>& languages) const
 {
 {
   std::string extension;
   std::string extension;
@@ -5908,9 +6001,9 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
   }
   }
   headerFilename += source.GetLocation().GetName();
   headerFilename += source.GetLocation().GetName();
 
 
-  auto filename = cmStrCat(
-    this->LocalGenerator->GetCurrentBinaryDirectory(), '/', this->GetName(),
-    "_verify_interface_header_sets/", headerFilename, extension);
+  auto filename =
+    cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+             verifyTargetName, '/', headerFilename, extension);
   auto* verificationSource = this->Makefile->GetOrCreateSource(filename);
   auto* verificationSource = this->Makefile->GetOrCreateSource(filename);
   source.SetSpecialSourceType(
   source.SetSpecialSourceType(
     cmSourceFile::SpecialSourceType::HeaderSetVerificationSource);
     cmSourceFile::SpecialSourceType::HeaderSetVerificationSource);

+ 1 - 0
Source/cmGeneratorTarget.h

@@ -1112,6 +1112,7 @@ public:
   bool AddHeaderSetVerification();
   bool AddHeaderSetVerification();
   std::string GenerateHeaderSetVerificationFile(
   std::string GenerateHeaderSetVerificationFile(
     cmSourceFile& source, std::string const& dir,
     cmSourceFile& source, std::string const& dir,
+    std::string const& verifyTargetName,
     cm::optional<std::set<std::string>>& languages) const;
     cm::optional<std::set<std::string>>& languages) const;
 
 
   std::string GetImportedXcFrameworkPath(std::string const& config) const;
   std::string GetImportedXcFrameworkPath(std::string const& config) const;

+ 27 - 4
Source/cmGlobalGenerator.cxx

@@ -1861,13 +1861,36 @@ bool cmGlobalGenerator::AddHeaderSetVerification()
     }
     }
   }
   }
 
 
-  cmTarget* allVerifyTarget = this->Makefiles.front()->FindTargetToUse(
-    "all_verify_interface_header_sets",
-    { cmStateEnums::TargetDomain::NATIVE });
-  if (allVerifyTarget) {
+  cmTarget* allVerifyInterfaceTarget =
+    this->Makefiles.front()->FindTargetToUse(
+      "all_verify_interface_header_sets",
+      { cmStateEnums::TargetDomain::NATIVE });
+  if (allVerifyInterfaceTarget) {
+    this->LocalGenerators.front()->AddGeneratorTarget(
+      cm::make_unique<cmGeneratorTarget>(allVerifyInterfaceTarget,
+                                         this->LocalGenerators.front().get()));
+  }
+  cmTarget* allVerifyPrivateTarget = this->Makefiles.front()->FindTargetToUse(
+    "all_verify_private_header_sets", { cmStateEnums::TargetDomain::NATIVE });
+  if (allVerifyPrivateTarget) {
+    this->LocalGenerators.front()->AddGeneratorTarget(
+      cm::make_unique<cmGeneratorTarget>(allVerifyPrivateTarget,
+                                         this->LocalGenerators.front().get()));
+  }
+
+  if (allVerifyInterfaceTarget || allVerifyPrivateTarget) {
+    cmTarget* allVerifyTarget =
+      this->GetMakefiles().front()->AddNewUtilityTarget(
+        "all_verify_header_sets", true);
     this->LocalGenerators.front()->AddGeneratorTarget(
     this->LocalGenerators.front()->AddGeneratorTarget(
       cm::make_unique<cmGeneratorTarget>(allVerifyTarget,
       cm::make_unique<cmGeneratorTarget>(allVerifyTarget,
                                          this->LocalGenerators.front().get()));
                                          this->LocalGenerators.front().get()));
+    if (allVerifyInterfaceTarget) {
+      allVerifyTarget->AddUtility(allVerifyInterfaceTarget->GetName(), false);
+    }
+    if (allVerifyPrivateTarget) {
+      allVerifyTarget->AddUtility(allVerifyPrivateTarget->GetName(), false);
+    }
   }
   }
 
 
   return true;
   return true;

+ 5 - 1
Source/cmPolicies.h

@@ -623,6 +623,9 @@ class cmMakefile;
          "file(GET_RUNTIME_DEPENDENCIES) normalizes paths before matching.",  \
          "file(GET_RUNTIME_DEPENDENCIES) normalizes paths before matching.",  \
          4, 3, 0, WARN)                                                       \
          4, 3, 0, WARN)                                                       \
   SELECT(POLICY, CMP0208, "export(EXPORT) does not allow empty arguments.",   \
   SELECT(POLICY, CMP0208, "export(EXPORT) does not allow empty arguments.",   \
+         4, 3, 0, WARN)                                                       \
+  SELECT(POLICY, CMP0209,                                                     \
+         "Verify interface header sets checks executables without exports.",  \
          4, 3, 0, WARN)
          4, 3, 0, WARN)
 
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
@@ -676,7 +679,8 @@ class cmMakefile;
   F(CMP0200)                                                                  \
   F(CMP0200)                                                                  \
   F(CMP0202)                                                                  \
   F(CMP0202)                                                                  \
   F(CMP0203)                                                                  \
   F(CMP0203)                                                                  \
-  F(CMP0204)
+  F(CMP0204)                                                                  \
+  F(CMP0209)
 
 
 #define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F)                                  \
 #define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F)                                  \
   F(CMP0116)                                                                  \
   F(CMP0116)                                                                  \

+ 1 - 0
Source/cmTarget.cxx

@@ -498,6 +498,7 @@ TargetProperty const StaticTargetProperties[] = {
   { "UNITY_BUILD_RELOCATABLE"_s, IC::CanCompileSources },
   { "UNITY_BUILD_RELOCATABLE"_s, IC::CanCompileSources },
   { "OPTIMIZE_DEPENDENCIES"_s, IC::CanCompileSources },
   { "OPTIMIZE_DEPENDENCIES"_s, IC::CanCompileSources },
   { "VERIFY_INTERFACE_HEADER_SETS"_s },
   { "VERIFY_INTERFACE_HEADER_SETS"_s },
+  { "VERIFY_PRIVATE_HEADER_SETS"_s },
   // -- Android
   // -- Android
   { "ANDROID_ANT_ADDITIONAL_OPTIONS"_s, IC::CanCompileSources },
   { "ANDROID_ANT_ADDITIONAL_OPTIONS"_s, IC::CanCompileSources },
   { "ANDROID_PROCESS_MAX"_s, IC::CanCompileSources },
   { "ANDROID_PROCESS_MAX"_s, IC::CanCompileSources },

+ 7 - 3
Tests/RunCMake/FetchContent/VerifyHeaderSet-stdout.txt

@@ -1,4 +1,8 @@
--- Before subproject, var = 'TRUE'
--- Inside subproject, var = 'FALSE'
--- After subproject, var = 'TRUE'
+-- Before subproject, interface var = 'TRUE'
+-- Before subproject, private var = 'TRUE'
+-- Inside subproject, interface var = 'FALSE'
+-- Inside subproject, private var = 'FALSE'
+-- After subproject, interface var = 'TRUE'
+-- After subproject, private var = 'TRUE'
 -- Subproject target property VERIFY_INTERFACE_HEADER_SETS='FALSE'
 -- Subproject target property VERIFY_INTERFACE_HEADER_SETS='FALSE'
+-- Subproject target property VERIFY_PRIVATE_HEADER_SETS='FALSE'

+ 9 - 4
Tests/RunCMake/FetchContent/VerifyHeaderSet.cmake

@@ -1,16 +1,21 @@
 enable_language(C)
 enable_language(C)
 
 
 set(CMAKE_VERIFY_INTERFACE_HEADER_SETS TRUE)
 set(CMAKE_VERIFY_INTERFACE_HEADER_SETS TRUE)
+set(CMAKE_VERIFY_PRIVATE_HEADER_SETS TRUE)
 
 
 include(FetchContent)
 include(FetchContent)
 FetchContent_Declare(verify_subproj
 FetchContent_Declare(verify_subproj
   SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/VerifyHeaderSet
   SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/VerifyHeaderSet
 )
 )
-message(STATUS "Before subproject, var = '${CMAKE_VERIFY_INTERFACE_HEADER_SETS}'")
+message(STATUS "Before subproject, interface var = '${CMAKE_VERIFY_INTERFACE_HEADER_SETS}'")
+message(STATUS "Before subproject, private var = '${CMAKE_VERIFY_PRIVATE_HEADER_SETS}'")
 FetchContent_MakeAvailable(verify_subproj)
 FetchContent_MakeAvailable(verify_subproj)
 
 
 # Provide a way to verify the variable was reset back to its original value
 # Provide a way to verify the variable was reset back to its original value
-message(STATUS "After subproject, var = '${CMAKE_VERIFY_INTERFACE_HEADER_SETS}'")
+message(STATUS "After subproject, interface var = '${CMAKE_VERIFY_INTERFACE_HEADER_SETS}'")
+message(STATUS "After subproject, private var = '${CMAKE_VERIFY_PRIVATE_HEADER_SETS}'")
 
 
-get_property(verify TARGET Blah PROPERTY VERIFY_INTERFACE_HEADER_SETS)
-message(STATUS "Subproject target property VERIFY_INTERFACE_HEADER_SETS='${verify}'")
+get_property(verify_interface TARGET Blah PROPERTY VERIFY_INTERFACE_HEADER_SETS)
+get_property(verify_private TARGET Blah PROPERTY VERIFY_PRIVATE_HEADER_SETS)
+message(STATUS "Subproject target property VERIFY_INTERFACE_HEADER_SETS='${verify_interface}'")
+message(STATUS "Subproject target property VERIFY_PRIVATE_HEADER_SETS='${verify_private}'")

+ 3 - 2
Tests/RunCMake/FetchContent/VerifyHeaderSet/CMakeLists.txt

@@ -1,9 +1,10 @@
 cmake_minimum_required(VERSION 3.24)
 cmake_minimum_required(VERSION 3.24)
 project(VerifyHeaderSet LANGUAGES C)
 project(VerifyHeaderSet LANGUAGES C)
 
 
-message(STATUS "Inside subproject, var = '${CMAKE_VERIFY_INTERFACE_HEADER_SETS}'")
+message(STATUS "Inside subproject, interface var = '${CMAKE_VERIFY_INTERFACE_HEADER_SETS}'")
+message(STATUS "Inside subproject, private var = '${CMAKE_VERIFY_PRIVATE_HEADER_SETS}'")
 
 
 add_library(Blah INTERFACE)
 add_library(Blah INTERFACE)
 target_sources(Blah
 target_sources(Blah
-  INTERFACE FILE_SET HEADERS FILES blah.h
+  PUBLIC FILE_SET HEADERS FILES blah.h
 )
 )

+ 1 - 0
Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt

@@ -52,6 +52,7 @@
    \* CMP0202
    \* CMP0202
    \* CMP0203
    \* CMP0203
    \* CMP0204
    \* CMP0204
+   \* CMP0209
 
 
 Call Stack \(most recent call first\):
 Call Stack \(most recent call first\):
   CMakeLists\.txt:3 \(include\)
   CMakeLists\.txt:3 \(include\)

+ 10 - 0
Tests/RunCMake/VerifyHeaderSets/AllVerifyPrivateHeaderSets-all_verify_private_header_sets-Debug-build-check.cmake

@@ -0,0 +1,10 @@
+# A custom command is used to copy the header file from the source directory to
+# the binary directory. If the verification target was built, the custom
+# command should have been executed, and the file should be present in the
+# binary directory.
+if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/dir3/lib3.h")
+  string(APPEND RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/dir3/lib3.h should exist but it does not\n")
+endif()
+if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/dir4/lib4.h")
+  string(APPEND RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/dir4/lib4.h should exist but it does not\n")
+endif()

+ 4 - 0
Tests/RunCMake/VerifyHeaderSets/AllVerifyPrivateHeaderSets.cmake

@@ -0,0 +1,4 @@
+enable_language(C)
+
+add_subdirectory(dir3)
+add_subdirectory(dir4)

+ 74 - 24
Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake

@@ -1,55 +1,105 @@
 include(RunCMake)
 include(RunCMake)
 
 
-function(run_cmake_build name target)
+function(run_cmake_build name verifyType target)
   if(NOT BUILD_CONFIG)
   if(NOT BUILD_CONFIG)
     set(BUILD_CONFIG Debug)
     set(BUILD_CONFIG Debug)
   endif()
   endif()
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
   set(RunCMake_TEST_NO_CLEAN 1)
   set(RunCMake_TEST_NO_CLEAN 1)
-  run_cmake_command(${name}-${target}-${BUILD_CONFIG}-build ${CMAKE_COMMAND} --build . --config ${BUILD_CONFIG} --target ${target})
+  run_cmake_command(${name}-${target}-${BUILD_CONFIG}-build
+    ${CMAKE_COMMAND} --build . --config ${BUILD_CONFIG} --target ${target}_verify_${verifyType}_header_sets
+  )
 endfunction()
 endfunction()
 
 
 set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_INTERFACE_HEADER_SETS=ON)
 set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_INTERFACE_HEADER_SETS=ON)
 if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
 if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
   list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
   list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
 endif()
 endif()
-run_cmake(VerifyHeaderSets)
+run_cmake(VerifyInterfaceHeaderSets)
 unset(RunCMake_TEST_OPTIONS)
 unset(RunCMake_TEST_OPTIONS)
 
 
-run_cmake_build(VerifyHeaderSets static_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets shared_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets object_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets interface_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets exe_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets export_exe_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets none_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets property_off_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets private_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets a_h_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets dir_c_h_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets dir_cxx_h_verify_interface_header_sets)
+run_cmake_build(VerifyInterfaceHeaderSets interface static)
+run_cmake_build(VerifyInterfaceHeaderSets interface shared)
+run_cmake_build(VerifyInterfaceHeaderSets interface object)
+run_cmake_build(VerifyInterfaceHeaderSets interface interface)
+run_cmake_build(VerifyInterfaceHeaderSets interface exe)
+run_cmake_build(VerifyInterfaceHeaderSets interface export_exe)
+run_cmake_build(VerifyInterfaceHeaderSets interface none)
+run_cmake_build(VerifyInterfaceHeaderSets interface property_off)
+run_cmake_build(VerifyInterfaceHeaderSets interface private)
+run_cmake_build(VerifyInterfaceHeaderSets interface a_h)
+run_cmake_build(VerifyInterfaceHeaderSets interface dir_c_h)
+run_cmake_build(VerifyInterfaceHeaderSets interface dir_cxx_h)
 
 
 if(NOT RunCMake_GENERATOR STREQUAL "Xcode")
 if(NOT RunCMake_GENERATOR STREQUAL "Xcode")
-  run_cmake_build(VerifyHeaderSets config_verify_interface_header_sets)
+  run_cmake_build(VerifyInterfaceHeaderSets interface config)
   if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
   if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
     set(BUILD_CONFIG Release)
     set(BUILD_CONFIG Release)
-    run_cmake_build(VerifyHeaderSets config_verify_interface_header_sets)
+    run_cmake_build(VerifyInterfaceHeaderSets interface config)
     unset(BUILD_CONFIG)
     unset(BUILD_CONFIG)
   endif()
   endif()
 endif()
 endif()
 
 
-run_cmake_build(VerifyHeaderSets lang_test_c_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets lang_test_cxx_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets interface_lang_test_cxx_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets list_verify_interface_header_sets)
-run_cmake_build(VerifyHeaderSets skip_linting_verify_interface_header_sets)
+run_cmake_build(VerifyInterfaceHeaderSets interface lang_test_c)
+run_cmake_build(VerifyInterfaceHeaderSets interface lang_test_cxx)
+run_cmake_build(VerifyInterfaceHeaderSets interface iface_lang_cxx)
+run_cmake_build(VerifyInterfaceHeaderSets interface list)
+run_cmake_build(VerifyInterfaceHeaderSets interface skip_linting)
 
 
 set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_INTERFACE_HEADER_SETS=ON)
 set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_INTERFACE_HEADER_SETS=ON)
 run_cmake(AllVerifyInterfaceHeaderSets)
 run_cmake(AllVerifyInterfaceHeaderSets)
 unset(RunCMake_TEST_OPTIONS)
 unset(RunCMake_TEST_OPTIONS)
 
 
-run_cmake_build(AllVerifyInterfaceHeaderSets all_verify_interface_header_sets)
+run_cmake_build(AllVerifyInterfaceHeaderSets interface all)
 
 
 set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_INTERFACE_HEADER_SETS=ON)
 set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_INTERFACE_HEADER_SETS=ON)
-run_cmake(VerifyHeaderSetsNonexistent)
+run_cmake(VerifyInterfaceHeaderSetsNonexistent)
+unset(RunCMake_TEST_OPTIONS)
+
+run_cmake(VerifyInterfaceHeaderSets-CMP0209-NEW)
+run_cmake(VerifyInterfaceHeaderSets-CMP0209-OLD)
+run_cmake(VerifyInterfaceHeaderSets-CMP0209-WARN)
+
+set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_PRIVATE_HEADER_SETS=ON)
+if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+endif()
+run_cmake(VerifyPrivateHeaderSets)
+unset(RunCMake_TEST_OPTIONS)
+
+run_cmake_build(VerifyPrivateHeaderSets private static)
+run_cmake_build(VerifyPrivateHeaderSets private shared)
+run_cmake_build(VerifyPrivateHeaderSets private object)
+run_cmake_build(VerifyPrivateHeaderSets private interface)
+run_cmake_build(VerifyPrivateHeaderSets private iface_private)
+run_cmake_build(VerifyPrivateHeaderSets private exe)
+run_cmake_build(VerifyPrivateHeaderSets private none)
+run_cmake_build(VerifyPrivateHeaderSets private property_off)
+run_cmake_build(VerifyPrivateHeaderSets private a_h)
+run_cmake_build(VerifyPrivateHeaderSets private dir_c_h)
+run_cmake_build(VerifyPrivateHeaderSets private dir_cxx_h)
+
+if(NOT RunCMake_GENERATOR STREQUAL "Xcode")
+  run_cmake_build(VerifyPrivateHeaderSets private config)
+  if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(BUILD_CONFIG Release)
+    run_cmake_build(VerifyPrivateHeaderSets private config)
+    unset(BUILD_CONFIG)
+  endif()
+endif()
+
+run_cmake_build(VerifyPrivateHeaderSets private lang_test_c)
+run_cmake_build(VerifyPrivateHeaderSets private lang_test_cxx)
+run_cmake_build(VerifyPrivateHeaderSets private iface_lang_cxx)
+run_cmake_build(VerifyPrivateHeaderSets private list)
+run_cmake_build(VerifyPrivateHeaderSets private skip_linting)
+
+set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_PRIVATE_HEADER_SETS=ON)
+run_cmake(AllVerifyPrivateHeaderSets)
+unset(RunCMake_TEST_OPTIONS)
+
+run_cmake_build(AllVerifyPrivateHeaderSets private all)
+
+set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_PRIVATE_HEADER_SETS=ON)
+run_cmake(VerifyPrivateHeaderSetsNonexistent)
 unset(RunCMake_TEST_OPTIONS)
 unset(RunCMake_TEST_OPTIONS)

+ 2 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209-NEW.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0209 NEW)
+include(VerifyInterfaceHeaderSets-CMP0209.cmake)

+ 2 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209-OLD.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0209 OLD)
+include(VerifyInterfaceHeaderSets-CMP0209.cmake)

+ 10 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209-WARN-stderr.txt

@@ -0,0 +1,10 @@
+CMake Warning \(dev\) in CMakeLists\.txt:
+  Policy CMP0209 is not set: Verify interface header sets checks executables
+  without exports\.  Run "cmake --help-policy CMP0209" for policy details\.
+  Use the cmake_policy command to set the policy and suppress this warning\.
+
+  Executable target "exe" has interface header file sets, but it does not
+  enable exports\.  Those headers would be verified under CMP0209 NEW
+  behavior\.
+
+This warning is for project developers\.  Use -Wno-dev to suppress it\.

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209-WARN.cmake

@@ -0,0 +1 @@
+include(VerifyInterfaceHeaderSets-CMP0209.cmake)

+ 16 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-CMP0209.cmake

@@ -0,0 +1,16 @@
+enable_language(C CXX)
+
+add_compile_definitions(TEST_ADD_COMPILE_DEFINITIONS)
+
+set_property(SOURCE a.h PROPERTY LANGUAGE C)
+set_property(SOURCE dir/c.h PROPERTY LANGUAGE C)
+set_property(SOURCE dir/cxx.h PROPERTY LANGUAGE CXX)
+
+set(CMAKE_VERIFY_INTERFACE_HEADER_SETS ON)
+
+add_executable(exe main.c)
+target_sources(exe INTERFACE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)
+
+add_executable(export_exe main.c)
+set_property(TARGET export_exe PROPERTY ENABLE_EXPORTS TRUE)
+target_sources(export_exe INTERFACE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)

+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-a_h_verify_interface_header_sets-Debug-build-result.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-a_h-Debug-build-result.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-a_h_verify_interface_header_sets-Debug-build-stderr.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-a_h-Debug-build-stderr.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-a_h_verify_interface_header_sets-Debug-build-stdout.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-a_h-Debug-build-stdout.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-check.cmake → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-check.cmake


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-config_verify_interface_header_sets-Debug-build-result.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-config-Debug-build-result.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-config_verify_interface_header_sets-Debug-build-stderr.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-config-Debug-build-stderr.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-config_verify_interface_header_sets-Debug-build-stdout.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-config-Debug-build-stdout.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-config_verify_interface_header_sets-Release-build-result.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-config-Release-build-result.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-config_verify_interface_header_sets-Release-build-stderr.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-config-Release-build-stderr.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-config_verify_interface_header_sets-Release-build-stdout.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-config-Release-build-stdout.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-dir_c_h_verify_interface_header_sets-Debug-build-result.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-dir_c_h-Debug-build-result.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-dir_c_h_verify_interface_header_sets-Debug-build-stderr.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-dir_c_h-Debug-build-stderr.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-dir_c_h_verify_interface_header_sets-Debug-build-stdout.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-dir_c_h-Debug-build-stdout.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-dir_cxx_h_verify_interface_header_sets-Debug-build-result.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-dir_cxx_h-Debug-build-result.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-dir_cxx_h_verify_interface_header_sets-Debug-build-stderr.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-dir_cxx_h-Debug-build-stderr.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-dir_cxx_h_verify_interface_header_sets-Debug-build-stdout.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-dir_cxx_h-Debug-build-stdout.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-exe_verify_interface_header_sets-Debug-build-result.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-exe-Debug-build-result.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-exe_verify_interface_header_sets-Debug-build-stderr.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-exe-Debug-build-stderr.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-none_verify_interface_header_sets-Debug-build-result.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-result.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-none_verify_interface_header_sets-Debug-build-stderr.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-none-Debug-build-stderr.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-private_verify_interface_header_sets-Debug-build-result.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-result.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-private_verify_interface_header_sets-Debug-build-stderr.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-private-Debug-build-stderr.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-property_off_verify_interface_header_sets-Debug-build-result.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-property_off-Debug-build-result.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-property_off_verify_interface_header_sets-Debug-build-stderr.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets-property_off-Debug-build-stderr.txt


+ 7 - 3
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets.cmake

@@ -1,3 +1,5 @@
+cmake_policy(SET CMP0209 NEW)
+
 enable_language(C CXX)
 enable_language(C CXX)
 
 
 add_compile_definitions(TEST_ADD_COMPILE_DEFINITIONS)
 add_compile_definitions(TEST_ADD_COMPILE_DEFINITIONS)
@@ -61,9 +63,11 @@ add_library(lang_test_cxx STATIC lib.c lib.cxx)
 target_compile_definitions(lang_test_cxx INTERFACE EXPECT_CXX)
 target_compile_definitions(lang_test_cxx INTERFACE EXPECT_CXX)
 target_sources(lang_test_cxx INTERFACE FILE_SET HEADERS FILES lang_test.h)
 target_sources(lang_test_cxx INTERFACE FILE_SET HEADERS FILES lang_test.h)
 
 
-add_library(interface_lang_test_cxx INTERFACE)
-target_compile_definitions(interface_lang_test_cxx INTERFACE EXPECT_CXX)
-target_sources(interface_lang_test_cxx INTERFACE FILE_SET HEADERS FILES lang_test.h)
+# Don't make the name of this target any longer or else it triggers a crash in the OpenWatcom compiler.
+# That crash only occurs when debug information is enabled, which is the case when this test runs.
+add_library(iface_lang_cxx INTERFACE)
+target_compile_definitions(iface_lang_cxx INTERFACE EXPECT_CXX)
+target_sources(iface_lang_cxx INTERFACE FILE_SET HEADERS FILES lang_test.h)
 
 
 set_property(SOURCE error.h PROPERTY LANGUAGE C)
 set_property(SOURCE error.h PROPERTY LANGUAGE C)
 
 

+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSetsNonexistent-result.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSetsNonexistent-result.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSetsNonexistent-stderr.txt → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSetsNonexistent-stderr.txt


+ 0 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSetsNonexistent.cmake → Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSetsNonexistent.cmake


+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-a_h-Debug-build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-a_h-Debug-build-stderr.txt

@@ -0,0 +1 @@
+(TEST_A_H defined)?

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-a_h-Debug-build-stdout.txt

@@ -0,0 +1 @@
+(TEST_A_H defined)?

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-config-Debug-build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-config-Debug-build-stderr.txt

@@ -0,0 +1 @@
+(Compiled in debug mode)?

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-config-Debug-build-stdout.txt

@@ -0,0 +1 @@
+(Compiled in debug mode)?

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-config-Release-build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-config-Release-build-stderr.txt

@@ -0,0 +1 @@
+(Compiled in release mode)?

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-config-Release-build-stdout.txt

@@ -0,0 +1 @@
+(Compiled in release mode)?

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-dir_c_h-Debug-build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-dir_c_h-Debug-build-stderr.txt

@@ -0,0 +1 @@
+(TEST_DIR_C_H defined)?

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-dir_c_h-Debug-build-stdout.txt

@@ -0,0 +1 @@
+(TEST_DIR_C_H defined)?

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-dir_cxx_h-Debug-build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-dir_cxx_h-Debug-build-stderr.txt

@@ -0,0 +1 @@
+(TEST_DIR_CXX_H defined)?

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-dir_cxx_h-Debug-build-stdout.txt

@@ -0,0 +1 @@
+(TEST_DIR_CXX_H defined)?

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-iface_lang_cxx-Debug-build-stderr.txt

@@ -0,0 +1 @@
+.*

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-interface-Debug-build-stderr.txt

@@ -0,0 +1 @@
+.*

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-none-Debug-build-stderr.txt

@@ -0,0 +1 @@
+.*

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-property_off-Debug-build-result.txt

@@ -0,0 +1 @@
+[^0]

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets-property_off-Debug-build-stderr.txt

@@ -0,0 +1 @@
+.*

+ 88 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets.cmake

@@ -0,0 +1,88 @@
+cmake_policy(SET CMP0209 NEW)
+
+enable_language(C CXX)
+
+add_compile_definitions(TEST_ADD_COMPILE_DEFINITIONS)
+
+set_property(SOURCE a.h PROPERTY LANGUAGE C)
+set_property(SOURCE dir/c.h PROPERTY LANGUAGE C)
+set_property(SOURCE dir/cxx.h PROPERTY LANGUAGE CXX)
+
+add_library(static STATIC lib.c)
+target_sources(static PRIVATE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)
+
+add_library(shared SHARED lib.c)
+target_sources(shared PRIVATE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)
+
+add_library(module MODULE lib.c)
+target_sources(module PRIVATE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)
+
+add_library(object OBJECT lib.c)
+target_sources(object PRIVATE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)
+
+add_library(interface INTERFACE)
+target_sources(interface INTERFACE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)
+
+# Though a bit strange, INTERFACE libraries can have PRIVATE file sets.
+# A more likely scenario would be generated headers which might be PUBLIC.
+# Don't make the name of this target any longer or else it triggers a crash in the OpenWatcom compiler.
+# That crash only occurs when debug information is enabled, which is the case when this test runs.
+add_library(iface_private INTERFACE)
+target_sources(iface_private PRIVATE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)
+
+add_executable(exe main.c)
+target_sources(exe PRIVATE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)
+
+add_library(none STATIC lib.c)
+
+add_library(property_off STATIC lib.c)
+target_sources(property_off PRIVATE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)
+set_property(TARGET property_off PROPERTY VERIFY_PRIVATE_HEADER_SETS OFF)
+
+add_library(a_h STATIC lib.c)
+target_compile_definitions(a_h PRIVATE TEST_A_H)
+target_sources(a_h PRIVATE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)
+
+add_library(dir_c_h STATIC lib.c)
+target_compile_definitions(dir_c_h PRIVATE TEST_DIR_C_H)
+target_sources(dir_c_h PRIVATE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)
+
+add_library(dir_cxx_h STATIC lib.c)
+target_compile_definitions(dir_cxx_h PRIVATE TEST_DIR_CXX_H)
+target_sources(dir_cxx_h PRIVATE FILE_SET HEADERS FILES a.h dir/c.h dir/cxx.h)
+
+set_property(SOURCE debug.h PROPERTY LANGUAGE C)
+set_property(SOURCE release.h PROPERTY LANGUAGE C)
+
+if(NOT CMAKE_GENERATOR STREQUAL "Xcode")
+  add_library(config STATIC lib.c)
+  target_sources(config PRIVATE FILE_SET HEADERS FILES $<IF:$<CONFIG:Debug>,debug.h,release.h>)
+endif()
+
+add_library(lang_test_c STATIC lib.c)
+target_sources(lang_test_c PRIVATE FILE_SET HEADERS FILES lang_test.h)
+
+add_library(lang_test_cxx STATIC lib.c lib.cxx)
+target_compile_definitions(lang_test_cxx PRIVATE EXPECT_CXX)
+target_sources(lang_test_cxx PRIVATE FILE_SET HEADERS FILES lang_test.h)
+
+# Don't make the name of this target any longer or else it triggers a crash in the OpenWatcom compiler.
+# That crash only occurs when debug information is enabled, which is the case when this test runs.
+add_library(iface_lang_cxx INTERFACE)
+target_compile_definitions(iface_lang_cxx INTERFACE EXPECT_CXX)
+target_sources(iface_lang_cxx INTERFACE FILE_SET HEADERS FILES lang_test.h)
+
+set_property(SOURCE error.h PROPERTY LANGUAGE C)
+
+add_library(list STATIC lib.c)
+target_sources(list PRIVATE
+  FILE_SET a TYPE HEADERS FILES a.h
+  FILE_SET c TYPE HEADERS FILES dir/c.h
+  FILE_SET error TYPE HEADERS FILES error.h
+)
+set_property(TARGET list PROPERTY PRIVATE_HEADER_SETS_TO_VERIFY "a;c")
+
+add_library(skip_linting STATIC lib.c)
+target_sources(skip_linting PRIVATE FILE_SET HEADERS FILES lang_test.h skip_linting.h)
+set_property(SOURCE skip_linting.h PROPERTY LANGUAGE C)
+set_property(SOURCE skip_linting.h PROPERTY SKIP_LINTING TRUE)

+ 1 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSetsNonexistent-result.txt

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

+ 9 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSetsNonexistent-stderr.txt

@@ -0,0 +1,9 @@
+^CMake Error in CMakeLists\.txt:
+  Property PRIVATE_HEADER_SETS_TO_VERIFY of target "nonexistent" contained
+  the following header sets that are nonexistent or not PRIVATE:
+
+    b
+    c
+
+
+CMake Generate step failed\.  Build files cannot be regenerated correctly\.$

+ 5 - 0
Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSetsNonexistent.cmake

@@ -0,0 +1,5 @@
+enable_language(C)
+
+add_library(nonexistent STATIC lib.c)
+target_sources(nonexistent PRIVATE FILE_SET a TYPE HEADERS FILES a.h)
+set_property(TARGET nonexistent PROPERTY PRIVATE_HEADER_SETS_TO_VERIFY "a;c;b")

+ 3 - 0
Tests/RunCMake/VerifyHeaderSets/dir3/CMakeLists.txt

@@ -0,0 +1,3 @@
+add_library(lib3 STATIC ../lib.c)
+add_custom_command(OUTPUT lib3.h COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/lib3.h lib3.h)
+target_sources(lib3 PRIVATE FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR} FILES ${CMAKE_CURRENT_BINARY_DIR}/lib3.h)

+ 5 - 0
Tests/RunCMake/VerifyHeaderSets/dir3/lib3.h

@@ -0,0 +1,5 @@
+#ifdef _WIN32
+__declspec(dllimport)
+#endif
+extern void
+lib3(void);

+ 3 - 0
Tests/RunCMake/VerifyHeaderSets/dir4/CMakeLists.txt

@@ -0,0 +1,3 @@
+add_library(lib4 STATIC ../lib.c)
+add_custom_command(OUTPUT lib4.h COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/lib4.h lib4.h)
+target_sources(lib4 PRIVATE FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR} FILES ${CMAKE_CURRENT_BINARY_DIR}/lib4.h)

+ 5 - 0
Tests/RunCMake/VerifyHeaderSets/dir4/lib4.h

@@ -0,0 +1,5 @@
+#ifdef _WIN32
+__declspec(dllimport)
+#endif
+extern void
+lib4(void);

+ 1 - 0
Tests/RunCMake/property_init/Always.cmake

@@ -5,6 +5,7 @@ set(properties
 
 
   # Build graph properties
   # Build graph properties
   "VERIFY_INTERFACE_HEADER_SETS"  "TRUE"    "<SAME>"
   "VERIFY_INTERFACE_HEADER_SETS"  "TRUE"    "<SAME>"
+  "VERIFY_PRIVATE_HEADER_SETS"    "TRUE"    "<SAME>"
 
 
   # Metadata
   # Metadata
   "FOLDER"                        "folder"  "<SAME>"
   "FOLDER"                        "folder"  "<SAME>"