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

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 2 дней назад
Родитель
Сommit
18c98682f5
91 измененных файлов с 772 добавлено и 246 удалено
  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::
    :maxdepth: 1
 
+   CMP0209: Verify interface header sets checks executables without exports. </policy/CMP0209>
    CMP0208: export(EXPORT) does not allow empty arguments. </policy/CMP0208>
    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>

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

@@ -393,6 +393,7 @@ Properties on Targets
    /prop_tgt/PRECOMPILE_HEADERS_REUSE_FROM
    /prop_tgt/PREFIX
    /prop_tgt/PRIVATE_HEADER
+   /prop_tgt/PRIVATE_HEADER_SETS_TO_VERIFY
    /prop_tgt/PROJECT_LABEL
    /prop_tgt/PUBLIC_HEADER
    /prop_tgt/RESOURCE
@@ -433,6 +434,7 @@ Properties on Targets
    /prop_tgt/UNITY_BUILD_RELOCATABLE
    /prop_tgt/UNITY_BUILD_UNIQUE_ID
    /prop_tgt/VERIFY_INTERFACE_HEADER_SETS
+   /prop_tgt/VERIFY_PRIVATE_HEADER_SETS
    /prop_tgt/VERSION
    /prop_tgt/VISIBILITY_INLINES_HIDDEN
    /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_UNIQUE_ID
    /variable/CMAKE_VERIFY_INTERFACE_HEADER_SETS
+   /variable/CMAKE_VERIFY_PRIVATE_HEADER_SETS
    /variable/CMAKE_VISIBILITY_INLINES_HIDDEN
    /variable/CMAKE_VS_DEBUGGER_COMMAND
    /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
 
 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
-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.
 
 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
 
-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_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
   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
   :variable:`CMAKE_PROJECT_INCLUDE` or
   :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE` to turn verification back
   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
 
@@ -2240,12 +2243,14 @@ endfunction()
 # calls will be available to the caller.
 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
     "__fcprefix__${CMAKE_VERIFY_INTERFACE_HEADER_SETS}"
+    "__fcprefix__${CMAKE_VERIFY_PRIVATE_HEADER_SETS}"
   )
   set(CMAKE_VERIFY_INTERFACE_HEADER_SETS FALSE)
+  set(CMAKE_VERIFY_PRIVATE_HEADER_SETS FALSE)
 
   get_property(__cmake_providerCommand GLOBAL PROPERTY
     __FETCHCONTENT_MAKEAVAILABLE_SERIAL_PROVIDER
@@ -2453,18 +2458,30 @@ macro(FetchContent_MakeAvailable)
   endforeach()
 
   # 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
   unset(__cmake_contentName)
   unset(__cmake_contentNameLower)
   unset(__cmake_contentNameUpper)
   unset(__cmake_providerCommand)
-  unset(__cmake_original_verify_setting)
+  unset(__cmake_original_verify_interface_setting)
+  unset(__cmake_original_verify_private_setting)
 
 endmacro()
 

+ 215 - 122
Source/cmGeneratorTarget.cxx

@@ -9,6 +9,7 @@
 #include <cstddef>
 #include <cstdio>
 #include <cstring>
+#include <initializer_list>
 #include <sstream>
 #include <unordered_set>
 #include <utility>
@@ -5709,145 +5710,236 @@ cmGeneratorTarget::ManagedType cmGeneratorTarget::GetManagedType(
 
 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;
@@ -5855,6 +5947,7 @@ bool cmGeneratorTarget::AddHeaderSetVerification()
 
 std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
   cmSourceFile& source, std::string const& dir,
+  std::string const& verifyTargetName,
   cm::optional<std::set<std::string>>& languages) const
 {
   std::string extension;
@@ -5908,9 +6001,9 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
   }
   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);
   source.SetSpecialSourceType(
     cmSourceFile::SpecialSourceType::HeaderSetVerificationSource);

+ 1 - 0
Source/cmGeneratorTarget.h

@@ -1112,6 +1112,7 @@ public:
   bool AddHeaderSetVerification();
   std::string GenerateHeaderSetVerificationFile(
     cmSourceFile& source, std::string const& dir,
+    std::string const& verifyTargetName,
     cm::optional<std::set<std::string>>& languages) 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(
       cm::make_unique<cmGeneratorTarget>(allVerifyTarget,
                                          this->LocalGenerators.front().get()));
+    if (allVerifyInterfaceTarget) {
+      allVerifyTarget->AddUtility(allVerifyInterfaceTarget->GetName(), false);
+    }
+    if (allVerifyPrivateTarget) {
+      allVerifyTarget->AddUtility(allVerifyPrivateTarget->GetName(), false);
+    }
   }
 
   return true;

+ 5 - 1
Source/cmPolicies.h

@@ -623,6 +623,9 @@ class cmMakefile;
          "file(GET_RUNTIME_DEPENDENCIES) normalizes paths before matching.",  \
          4, 3, 0, WARN)                                                       \
   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)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
@@ -676,7 +679,8 @@ class cmMakefile;
   F(CMP0200)                                                                  \
   F(CMP0202)                                                                  \
   F(CMP0203)                                                                  \
-  F(CMP0204)
+  F(CMP0204)                                                                  \
+  F(CMP0209)
 
 #define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F)                                  \
   F(CMP0116)                                                                  \

+ 1 - 0
Source/cmTarget.cxx

@@ -498,6 +498,7 @@ TargetProperty const StaticTargetProperties[] = {
   { "UNITY_BUILD_RELOCATABLE"_s, IC::CanCompileSources },
   { "OPTIMIZE_DEPENDENCIES"_s, IC::CanCompileSources },
   { "VERIFY_INTERFACE_HEADER_SETS"_s },
+  { "VERIFY_PRIVATE_HEADER_SETS"_s },
   // -- Android
   { "ANDROID_ANT_ADDITIONAL_OPTIONS"_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_PRIVATE_HEADER_SETS='FALSE'

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

@@ -1,16 +1,21 @@
 enable_language(C)
 
 set(CMAKE_VERIFY_INTERFACE_HEADER_SETS TRUE)
+set(CMAKE_VERIFY_PRIVATE_HEADER_SETS TRUE)
 
 include(FetchContent)
 FetchContent_Declare(verify_subproj
   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)
 
 # 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)
 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)
 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
    \* CMP0203
    \* CMP0204
+   \* CMP0209
 
 Call Stack \(most recent call first\):
   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)
 
-function(run_cmake_build name target)
+function(run_cmake_build name verifyType target)
   if(NOT BUILD_CONFIG)
     set(BUILD_CONFIG Debug)
   endif()
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
   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()
 
 set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_INTERFACE_HEADER_SETS=ON)
 if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
   list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
 endif()
-run_cmake(VerifyHeaderSets)
+run_cmake(VerifyInterfaceHeaderSets)
 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")
-  run_cmake_build(VerifyHeaderSets config_verify_interface_header_sets)
+  run_cmake_build(VerifyInterfaceHeaderSets interface config)
   if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
     set(BUILD_CONFIG Release)
-    run_cmake_build(VerifyHeaderSets config_verify_interface_header_sets)
+    run_cmake_build(VerifyInterfaceHeaderSets interface config)
     unset(BUILD_CONFIG)
   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)
 run_cmake(AllVerifyInterfaceHeaderSets)
 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)
-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)

+ 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)
 
 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_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)
 

+ 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
   "VERIFY_INTERFACE_HEADER_SETS"  "TRUE"    "<SAME>"
+  "VERIFY_PRIVATE_HEADER_SETS"    "TRUE"    "<SAME>"
 
   # Metadata
   "FOLDER"                        "folder"  "<SAME>"