瀏覽代碼

Merge topic 'precompile-headers'

8da78d4efe Precompile headers: Update documentation
5772930164 Precompile headers: Add unit tests
519606704e Precompile headers: Add support for Visual Studio generators
28be170fbc Precompile headers: Add support for Xcode generator
b8626261e9 Precompile headers: Add methods to generate PCH sources
375d01c680 PCH: add example/test
9b6797e71d PCH: add target_precompile_headers command
0467a2f91b PCH: add PRECOMPILE_HEADERS to special properties

Acked-by: Kitware Robot <[email protected]>
Acked-by: Daniel Pfeifer <[email protected]>
Acked-by: Ivan171 <[email protected]>
Acked-by: Stanislav Ershov <[email protected]>
Acked-by: Steve Mokris <[email protected]>
Acked-by: Evgeniy Dushistov <[email protected]>
Acked-by: Danila Malyutin <[email protected]>
Acked-by: Viktor Kirilov <[email protected]>
Acked-by: Lucas Zhao <[email protected]>
Merge-request: !3553
Brad King 6 年之前
父節點
當前提交
fa36e2151d
共有 63 個文件被更改,包括 924 次插入6 次删除
  1. 56 0
      Help/command/target_precompile_headers.rst
  2. 1 0
      Help/manual/cmake-commands.7.rst
  3. 4 0
      Help/manual/cmake-properties.7.rst
  4. 1 0
      Help/manual/cmake-variables.7.rst
  5. 13 0
      Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst
  6. 8 0
      Help/prop_tgt/DISABLE_PRECOMPILE_HEADERS.rst
  7. 14 0
      Help/prop_tgt/INTERFACE_PRECOMPILE_HEADERS.rst
  8. 12 0
      Help/prop_tgt/PRECOMPILE_HEADERS.rst
  9. 6 0
      Help/release/dev/precompile-headers.rst
  10. 6 0
      Help/variable/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst
  11. 2 0
      Modules/Compiler/Clang.cmake
  12. 8 0
      Modules/Compiler/GNU.cmake
  13. 7 0
      Modules/Compiler/Intel.cmake
  14. 7 0
      Modules/Platform/Windows-Clang.cmake
  15. 7 0
      Modules/Platform/Windows-Embarcadero.cmake
  16. 9 0
      Modules/Platform/Windows-MSVC.cmake
  17. 2 0
      Source/CMakeLists.txt
  18. 4 0
      Source/cmCommands.cxx
  19. 3 0
      Source/cmExportBuildFileGenerator.cxx
  20. 2 1
      Source/cmGeneratorExpressionDAGChecker.h
  21. 153 0
      Source/cmGeneratorTarget.cxx
  22. 12 0
      Source/cmGeneratorTarget.h
  23. 7 0
      Source/cmGlobalVisualStudioGenerator.h
  24. 12 0
      Source/cmGlobalXCodeGenerator.cxx
  25. 83 0
      Source/cmLocalGenerator.cxx
  26. 2 0
      Source/cmLocalGenerator.h
  27. 2 0
      Source/cmLocalVisualStudio7Generator.cxx
  28. 2 0
      Source/cmMakefileExecutableTargetGenerator.cxx
  29. 2 0
      Source/cmMakefileLibraryTargetGenerator.cxx
  30. 19 1
      Source/cmMakefileTargetGenerator.cxx
  31. 2 0
      Source/cmMakefileUtilityTargetGenerator.cxx
  32. 2 0
      Source/cmNinjaNormalTargetGenerator.cxx
  33. 13 3
      Source/cmNinjaTargetGenerator.cxx
  34. 46 0
      Source/cmTarget.cxx
  35. 5 0
      Source/cmTarget.h
  36. 36 0
      Source/cmTargetPrecompileHeadersCommand.cxx
  37. 41 0
      Source/cmTargetPrecompileHeadersCommand.h
  38. 12 1
      Source/cmVisualStudio10TargetGenerator.cxx
  39. 2 0
      Tests/RunCMake/CMakeLists.txt
  40. 3 0
      Tests/RunCMake/PrecompileHeaders/CMakeLists.txt
  41. 17 0
      Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake
  42. 14 0
      Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake
  43. 36 0
      Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake
  44. 20 0
      Tests/RunCMake/PrecompileHeaders/PchInterface.cmake
  45. 12 0
      Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake
  46. 11 0
      Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue.cmake
  47. 18 0
      Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
  48. 13 0
      Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake
  49. 6 0
      Tests/RunCMake/PrecompileHeaders/foo.c
  50. 7 0
      Tests/RunCMake/PrecompileHeaders/foobar.c
  51. 9 0
      Tests/RunCMake/PrecompileHeaders/include/bar.h
  52. 6 0
      Tests/RunCMake/PrecompileHeaders/include/foo.h
  53. 4 0
      Tests/RunCMake/PrecompileHeaders/main.cpp
  54. 3 0
      Tests/RunCMake/PrecompileHeaders/non-pch.cpp
  55. 3 0
      Tests/RunCMake/PrecompileHeaders/pch.h
  56. 1 0
      Tests/RunCMake/VS10Project/RunCMakeTest.cmake
  57. 69 0
      Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake
  58. 4 0
      Tests/RunCMake/VS10Project/VsPrecompileHeaders.cmake
  59. 1 0
      Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
  60. 35 0
      Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake
  61. 4 0
      Tests/RunCMake/XcodeProject/XcodePrecompileHeaders.cmake
  62. 2 0
      Utilities/IWYU/mapping.imp
  63. 1 0
      bootstrap

+ 56 - 0
Help/command/target_precompile_headers.rst

@@ -0,0 +1,56 @@
+target_precompile_headers
+-------------------------
+
+Add a list of header files to precompile.
+
+.. code-block:: cmake
+
+  target_precompile_headers(<target>
+    <INTERFACE|PUBLIC|PRIVATE> [header1...]
+    [<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])
+
+Adds header files to :prop_tgt:`PRECOMPILE_HEADERS` or
+:prop_tgt:`INTERFACE_PRECOMPILE_HEADERS` target properties.
+
+Precompiling header files can speed up compilation by creating a partially
+processed version of some header files, and then using that version during
+compilations rather than repeatedly parsing the original headers.
+
+The named ``<target>`` must have been created by a command such as
+:command:`add_executable` or :command:`add_library` and must not be an
+:ref:`ALIAS target <Alias Targets>`.
+
+The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to
+specify the scope of the following arguments.  ``PRIVATE`` and ``PUBLIC``
+items will populate the :prop_tgt:`PRECOMPILE_HEADERS` property of
+``<target>``.  ``PUBLIC`` and ``INTERFACE`` items will populate the
+:prop_tgt:`INTERFACE_PRECOMPILE_HEADERS` property of ``<target>``.
+(:ref:`IMPORTED targets <Imported Targets>` only support ``INTERFACE`` items.)
+Repeated calls for the same ``<target>`` append items in the order called.
+
+Arguments to ``target_precompile_headers`` may use "generator expressions"
+with the syntax ``$<...>``.
+See the :manual:`cmake-generator-expressions(7)` manual for available
+expressions.  See the :manual:`cmake-compile-features(7)` manual for
+information on compile features and a list of supported compilers.
+
+.. code-block:: cmake
+
+  target_precompile_headers(<target>
+    PUBLIC
+      "project_header.h"
+    PRIVATE
+      <unordered_map>
+  )
+
+Header files will be double quoted if they are not specified with double
+quotes or angle brackets.
+
+See Also
+^^^^^^^^
+
+For disabling precompile headers for specific targets there is the
+property :prop_tgt:`DISABLE_PRECOMPILE_HEADERS`.
+
+For skipping certain source files there is the source file property
+:prop_sf:`SKIP_PRECOMPILE_HEADERS`.

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

@@ -112,6 +112,7 @@ These commands are available only in CMake projects.
    /command/target_link_directories
    /command/target_link_libraries
    /command/target_link_options
+   /command/target_precompile_headers
    /command/target_sources
    /command/try_compile
    /command/try_run

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

@@ -181,6 +181,7 @@ Properties on Targets
    /prop_tgt/DEFINE_SYMBOL
    /prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY
    /prop_tgt/DEPLOYMENT_ADDITIONAL_FILES
+   /prop_tgt/DISABLE_PRECOMPILE_HEADERS
    /prop_tgt/DOTNET_TARGET_FRAMEWORK_VERSION
    /prop_tgt/EchoString
    /prop_tgt/ENABLE_EXPORTS
@@ -240,6 +241,7 @@ Properties on Targets
    /prop_tgt/INTERFACE_LINK_DIRECTORIES
    /prop_tgt/INTERFACE_LINK_LIBRARIES
    /prop_tgt/INTERFACE_LINK_OPTIONS
+   /prop_tgt/INTERFACE_PRECOMPILE_HEADERS
    /prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE
    /prop_tgt/INTERFACE_SOURCES
    /prop_tgt/INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
@@ -295,6 +297,7 @@ Properties on Targets
    /prop_tgt/PDB_OUTPUT_DIRECTORY_CONFIG
    /prop_tgt/PDB_OUTPUT_DIRECTORY
    /prop_tgt/POSITION_INDEPENDENT_CODE
+   /prop_tgt/PRECOMPILE_HEADERS
    /prop_tgt/PREFIX
    /prop_tgt/PRIVATE_HEADER
    /prop_tgt/PROJECT_LABEL
@@ -446,6 +449,7 @@ Properties on Source Files
    /prop_sf/SKIP_AUTOMOC
    /prop_sf/SKIP_AUTORCC
    /prop_sf/SKIP_AUTOUIC
+   /prop_sf/SKIP_PRECOMPILE_HEADERS
    /prop_sf/Swift_DEPENDENCIES_FILE
    /prop_sf/Swift_DIAGNOSTICS_FILE
    /prop_sf/SYMBOLIC

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

@@ -355,6 +355,7 @@ Variables that Control the Build
    /variable/CMAKE_CUDA_SEPARABLE_COMPILATION
    /variable/CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS
    /variable/CMAKE_DEBUG_POSTFIX
+   /variable/CMAKE_DISABLE_PRECOMPILE_HEADERS
    /variable/CMAKE_ENABLE_EXPORTS
    /variable/CMAKE_EXE_LINKER_FLAGS
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG

+ 13 - 0
Help/prop_sf/SKIP_PRECOMPILE_HEADERS.rst

@@ -0,0 +1,13 @@
+SKIP_PRECOMPILE_HEADERS
+-----------------------
+
+Is this source file skipped by :prop_tgt:`PRECOMPILE_HEADERS` feature.
+
+This property helps with build problems that one would run into
+when using the :prop_tgt:`PRECOMPILE_HEADERS` feature.
+
+One example would be the usage of Objective-C (*.m) files, and
+Objective-C++ (*.mm) files, which lead to compilation failure
+because they are treated (in case of Ninja / Makefile generator)
+as C, and CXX respectively. The precompile headers are not
+compatible between languages.

+ 8 - 0
Help/prop_tgt/DISABLE_PRECOMPILE_HEADERS.rst

@@ -0,0 +1,8 @@
+DISABLE_PRECOMPILE_HEADERS
+--------------------------
+
+Disables the precompilation of header files specified by
+:prop_tgt:`PRECOMPILE_HEADERS` property.
+
+If the property is not set, CMake will use the value provided
+by :variable:`CMAKE_DISABLE_PRECOMPILE_HEADERS`.

+ 14 - 0
Help/prop_tgt/INTERFACE_PRECOMPILE_HEADERS.rst

@@ -0,0 +1,14 @@
+INTERFACE_PRECOMPILE_HEADERS
+----------------------------
+
+List of interface header files to precompile into consuming targets.
+
+Targets may populate this property to publish the header files
+for consuming targets to precompile.  The :command:`target_precompile_headers`
+command populates this property with values given to the ``PUBLIC`` and
+``INTERFACE`` keywords.  Projects may also get and set the property directly.
+
+Contents of ``INTERFACE_PRECOMPILE_HEADERS`` may use "generator expressions"
+with the syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)`
+manual for available expressions.  See the :manual:`cmake-buildsystem(7)`
+manual for more on defining buildsystem properties.

+ 12 - 0
Help/prop_tgt/PRECOMPILE_HEADERS.rst

@@ -0,0 +1,12 @@
+PRECOMPILE_HEADERS
+------------------
+
+List of header files to precompile.
+
+This property holds a :ref:`semicolon-separated list <CMake Language Lists>`
+of header files to precompile specified so far for its target.
+Use the :command:`target_precompile_headers` command to append more header
+files.
+
+This property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.

+ 6 - 0
Help/release/dev/precompile-headers.rst

@@ -0,0 +1,6 @@
+Precompile Headers
+------------------
+
+* The :prop_tgt:`PRECOMPILE_HEADERS` target property was added to tell
+  generators to use a list of precompile headers for faster compilation
+  times.

+ 6 - 0
Help/variable/CMAKE_DISABLE_PRECOMPILE_HEADERS.rst

@@ -0,0 +1,6 @@
+CMAKE_DISABLE_PRECOMPILE_HEADERS
+--------------------------------
+
+Default value for :prop_tgt:`DISABLE_PRECOMPILE_HEADERS` of targets.
+
+By default ``CMAKE_DISABLE_PRECOMPILE_HEADERS`` is ``OFF``.

+ 2 - 0
Modules/Compiler/Clang.cmake

@@ -96,5 +96,7 @@ else()
     set(CMAKE_${lang}_ARCHIVE_FINISH_IPO
       "\"${__ranlib}\" <TARGET>"
     )
+
+    set(CMAKE_PCH_PROLOGUE "#pragma clang system_header")
   endmacro()
 endif()

+ 8 - 0
Modules/Compiler/GNU.cmake

@@ -11,6 +11,9 @@ set(__COMPILER_GNU 1)
 include(Compiler/CMakeCommonCompilerMacros)
 include(Internal/CMakeCheckCompilerFlag)
 
+set(__pch_header_C "c-header")
+set(__pch_header_CXX "c++-header")
+
 macro(__compiler_gnu lang)
   # Feature flags.
   set(CMAKE_${lang}_VERBOSE_FLAG "-v")
@@ -104,4 +107,9 @@ macro(__compiler_gnu lang)
     unset(_COMPILER_ARGS)
   endif()
   list(APPEND CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "-dM" "-E" "-c" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp")
+
+  set(CMAKE_PCH_EXTENSION .gch)
+  set(CMAKE_PCH_PROLOGUE "#pragma GCC system_header")
+  set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Winvalid-pch -include <PCH_HEADER>)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Winvalid-pch -x ${__pch_header_${lang}} -include <PCH_HEADER>)
 endmacro()

+ 7 - 0
Modules/Compiler/Intel.cmake

@@ -32,5 +32,12 @@ else()
       unset(_COMPILER_ARGS)
     endif()
     list(APPEND CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "-QdM" "-P" "-Za" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp")
+
+    # Precompile Headers
+    set(CMAKE_PCH_EXTENSION .pchi)
+    set(CMAKE_LINK_PCH ON)
+    set(CMAKE_PCH_EPILOGUE "#pragma hdrstop")
+    set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Winvalid-pch -Wno-pch-messages -pch-use <PCH_FILE> -include <PCH_HEADER>)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Winvalid-pch -Wno-pch-messages -pch-create <PCH_FILE> -include <PCH_HEADER>)
   endmacro()
 endif()

+ 7 - 0
Modules/Platform/Windows-Clang.cmake

@@ -8,6 +8,9 @@ if(__WINDOWS_CLANG)
 endif()
 set(__WINDOWS_CLANG 1)
 
+set(__pch_header_C "c-header")
+set(__pch_header_CXX "c++-header")
+
 macro(__windows_compiler_clang_gnu lang)
   set(CMAKE_LIBRARY_PATH_FLAG "-L")
   set(CMAKE_LINK_LIBRARY_FLAG "-l")
@@ -73,6 +76,10 @@ macro(__windows_compiler_clang_gnu lang)
   string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG -Xclang -gcodeview ${__ADDED_FLAGS}")
   set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-isystem ")
 
+  set(CMAKE_PCH_EXTENSION .gch)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Winvalid-pch -include <PCH_HEADER>)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Winvalid-pch -x ${__pch_header_${lang}} -include <PCH_HEADER>)
+
   unset(__ADDED_FLAGS)
   unset(__ADDED_FLAGS_DEBUG)
   string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE_LOWER)

+ 7 - 0
Modules/Platform/Windows-Embarcadero.cmake

@@ -119,6 +119,13 @@ macro(__embarcadero_language lang)
     "tlib ${CMAKE_START_TEMP_FILE}/p512 <LINK_FLAGS> /a <TARGET_QUOTED> <OBJECTS>${CMAKE_END_TEMP_FILE}"
     )
 
+  # Precompile Headers
+  if (EMBARCADERO)
+    set(CMAKE_PCH_EXTENSION .pch)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Xclang -include-pch -Xclang <PCH_FILE>)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Xclang -emit-pch -Xclang -include -Xclang <PCH_HEADER>)
+  endif()
+
   # Initial configuration flags.
   string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_tM}")
   string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -Od -v")

+ 9 - 0
Modules/Platform/Windows-MSVC.cmake

@@ -329,6 +329,15 @@ macro(__windows_compiler_msvc lang)
   set(CMAKE_${lang}_LINK_EXECUTABLE
     "${_CMAKE_VS_LINK_EXE}<CMAKE_LINKER> ${CMAKE_CL_NOLOGO} <OBJECTS> ${CMAKE_START_TEMP_FILE} /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES>${CMAKE_END_TEMP_FILE}")
 
+  set(CMAKE_PCH_EXTENSION .pch)
+  set(CMAKE_LINK_PCH ON)
+  if(MSVC_VERSION GREATER_EQUAL 1910)
+    # VS 2017 or greater
+    set(CMAKE_PCH_PROLOGUE "#pragma system_header")
+  endif()
+  set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH /Yu<PCH_HEADER> /FI<PCH_HEADER>)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH /Yc<PCH_HEADER> /FI<PCH_HEADER>)
+
   if("x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xMSVC")
     set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
     set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)

+ 2 - 0
Source/CMakeLists.txt

@@ -654,6 +654,8 @@ set(SRCS
   cmTargetLinkDirectoriesCommand.h
   cmTargetLinkLibrariesCommand.cxx
   cmTargetLinkLibrariesCommand.h
+  cmTargetPrecompileHeadersCommand.cxx
+  cmTargetPrecompileHeadersCommand.h
   cmTargetPropCommandBase.cxx
   cmTargetPropCommandBase.h
   cmTargetSourcesCommand.cxx

+ 4 - 0
Source/cmCommands.cxx

@@ -78,6 +78,7 @@
 #include "cmTargetCompileOptionsCommand.h"
 #include "cmTargetIncludeDirectoriesCommand.h"
 #include "cmTargetLinkLibrariesCommand.h"
+#include "cmTargetPrecompileHeadersCommand.h"
 #include "cmTargetSourcesCommand.h"
 #include "cmTryCompileCommand.h"
 #include "cmTryRunCommand.h"
@@ -276,6 +277,9 @@ void GetProjectCommands(cmState* state)
   state->AddBuiltinCommand("try_compile",
                            cm::make_unique<cmTryCompileCommand>());
   state->AddBuiltinCommand("try_run", cm::make_unique<cmTryRunCommand>());
+  state->AddBuiltinCommand(
+    "target_precompile_headers",
+    cm::make_unique<cmTargetPrecompileHeadersCommand>());
 
 #if !defined(CMAKE_BOOTSTRAP)
   state->AddBuiltinCommand("add_compile_definitions",

+ 3 - 0
Source/cmExportBuildFileGenerator.cxx

@@ -91,6 +91,9 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
     this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gte,
                                     cmGeneratorExpression::BuildInterface,
                                     properties, missingTargets);
+    this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gte,
+                                    cmGeneratorExpression::BuildInterface,
+                                    properties, missingTargets);
     this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
                                     cmGeneratorExpression::BuildInterface,
                                     properties, missingTargets);

+ 2 - 1
Source/cmGeneratorExpressionDAGChecker.h

@@ -29,7 +29,8 @@ class cmGeneratorTarget;
   SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES)                      \
   SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS)                              \
   SELECT(F, EvaluatingLinkDirectories, LINK_DIRECTORIES)                      \
-  SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS)
+  SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS)                              \
+  SELECT(F, EvaluatingPrecompileHeaders, PRECOMPILE_HEADERS)
 
 #define CM_FOR_EACH_TRANSITIVE_PROPERTY(F)                                    \
   CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH)

+ 153 - 0
Source/cmGeneratorTarget.cxx

@@ -21,6 +21,7 @@
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
 #include "cmCustomCommandLines.h"
+#include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpressionContext.h"
 #include "cmGeneratorExpressionDAGChecker.h"
@@ -278,6 +279,7 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
   , DebugCompileDefinitionsDone(false)
   , DebugLinkOptionsDone(false)
   , DebugLinkDirectoriesDone(false)
+  , DebugPrecompileHeadersDone(false)
   , DebugSourcesDone(false)
   , LinkImplementationLanguageIsContextDependent(true)
   , UtilityItemsDone(false)
@@ -312,6 +314,10 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
                                      t->GetLinkDirectoriesBacktraces(),
                                      this->LinkDirectoriesEntries);
 
+  CreatePropertyGeneratorExpressions(t->GetPrecompileHeadersEntries(),
+                                     t->GetPrecompileHeadersBacktraces(),
+                                     this->PrecompileHeadersEntries);
+
   CreatePropertyGeneratorExpressions(t->GetSourceEntries(),
                                      t->GetSourceBacktraces(),
                                      this->SourceEntries, true);
@@ -327,6 +333,7 @@ cmGeneratorTarget::~cmGeneratorTarget()
   cmDeleteAll(this->CompileDefinitionsEntries);
   cmDeleteAll(this->LinkOptionsEntries);
   cmDeleteAll(this->LinkDirectoriesEntries);
+  cmDeleteAll(this->PrecompileHeadersEntries);
   cmDeleteAll(this->SourceEntries);
   cmDeleteAll(this->LinkInformation);
 }
@@ -3312,6 +3319,152 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions(
   return list;
 }
 
+std::vector<BT<std::string>> cmGeneratorTarget::GetPrecompileHeaders(
+  const std::string& config, const std::string& language) const
+{
+  std::unordered_set<std::string> uniqueOptions;
+
+  cmGeneratorExpressionDAGChecker dagChecker(this, "PRECOMPILE_HEADERS",
+                                             nullptr, nullptr);
+
+  std::vector<std::string> debugProperties;
+  const char* debugProp =
+    this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES");
+  if (debugProp) {
+    cmExpandList(debugProp, debugProperties);
+  }
+
+  bool debugDefines = !this->DebugPrecompileHeadersDone &&
+    std::find(debugProperties.begin(), debugProperties.end(),
+              "PRECOMPILE_HEADERS") != debugProperties.end();
+
+  if (this->GlobalGenerator->GetConfigureDoneCMP0026()) {
+    this->DebugPrecompileHeadersDone = true;
+  }
+
+  std::vector<EvaluatedTargetPropertyEntry> entries =
+    EvaluateTargetPropertyEntries(this, config, language, &dagChecker,
+                                  this->PrecompileHeadersEntries);
+
+  AddInterfaceEntries(this, config, "INTERFACE_PRECOMPILE_HEADERS", language,
+                      &dagChecker, entries);
+
+  std::vector<BT<std::string>> list;
+  processOptions(this, entries, list, uniqueOptions, debugDefines,
+                 "precompile headers", OptionsParse::None);
+
+  return list;
+}
+
+std::string cmGeneratorTarget::GetPchHeader(const std::string& config,
+                                            const std::string& language) const
+{
+  if (language != "C" && language != "CXX") {
+    return std::string();
+  }
+  if (this->GetPropertyAsBool("DISABLE_PRECOMPILE_HEADERS")) {
+    return std::string();
+  }
+  const auto inserted =
+    this->PchHeaders.insert(std::make_pair(language + config, ""));
+  if (inserted.second) {
+    const std::vector<BT<std::string>> headers =
+      this->GetPrecompileHeaders(config, language);
+    if (headers.empty()) {
+      return std::string();
+    }
+    std::string& filename = inserted.first->second;
+
+    if (this->GetGlobalGenerator()->IsMultiConfig()) {
+      filename =
+        cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), "/");
+    } else {
+      // For GCC we need to have the header file .h[xx]
+      // next to the .h[xx].gch file
+      filename = this->ObjectDirectory;
+    }
+
+    filename = cmStrCat(filename, "CMakeFiles/", this->GetName(),
+                        ".dir/cmake_pch", ((language == "C") ? ".h" : ".hxx"));
+
+    const std::string filename_tmp = cmStrCat(filename, ".tmp");
+    {
+      auto pchPrologue = this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE");
+      auto pchEpilogue = this->Makefile->GetDefinition("CMAKE_PCH_EPILOGUE");
+
+      cmGeneratedFileStream file(
+        filename_tmp, false,
+        this->GetGlobalGenerator()->GetMakefileEncoding());
+      file << "/* generated by CMake */\n\n";
+      if (pchPrologue) {
+        file << pchPrologue << "\n";
+      }
+      if (this->GetGlobalGenerator()->IsXcode()) {
+        file << "#ifndef CMAKE_SKIP_PRECOMPILE_HEADERS\n";
+      }
+      if (language == "CXX") {
+        file << "#ifdef __cplusplus\n";
+      }
+      for (auto const& header_bt : headers) {
+        if (header_bt.Value.empty()) {
+          continue;
+        }
+        if (header_bt.Value[0] == '<' || header_bt.Value[0] == '"') {
+          file << "#include " << header_bt.Value << "\n";
+        } else {
+          file << "#include \"" << header_bt.Value << "\"\n";
+        }
+      }
+      if (language == "CXX") {
+        file << "#endif // __cplusplus\n";
+      }
+      if (this->GetGlobalGenerator()->IsXcode()) {
+        file << "#endif // CMAKE_SKIP_PRECOMPILE_HEADERS\n";
+      }
+      if (pchEpilogue) {
+        file << pchEpilogue << "\n";
+      }
+    }
+    cmSystemTools::CopyFileIfDifferent(filename_tmp, filename);
+    cmSystemTools::RemoveFile(filename_tmp);
+  }
+  return inserted.first->second;
+}
+
+std::string cmGeneratorTarget::GetPchSource(const std::string& config,
+                                            const std::string& language) const
+{
+  if (language != "C" && language != "CXX") {
+    return std::string();
+  }
+  const auto inserted =
+    this->PchSources.insert(std::make_pair(language + config, ""));
+  if (inserted.second) {
+    const std::string pchHeader = this->GetPchHeader(config, language);
+    if (pchHeader.empty()) {
+      return std::string();
+    }
+    std::string& filename = inserted.first->second;
+    filename = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(),
+                        "/CMakeFiles/", this->GetName(), ".dir/cmake_pch");
+
+    // For GCC the source extension will be tranformed into .h[xx].gch
+    if (!this->Makefile->IsOn("CMAKE_LINK_PCH")) {
+      filename += ((language == "C") ? ".h.c" : ".hxx.cxx");
+    } else {
+      filename += ((language == "C") ? ".c" : ".cxx");
+    }
+    const std::string filename_tmp = cmStrCat(filename, ".tmp");
+    {
+      cmGeneratedFileStream file(filename_tmp);
+      file << "/* generated by CMake */\n";
+    }
+    cmSystemTools::CopyFileIfDifferent(filename_tmp, filename);
+    cmSystemTools::RemoveFile(filename_tmp);
+  }
+  return inserted.first->second;
+}
+
 void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result,
                                        const std::string& config,
                                        const std::string& language) const

+ 12 - 0
Source/cmGeneratorTarget.h

@@ -455,6 +455,14 @@ public:
   std::vector<BT<std::string>> GetLinkDepends(
     std::string const& config, std::string const& language) const;
 
+  std::vector<BT<std::string>> GetPrecompileHeaders(
+    const std::string& config, const std::string& language) const;
+
+  std::string GetPchHeader(const std::string& config,
+                           const std::string& language) const;
+  std::string GetPchSource(const std::string& config,
+                           const std::string& language) const;
+
   bool IsSystemIncludeDirectory(const std::string& dir,
                                 const std::string& config,
                                 const std::string& language) const;
@@ -867,8 +875,11 @@ private:
   std::vector<TargetPropertyEntry*> CompileDefinitionsEntries;
   std::vector<TargetPropertyEntry*> LinkOptionsEntries;
   std::vector<TargetPropertyEntry*> LinkDirectoriesEntries;
+  std::vector<TargetPropertyEntry*> PrecompileHeadersEntries;
   std::vector<TargetPropertyEntry*> SourceEntries;
   mutable std::set<std::string> LinkImplicitNullProperties;
+  mutable std::map<std::string, std::string> PchHeaders;
+  mutable std::map<std::string, std::string> PchSources;
 
   void ExpandLinkItems(std::string const& prop, std::string const& value,
                        std::string const& config,
@@ -922,6 +933,7 @@ private:
   mutable bool DebugCompileDefinitionsDone;
   mutable bool DebugLinkOptionsDone;
   mutable bool DebugLinkDirectoriesDone;
+  mutable bool DebugPrecompileHeadersDone;
   mutable bool DebugSourcesDone;
   mutable bool LinkImplementationLanguageIsContextDependent;
   mutable bool UtilityItemsDone;

+ 7 - 0
Source/cmGlobalVisualStudioGenerator.h

@@ -110,6 +110,13 @@ public:
 
   bool IsIncludeExternalMSProjectSupported() const override { return true; }
 
+  /** Get encoding used by generator for generated source files
+   */
+  codecvt::Encoding GetMakefileEncoding() const override
+  {
+    return codecvt::ANSI;
+  }
+
   class TargetSet : public std::set<cmGeneratorTarget const*>
   {
   };

+ 12 - 0
Source/cmGlobalXCodeGenerator.cxx

@@ -2374,6 +2374,16 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
     buildSettings->AddAttribute("DYLIB_COMPATIBILITY_VERSION",
                                 this->CreateString(vso.str()));
   }
+
+  // Precompile Headers
+  std::string pchHeader = gtgt->GetPchHeader(configName, llang);
+  if (!pchHeader.empty()) {
+    buildSettings->AddAttribute("GCC_PREFIX_HEADER",
+                                this->CreateString(pchHeader));
+    buildSettings->AddAttribute("GCC_PRECOMPILE_PREFIX_HEADER",
+                                this->CreateString("YES"));
+  }
+
   // put this last so it can override existing settings
   // Convert "XCODE_ATTRIBUTE_*" properties directly.
   {
@@ -2829,6 +2839,8 @@ bool cmGlobalXCodeGenerator::CreateGroups(
         continue;
       }
 
+      generator->AddPchDependencies(gtgt, "");
+
       auto addSourceToGroup = [this, mf, gtgt,
                                &sourceGroups](std::string const& source) {
         cmSourceGroup* sourceGroup = mf->FindSourceGroup(source, sourceGroups);

+ 83 - 0
Source/cmLocalGenerator.cxx

@@ -18,6 +18,7 @@
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocation.h"
+#include "cmSourceFileLocationKind.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"
@@ -37,6 +38,7 @@
 
 #include <algorithm>
 #include <assert.h>
+#include <functional>
 #include <initializer_list>
 #include <iterator>
 #include <memory>
@@ -2124,6 +2126,82 @@ void cmLocalGenerator::AppendFlagEscape(std::string& flags,
   this->AppendFlags(flags, this->EscapeForShell(rawFlag));
 }
 
+void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target,
+                                          const std::string& config)
+{
+  const std::string lang = target->GetLinkerLanguage(config);
+  const std::string buildType = cmSystemTools::UpperCase(config);
+  const std::string pchSource = target->GetPchSource(config, lang);
+  const std::string pchHeader = target->GetPchHeader(config, lang);
+
+  if (pchSource.empty() || pchHeader.empty()) {
+    return;
+  }
+
+  const std::string createOptVar =
+    cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_CREATE_PCH");
+  std::string createOptionList =
+    this->Makefile->GetSafeDefinition(createOptVar);
+
+  const std::string useOptVar =
+    cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_USE_PCH");
+  std::string useOptionList = this->Makefile->GetSafeDefinition(useOptVar);
+
+  const std::string pchExtension =
+    this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION");
+
+  if (createOptionList.empty() || useOptionList.empty() ||
+      pchExtension.empty()) {
+    return;
+  }
+
+  auto pch_sf = this->Makefile->GetOrCreateSource(
+    pchSource, false, cmSourceFileLocationKind::Known);
+  std::string pchFile = pchHeader;
+
+  if (!this->GetGlobalGenerator()->IsXcode()) {
+    // Exclude the pch files from linking
+    if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
+      cmSystemTools::ReplaceString(pchFile, (lang == "C" ? ".h" : ".hxx"),
+                                   pchExtension);
+      pch_sf->SetProperty("OBJECT_OUTPUTS", pchFile.c_str());
+    } else {
+      pchFile += pchExtension;
+      pch_sf->SetProperty("PCH_EXTENSION", pchExtension.c_str());
+    }
+
+    target->AddSource(pchSource, true);
+
+    for (auto& str : { std::ref(useOptionList), std::ref(createOptionList) }) {
+      cmSystemTools::ReplaceString(str, "<PCH_HEADER>", pchHeader);
+      cmSystemTools::ReplaceString(str, "<PCH_FILE>", pchFile);
+    }
+  }
+
+  pch_sf->SetProperty("COMPILE_OPTIONS", createOptionList.c_str());
+
+  std::vector<cmSourceFile*> sources;
+  target->GetSourceFiles(sources, buildType);
+  for (cmSourceFile* sf : sources) {
+    if (pch_sf == sf || sf->GetLanguage() != lang) {
+      continue;
+    }
+
+    if (sf->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) {
+      if (this->GetGlobalGenerator()->IsXcode()) {
+        sf->SetProperty("COMPILE_DEFINITIONS",
+                        "CMAKE_SKIP_PRECOMPILE_HEADERS");
+      }
+      continue;
+    }
+
+    if (!this->GetGlobalGenerator()->IsXcode()) {
+      sf->SetProperty("OBJECT_DEPENDS", pchFile.c_str());
+      sf->SetProperty("COMPILE_OPTIONS", useOptionList.c_str());
+    }
+  }
+}
+
 void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags,
                                             cmGeneratorTarget* target,
                                             const std::string& config,
@@ -2716,6 +2794,11 @@ std::string cmLocalGenerator::GetObjectFileNameWithoutTarget(
       }
     }
 
+    const char* pchExtension = source.GetProperty("PCH_EXTENSION");
+    if (pchExtension) {
+      customOutputExtension = pchExtension;
+    }
+
     // Remove the source extension if it is to be replaced.
     if (replaceExt || customOutputExtension) {
       keptSourceExtension = false;

+ 2 - 0
Source/cmLocalGenerator.h

@@ -124,6 +124,8 @@ public:
   virtual void AppendFlags(std::string& flags, const char* newFlags) const;
   virtual void AppendFlagEscape(std::string& flags,
                                 const std::string& rawFlag) const;
+  void AddPchDependencies(cmGeneratorTarget* target,
+                          const std::string& config);
   void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target,
                             const std::string& config,
                             const std::string& lang);

+ 2 - 0
Source/cmLocalVisualStudio7Generator.cxx

@@ -1320,6 +1320,8 @@ void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout,
                                                     const std::string& libName,
                                                     cmGeneratorTarget* target)
 {
+  this->AddPchDependencies(target, "");
+
   std::vector<std::string> configs;
   this->Makefile->GetConfigurations(configs);
 

+ 2 - 0
Source/cmMakefileExecutableTargetGenerator.cxx

@@ -40,6 +40,8 @@ cmMakefileExecutableTargetGenerator::cmMakefileExecutableTargetGenerator(
   this->OSXBundleGenerator =
     cm::make_unique<cmOSXBundleGenerator>(target, this->ConfigName);
   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
+
+  this->LocalGenerator->AddPchDependencies(target, this->ConfigName);
 }
 
 cmMakefileExecutableTargetGenerator::~cmMakefileExecutableTargetGenerator() =

+ 2 - 0
Source/cmMakefileLibraryTargetGenerator.cxx

@@ -42,6 +42,8 @@ cmMakefileLibraryTargetGenerator::cmMakefileLibraryTargetGenerator(
   this->OSXBundleGenerator =
     cm::make_unique<cmOSXBundleGenerator>(target, this->ConfigName);
   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
+
+  this->LocalGenerator->AddPchDependencies(target, this->ConfigName);
 }
 
 cmMakefileLibraryTargetGenerator::~cmMakefileLibraryTargetGenerator() =

+ 19 - 1
Source/cmMakefileTargetGenerator.cxx

@@ -239,10 +239,15 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
   this->GeneratorTarget->GetExtraSources(extraSources, config);
   this->OSXBundleGenerator->GenerateMacOSXContentStatements(
     extraSources, this->MacOSXContentGenerator);
+  const char* pchExtension =
+    this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
   std::vector<cmSourceFile const*> externalObjects;
   this->GeneratorTarget->GetExternalObjects(externalObjects, config);
   for (cmSourceFile const* sf : externalObjects) {
-    this->ExternalObjects.push_back(sf->GetFullPath());
+    auto const& objectFileName = sf->GetFullPath();
+    if (!cmSystemTools::StringEndsWith(objectFileName, pchExtension)) {
+      this->ExternalObjects.push_back(objectFileName);
+    }
   }
   std::vector<cmSourceFile const*> objectSources;
   this->GeneratorTarget->GetObjectSources(objectSources, config);
@@ -1238,7 +1243,14 @@ void cmMakefileTargetGenerator::WriteObjectsVariable(
   if (!lineContinue) {
     lineContinue = "\\";
   }
+
+  const char* pchExtension =
+    this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
+
   for (std::string const& obj : this->Objects) {
+    if (cmSystemTools::StringEndsWith(obj, pchExtension)) {
+      continue;
+    }
     *this->BuildFileStream << " " << lineContinue << "\n";
     *this->BuildFileStream
       << cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath(
@@ -1331,10 +1343,16 @@ private:
 void cmMakefileTargetGenerator::WriteObjectsStrings(
   std::vector<std::string>& objStrings, std::string::size_type limit)
 {
+  const char* pchExtension =
+    this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
+
   cmMakefileTargetGeneratorObjectStrings helper(
     objStrings, this->LocalGenerator,
     this->LocalGenerator->GetStateSnapshot().GetDirectory(), limit);
   for (std::string const& obj : this->Objects) {
+    if (cmSystemTools::StringEndsWith(obj, pchExtension)) {
+      continue;
+    }
     helper.Feed(obj);
   }
   for (std::string const& obj : this->ExternalObjects) {

+ 2 - 0
Source/cmMakefileUtilityTargetGenerator.cxx

@@ -25,6 +25,8 @@ cmMakefileUtilityTargetGenerator::cmMakefileUtilityTargetGenerator(
   this->OSXBundleGenerator =
     cm::make_unique<cmOSXBundleGenerator>(target, this->ConfigName);
   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
+
+  this->LocalGenerator->AddPchDependencies(target, this->ConfigName);
 }
 
 cmMakefileUtilityTargetGenerator::~cmMakefileUtilityTargetGenerator() =

+ 2 - 0
Source/cmNinjaNormalTargetGenerator.cxx

@@ -59,6 +59,8 @@ cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator(
   this->OSXBundleGenerator =
     cm::make_unique<cmOSXBundleGenerator>(target, this->GetConfigName());
   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
+
+  GetLocalGenerator()->AddPchDependencies(target, this->GetConfigName());
 }
 
 cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() = default;

+ 13 - 3
Source/cmNinjaTargetGenerator.cxx

@@ -790,10 +790,16 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements()
       extraSources, this->MacOSXContentGenerator.get());
   }
   {
+    const char* pchExtension =
+      GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION");
+
     std::vector<cmSourceFile const*> externalObjects;
     this->GeneratorTarget->GetExternalObjects(externalObjects, config);
     for (cmSourceFile const* sf : externalObjects) {
-      this->Objects.push_back(this->GetSourceFilePath(sf));
+      const auto objectFileName = this->GetSourceFilePath(sf);
+      if (!cmSystemTools::StringEndsWith(objectFileName, pchExtension)) {
+        this->Objects.push_back(objectFileName);
+      }
     }
   }
 
@@ -955,8 +961,12 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
     vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"]);
 
   objBuild.Outputs.push_back(objectFileName);
-  // Add this object to the list of object files.
-  this->Objects.push_back(objectFileName);
+  const char* pchExtension =
+    this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION");
+  if (!cmSystemTools::StringEndsWith(objectFileName, pchExtension)) {
+    // Add this object to the list of object files.
+    this->Objects.push_back(objectFileName);
+  }
 
   objBuild.ExplicitDeps.push_back(sourceFileName);
 

+ 46 - 0
Source/cmTarget.cxx

@@ -190,6 +190,8 @@ public:
   std::vector<cmListFileBacktrace> CompileFeaturesBacktraces;
   std::vector<std::string> CompileDefinitionsEntries;
   std::vector<cmListFileBacktrace> CompileDefinitionsBacktraces;
+  std::vector<std::string> PrecompileHeadersEntries;
+  std::vector<cmListFileBacktrace> PrecompileHeadersBacktraces;
   std::vector<std::string> SourceEntries;
   std::vector<cmListFileBacktrace> SourceBacktraces;
   std::vector<std::string> LinkOptionsEntries;
@@ -349,6 +351,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
     initProp("Swift_LANGUAGE_VERSION");
     initProp("Swift_MODULE_DIRECTORY");
     initProp("VS_JUST_MY_CODE_DEBUGGING");
+    initProp("DISABLE_PRECOMPILE_HEADERS");
 #ifdef __APPLE__
     if (this->GetGlobalGenerator()->IsXcode()) {
       initProp("XCODE_GENERATE_SCHEME");
@@ -1018,6 +1021,16 @@ cmBacktraceRange cmTarget::GetCompileDefinitionsBacktraces() const
   return cmMakeRange(impl->CompileDefinitionsBacktraces);
 }
 
+cmStringRange cmTarget::GetPrecompileHeadersEntries() const
+{
+  return cmMakeRange(impl->PrecompileHeadersEntries);
+}
+
+cmBacktraceRange cmTarget::GetPrecompileHeadersBacktraces() const
+{
+  return cmMakeRange(impl->PrecompileHeadersBacktraces);
+}
+
 cmStringRange cmTarget::GetSourceEntries() const
 {
   return cmMakeRange(impl->SourceEntries);
@@ -1069,6 +1082,7 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
   MAKE_STATIC_PROP(COMPILE_DEFINITIONS);
   MAKE_STATIC_PROP(COMPILE_FEATURES);
   MAKE_STATIC_PROP(COMPILE_OPTIONS);
+  MAKE_STATIC_PROP(PRECOMPILE_HEADERS);
   MAKE_STATIC_PROP(CUDA_PTX_COMPILATION);
   MAKE_STATIC_PROP(EXPORT_NAME);
   MAKE_STATIC_PROP(IMPORTED_GLOBAL);
@@ -1167,6 +1181,14 @@ void cmTarget::SetProperty(const std::string& prop, const char* value)
       cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
       impl->LinkDirectoriesBacktraces.push_back(lfbt);
     }
+  } else if (prop == propPRECOMPILE_HEADERS) {
+    impl->PrecompileHeadersEntries.clear();
+    impl->PrecompileHeadersBacktraces.clear();
+    if (value) {
+      impl->PrecompileHeadersEntries.emplace_back(value);
+      cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
+      impl->PrecompileHeadersBacktraces.push_back(lfbt);
+    }
   } else if (prop == propLINK_LIBRARIES) {
     impl->LinkImplementationPropertyEntries.clear();
     impl->LinkImplementationPropertyBacktraces.clear();
@@ -1283,6 +1305,12 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value,
       cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
       impl->LinkDirectoriesBacktraces.push_back(lfbt);
     }
+  } else if (prop == "PRECOMPILE_HEADERS") {
+    if (value && *value) {
+      impl->PrecompileHeadersEntries.emplace_back(value);
+      cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
+      impl->PrecompileHeadersBacktraces.push_back(lfbt);
+    }
   } else if (prop == "LINK_LIBRARIES") {
     if (value && *value) {
       cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace();
@@ -1394,6 +1422,13 @@ void cmTarget::InsertLinkDirectory(std::string const& entry,
   impl->LinkDirectoriesBacktraces.insert(btPosition, bt);
 }
 
+void cmTarget::InsertPrecompileHeader(std::string const& entry,
+                                      cmListFileBacktrace const& bt)
+{
+  impl->PrecompileHeadersEntries.push_back(entry);
+  impl->PrecompileHeadersBacktraces.push_back(bt);
+}
+
 static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
                                                   const char* value,
                                                   cmMakefile* context,
@@ -1514,6 +1549,7 @@ const char* cmTarget::GetProperty(const std::string& prop) const
   MAKE_STATIC_PROP(COMPILE_DEFINITIONS);
   MAKE_STATIC_PROP(LINK_OPTIONS);
   MAKE_STATIC_PROP(LINK_DIRECTORIES);
+  MAKE_STATIC_PROP(PRECOMPILE_HEADERS);
   MAKE_STATIC_PROP(IMPORTED);
   MAKE_STATIC_PROP(IMPORTED_GLOBAL);
   MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES);
@@ -1529,6 +1565,7 @@ const char* cmTarget::GetProperty(const std::string& prop) const
     propCOMPILE_FEATURES,
     propCOMPILE_OPTIONS,
     propCOMPILE_DEFINITIONS,
+    propPRECOMPILE_HEADERS,
     propLINK_OPTIONS,
     propLINK_DIRECTORIES,
     propIMPORTED,
@@ -1617,6 +1654,15 @@ const char* cmTarget::GetProperty(const std::string& prop) const
       output = cmJoin(impl->Utilities, ";");
       return output.c_str();
     }
+    if (prop == propPRECOMPILE_HEADERS) {
+      if (impl->PrecompileHeadersEntries.empty()) {
+        return nullptr;
+      }
+
+      static std::string output;
+      output = cmJoin(impl->PrecompileHeadersEntries, ";");
+      return output.c_str();
+    }
     if (prop == propIMPORTED) {
       return this->IsImported() ? "TRUE" : "FALSE";
     }

+ 5 - 0
Source/cmTarget.h

@@ -216,6 +216,8 @@ public:
                         cmListFileBacktrace const& bt, bool before = false);
   void InsertLinkDirectory(std::string const& entry,
                            cmListFileBacktrace const& bt, bool before = false);
+  void InsertPrecompileHeader(std::string const& entry,
+                              cmListFileBacktrace const& bt);
 
   void AppendBuildInterfaceIncludes();
 
@@ -237,6 +239,9 @@ public:
   cmStringRange GetCompileDefinitionsEntries() const;
   cmBacktraceRange GetCompileDefinitionsBacktraces() const;
 
+  cmStringRange GetPrecompileHeadersEntries() const;
+  cmBacktraceRange GetPrecompileHeadersBacktraces() const;
+
   cmStringRange GetSourceEntries() const;
   cmBacktraceRange GetSourceBacktraces() const;
 

+ 36 - 0
Source/cmTargetPrecompileHeadersCommand.cxx

@@ -0,0 +1,36 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmTargetPrecompileHeadersCommand.h"
+
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
+#include "cmTarget.h"
+
+bool cmTargetPrecompileHeadersCommand::InitialPass(
+  std::vector<std::string> const& args, cmExecutionStatus&)
+{
+  return this->HandleArguments(args, "PRECOMPILE_HEADERS");
+}
+
+void cmTargetPrecompileHeadersCommand::HandleMissingTarget(
+  const std::string& name)
+{
+  const std::string e =
+    cmStrCat("Cannot specify precompile headers for target \"", name,
+             "\" which is not built by this project.");
+  this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
+}
+
+std::string cmTargetPrecompileHeadersCommand::Join(
+  const std::vector<std::string>& content)
+{
+  return cmJoin(content, ";");
+}
+
+bool cmTargetPrecompileHeadersCommand::HandleDirectContent(
+  cmTarget* tgt, const std::vector<std::string>& content, bool, bool)
+{
+  tgt->AppendProperty("PRECOMPILE_HEADERS", this->Join(content).c_str());
+  return true;
+}

+ 41 - 0
Source/cmTargetPrecompileHeadersCommand.h

@@ -0,0 +1,41 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmTargetPrecompileHeadersCommand_h
+#define cmTargetPrecompileHeadersCommand_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+#include <vector>
+
+#include "cm_memory.hxx"
+
+#include "cmCommand.h"
+
+#include "cmTargetPropCommandBase.h"
+
+class cmExecutionStatus;
+class cmTarget;
+
+class cmTargetPrecompileHeadersCommand : public cmTargetPropCommandBase
+{
+public:
+  std::unique_ptr<cmCommand> Clone() override
+  {
+    return cm::make_unique<cmTargetPrecompileHeadersCommand>();
+  }
+
+  bool InitialPass(std::vector<std::string> const& args,
+                   cmExecutionStatus& status) override;
+
+private:
+  void HandleMissingTarget(const std::string& name) override;
+
+  bool HandleDirectContent(cmTarget* tgt,
+                           const std::vector<std::string>& content,
+                           bool prepend, bool system) override;
+
+  std::string Join(const std::vector<std::string>& content) override;
+};
+
+#endif

+ 12 - 1
Source/cmVisualStudio10TargetGenerator.cxx

@@ -249,6 +249,8 @@ cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator(
     this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
   this->InSourceBuild = (this->Makefile->GetCurrentSourceDirectory() ==
                          this->Makefile->GetCurrentBinaryDirectory());
+
+  this->LocalGenerator->AddPchDependencies(target, "");
 }
 
 cmVisualStudio10TargetGenerator::~cmVisualStudio10TargetGenerator()
@@ -2148,6 +2150,9 @@ void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0)
       if (si.Kind == cmGeneratorTarget::SourceKindObjectSource) {
         this->OutputSourceSpecificFlags(e2, si.Source);
       }
+      if (si.Source->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) {
+        e2.Element("PrecompiledHeader", "NotUsing");
+      }
       if (!exclude_configs.empty()) {
         this->WriteExcludeFromBuild(e2, exclude_configs);
       }
@@ -2629,6 +2634,13 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
     this->IPOEnabledConfigurations.insert(configName);
   }
 
+  // Precompile Headers
+  std::string pchHeader =
+    this->GeneratorTarget->GetPchHeader(configName, linkLanguage);
+  if (this->MSTools && vcxproj == this->ProjectType && pchHeader.empty()) {
+    clOptions.AddFlag("PrecompiledHeader", "NotUsing");
+  }
+
   // Get preprocessor definitions for this directory.
   std::string defineFlags = this->Makefile->GetDefineFlags();
   if (this->MSTools) {
@@ -2644,7 +2656,6 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
         // replace this setting with "true" below.
         clOptions.AddFlag("UseFullPaths", "false");
       }
-      clOptions.AddFlag("PrecompiledHeader", "NotUsing");
       clOptions.AddFlag("AssemblerListingLocation", "$(IntDir)");
     }
   }

+ 2 - 0
Tests/RunCMake/CMakeLists.txt

@@ -573,3 +573,5 @@ if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|9[0-9])")
 endif()
 
 add_RunCMake_test("CTestCommandExpandLists")
+
+add_RunCMake_test(PrecompileHeaders)

+ 3 - 0
Tests/RunCMake/PrecompileHeaders/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.15.0)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 17 - 0
Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake

@@ -0,0 +1,17 @@
+if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/CMakeFiles/foo.dir/cmake_pch.h")
+    set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/CMakeFiles/foobar.dir/cmake_pch.h")
+else()
+    set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/cmake_pch.h")
+    set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/cmake_pch.h")
+endif()
+
+if (NOT EXISTS ${foo_pch_header})
+   set(RunCMake_TEST_FAILED "Generated foo pch header ${foo_pch_header} does not exist")
+   return()
+endif()
+
+if (EXISTS ${foobar_pch_header})
+   set(RunCMake_TEST_FAILED "Generated foobar pch header ${foobar_pch_header} should not exist")
+   return()
+endif()

+ 14 - 0
Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake

@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.15)
+project(DisabledPch C)
+
+add_library(foo foo.c)
+target_include_directories(foo PUBLIC include)
+target_precompile_headers(foo PUBLIC
+  foo.h
+  <stdio.h>
+  \"string.h\"
+)
+
+add_executable(foobar foobar.c)
+target_link_libraries(foobar foo)
+set_target_properties(foobar PROPERTIES DISABLE_PRECOMPILE_HEADERS ON)

+ 36 - 0
Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake

@@ -0,0 +1,36 @@
+if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/CMakeFiles/foo.dir/cmake_pch.h")
+  set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/CMakeFiles/foobar.dir/cmake_pch.h")
+else()
+  set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/cmake_pch.h")
+  set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/cmake_pch.h")
+endif()
+
+if (NOT EXISTS ${foo_pch_header})
+  set(RunCMake_TEST_FAILED "Generated foo pch header ${foo_pch_header} does not exist")
+  return()
+endif()
+
+if (NOT EXISTS ${foobar_pch_header})
+  set(RunCMake_TEST_FAILED "Generated foobar pch header ${foobar_pch_header} does not exist")
+  return()
+endif()
+
+file(STRINGS ${foo_pch_header} foo_pch_header_strings)
+
+if (NOT "#include \"foo.h\"" IN_LIST foo_pch_header_strings OR
+    NOT "#include <stdio.h>" IN_LIST foo_pch_header_strings OR
+    NOT "#include \"string.h\"" IN_LIST foo_pch_header_strings)
+  set(RunCMake_TEST_FAILED "Generated foo pch header ${foo_pch_header} has bad content")
+  return()
+endif()
+
+file(STRINGS ${foobar_pch_header} foobar_pch_header_strings)
+
+if (NOT "#include \"foo.h\"" IN_LIST foobar_pch_header_strings OR
+    NOT "#include <stdio.h>" IN_LIST foobar_pch_header_strings OR
+    NOT "#include \"string.h\"" IN_LIST foobar_pch_header_strings OR
+    NOT "#include \"bar.h\"" IN_LIST foobar_pch_header_strings)
+  set(RunCMake_TEST_FAILED "Generated foobar pch header ${foobar_pch_header} has bad content")
+  return()
+endif()

+ 20 - 0
Tests/RunCMake/PrecompileHeaders/PchInterface.cmake

@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.15)
+project(PchInterface C)
+
+add_library(foo foo.c)
+target_include_directories(foo PUBLIC include)
+target_precompile_headers(foo PUBLIC
+  foo.h
+  <stdio.h>
+  \"string.h\"
+)
+
+add_library(bar INTERFACE)
+target_include_directories(bar INTERFACE include)
+target_precompile_headers(bar INTERFACE bar.h)
+
+add_executable(foobar foobar.c)
+target_link_libraries(foobar foo bar)
+
+enable_testing()
+add_test(NAME foobar COMMAND foobar)

+ 12 - 0
Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake

@@ -0,0 +1,12 @@
+if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(main_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/main.dir/CMakeFiles/main.dir/cmake_pch.hxx")
+else()
+    set(main_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/main.dir/cmake_pch.hxx")
+endif()
+
+file(STRINGS ${main_pch_header} main_pch_header_strings)
+string(REGEX MATCH "#pragma warning\\(push, 0\\).*#include.*pch.h.*#pragma warning\\(pop\\)" matched_code ${main_pch_header_strings})
+if(NOT matched_code)
+  set(RunCMake_TEST_FAILED "Generated pch file doesn't include expected prologue and epilogue code")
+  return()
+endif()

+ 11 - 0
Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue.cmake

@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.15)
+
+project(PchPrologueEpilogue)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_PCH_PROLOGUE "#pragma warning(push, 0)")
+set(CMAKE_PCH_EPILOGUE "#pragma warning(pop)")
+
+add_executable(main main.cpp)
+target_precompile_headers(main PRIVATE pch.h)

+ 18 - 0
Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake

@@ -0,0 +1,18 @@
+cmake_policy(SET CMP0057 NEW)
+include(RunCMake)
+
+function(run_test name)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
+  run_cmake(${name})
+  # Precompiled headers are not supported with multiple architectures.
+  if(NOT "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]")
+    set(RunCMake_TEST_NO_CLEAN 1)
+    run_cmake_command(${name}-build ${CMAKE_COMMAND} --build . --config Debug)
+    run_cmake_command(${name}-test ${CMAKE_CTEST_COMMAND} -C Debug)
+  endif()
+endfunction()
+
+run_cmake(DisabledPch)
+run_test(PchInterface)
+run_cmake(PchPrologueEpilogue)
+run_test(SkipPrecompileHeaders)

+ 13 - 0
Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake

@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.15)
+
+project(SkipPrecompileHeaders)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+add_executable(pch-test main.cpp non-pch.cpp)
+target_precompile_headers(pch-test PRIVATE pch.h)
+
+set_source_files_properties(non-pch.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
+
+enable_testing()
+add_test(NAME pch-test COMMAND pch-test)

+ 6 - 0
Tests/RunCMake/PrecompileHeaders/foo.c

@@ -0,0 +1,6 @@
+#include "foo.h"
+
+int foo()
+{
+  return 0;
+}

+ 7 - 0
Tests/RunCMake/PrecompileHeaders/foobar.c

@@ -0,0 +1,7 @@
+#include "bar.h"
+#include "foo.h"
+
+int main()
+{
+  return foo() + bar();
+}

+ 9 - 0
Tests/RunCMake/PrecompileHeaders/include/bar.h

@@ -0,0 +1,9 @@
+#ifndef bar_h
+#define bar_h
+
+static int bar()
+{
+  return 0;
+}
+
+#endif

+ 6 - 0
Tests/RunCMake/PrecompileHeaders/include/foo.h

@@ -0,0 +1,6 @@
+#ifndef foo_h
+#define foo_h
+
+extern int foo();
+
+#endif

+ 4 - 0
Tests/RunCMake/PrecompileHeaders/main.cpp

@@ -0,0 +1,4 @@
+int main()
+{
+  return 0;
+}

+ 3 - 0
Tests/RunCMake/PrecompileHeaders/non-pch.cpp

@@ -0,0 +1,3 @@
+#ifdef PCH_INCLUDED
+#  error "PCH must not be included into this file!"
+#endif

+ 3 - 0
Tests/RunCMake/PrecompileHeaders/pch.h

@@ -0,0 +1,3 @@
+#pragma once
+
+#define PCH_INCLUDED 1

+ 1 - 0
Tests/RunCMake/VS10Project/RunCMakeTest.cmake

@@ -25,6 +25,7 @@ run_cmake(VsProjectImport)
 run_cmake(VsPackageReferences)
 run_cmake(VsDpiAware)
 run_cmake(VsDpiAwareBadParam)
+run_cmake(VsPrecompileHeaders)
 
 if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.05)
   run_cmake(VsJustMyCode)

+ 69 - 0
Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake

@@ -0,0 +1,69 @@
+set(pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/cmake_pch.hxx")
+set(pch_source "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/cmake_pch.cxx")
+
+file(TO_NATIVE_PATH "${pch_source}" pch_source_win)
+
+if(NOT EXISTS "${pch_header}")
+  set(RunCMake_TEST_FAILED "Generated PCH header ${pch_header} does not exist.")
+  return()
+endif()
+if(NOT EXISTS "${pch_source}")
+  set(RunCMake_TEST_FAILED "Generated PCH header ${pch_source} does not exist.")
+  return()
+endif()
+
+set(tgt_project "${RunCMake_TEST_BINARY_DIR}/tgt.vcxproj")
+if (NOT EXISTS "${tgt_project}")
+  set(RunCMake_TEST_FAILED "Generated project file ${tgt_project} doesn't exist.")
+  return()
+endif()
+
+file(STRINGS ${tgt_project} tgt_projects_strings)
+
+foreach(line IN LISTS tgt_projects_strings)
+  if (line MATCHES "<PrecompiledHeader.*>Use</PrecompiledHeader>")
+    set(have_pch_use ON)
+  endif()
+
+  if (line MATCHES "<PrecompiledHeader.*>Create</PrecompiledHeader>")
+    set(have_pch_create ON)
+  endif()
+
+  if (line MATCHES "<PrecompiledHeaderFile.*>${pch_header}</PrecompiledHeaderFile>")
+    set(have_pch_header ON)
+  endif()
+
+  if (line MATCHES "<ForcedIncludeFiles.*>${pch_header}</ForcedIncludeFiles>")
+    set(have_force_pch_header ON)
+  endif()
+
+  string(FIND "${line}" "<ClCompile Include=\"${pch_source_win}\">" find_pos)
+  if (NOT find_pos EQUAL "-1")
+    set(have_pch_source_compile ON)
+  endif()
+endforeach()
+
+if (NOT have_pch_use)
+  set(RunCMake_TEST_FAILED "Generated project should have the <PrecompiledHeader>Use</PrecompiledHeader> block.")
+  return()
+endif()
+
+if (NOT have_pch_create)
+  set(RunCMake_TEST_FAILED "Generated project should have the <PrecompiledHeader>Create</PrecompiledHeader> block.")
+  return()
+endif()
+
+if (NOT have_pch_header)
+  set(RunCMake_TEST_FAILED "Generated project should have the <PrecompiledHeaderFile>${pch_header}</PrecompiledHeaderFile> block.")
+  return()
+endif()
+
+if (NOT have_force_pch_header)
+  set(RunCMake_TEST_FAILED "Generated project should have the <ForcedIncludeFiles>${pch_header}</ForcedIncludeFiles> block.")
+  return()
+endif()
+
+if (NOT have_pch_source_compile)
+  set(RunCMake_TEST_FAILED "Generated project should have the <ClCompile Include=\"${pch_source_win}\"> block.")
+  return()
+endif()

+ 4 - 0
Tests/RunCMake/VS10Project/VsPrecompileHeaders.cmake

@@ -0,0 +1,4 @@
+project(VsPrecompileHeaders CXX)
+
+add_library(tgt SHARED empty.cxx)
+target_precompile_headers(tgt PRIVATE stdafx.h)

+ 1 - 0
Tests/RunCMake/XcodeProject/RunCMakeTest.cmake

@@ -12,6 +12,7 @@ run_cmake(XcodeObjectNeedsQuote)
 run_cmake(XcodeOptimizationFlags)
 run_cmake(XcodePreserveNonOptimizationFlags)
 run_cmake(XcodePreserveObjcFlag)
+run_cmake(XcodePrecompileHeaders)
 if (NOT XCODE_VERSION VERSION_LESS 6)
   run_cmake(XcodePlatformFrameworks)
 endif()

+ 35 - 0
Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake

@@ -0,0 +1,35 @@
+set(pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/cmake_pch.hxx")
+
+if(NOT EXISTS "${pch_header}")
+  set(RunCMake_TEST_FAILED "Generated PCH header ${pch_header} does not exist.")
+  return()
+endif()
+
+set(tgt_project "${RunCMake_TEST_BINARY_DIR}/XcodePrecompileHeaders.xcodeproj/project.pbxproj")
+if (NOT EXISTS "${tgt_project}")
+  set(RunCMake_TEST_FAILED "Generated project file ${tgt_project} doesn't exist.")
+  return()
+endif()
+
+file(STRINGS ${tgt_project} tgt_projects_strings)
+
+foreach(line IN LISTS tgt_projects_strings)
+  if (line MATCHES "GCC_PRECOMPILE_PREFIX_HEADER = YES;")
+    set(have_pch_prefix ON)
+  endif()
+
+  string(FIND "${line}" "GCC_PREFIX_HEADER = \"${pch_header}\";" find_pos)
+  if (NOT find_pos EQUAL "-1")
+    set(have_pch_header ON)
+  endif()
+endforeach()
+
+if (NOT have_pch_prefix)
+  set(RunCMake_TEST_FAILED "Generated project should have the GCC_PRECOMPILE_PREFIX_HEADER = YES; line.")
+  return()
+endif()
+
+if (NOT have_pch_header)
+  set(RunCMake_TEST_FAILED "Generated project should have the GCC_PREFIX_HEADER = \"${pch_header}\"; line.")
+  return()
+endif()

+ 4 - 0
Tests/RunCMake/XcodeProject/XcodePrecompileHeaders.cmake

@@ -0,0 +1,4 @@
+project(XcodePrecompileHeaders CXX)
+
+add_library(tgt foo.cpp)
+target_precompile_headers(tgt PRIVATE stdafx.h)

+ 2 - 0
Utilities/IWYU/mapping.imp

@@ -23,6 +23,7 @@
   # HACK: check whether this can be removed with next iwyu release.
   { include: [ "<bits/shared_ptr.h>", private, "<memory>", public ] },
   { include: [ "<bits/std_function.h>", private, "<functional>", public ] },
+  { include: [ "<bits/refwrap.h>", private, "<functional>", public ] },
   { include: [ "<bits/stdint-intn.h>", private, "<stdint.h>", public ] },
   { include: [ "<bits/stdint-uintn.h>", private, "<stdint.h>", public ] },
   { include: [ "<bits/time.h>", private, "<time.h>", public ] },
@@ -73,6 +74,7 @@
   # Use '-Xiwyu -v7' to see the fully qualified names that need this.
   # TODO: Can this be simplified with an @-expression?
   #{ symbol: [ "@std::__decay_and_strip<.*>::__type", private, "\"cmConfigure.h\"", public ] },
+  { symbol: [ "std::__decay_and_strip<char const (&)[1]>::__type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__decay_and_strip<cmCommand *&>::__type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__decay_and_strip<cmGeneratorTarget *&>::__type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__decay_and_strip<cmFindCommon::PathLabel &>::__type", private, "\"cmConfigure.h\"", public ] },

+ 1 - 0
bootstrap

@@ -435,6 +435,7 @@ CMAKE_CXX_SOURCES="\
   cmTargetCompileOptionsCommand \
   cmTargetIncludeDirectoriesCommand \
   cmTargetLinkLibrariesCommand \
+  cmTargetPrecompileHeadersCommand \
   cmTargetPropCommandBase \
   cmTargetPropertyComputer \
   cmTargetSourcesCommand \