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 <kwrobot@kitware.com>
Acked-by: Daniel Pfeifer <daniel@pfeifer-mail.de>
Acked-by: Ivan171 <heavenandhell171@gmail.com>
Acked-by: Stanislav Ershov <digital.stream.of.mind@gmail.com>
Acked-by: Steve Mokris <smokris@softpixel.com>
Acked-by: Evgeniy Dushistov <dushistov@mail.ru>
Acked-by: Danila Malyutin <flashmozzg@gmail.com>
Acked-by: Viktor Kirilov <vik.kirilov@gmail.com>
Acked-by: Lucas Zhao <zhaopf6@163.com>
Merge-request: !3553
This commit is contained in:
Brad King 2019-08-29 13:37:26 +00:00 committed by Kitware Robot
commit fa36e2151d
63 changed files with 924 additions and 6 deletions

View File

@ -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`.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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`.

View File

@ -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.

View File

@ -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)>`.

View File

@ -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.

View File

@ -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``.

View File

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

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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")

View File

@ -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)

View File

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

View File

@ -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",

View File

@ -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);

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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*>
{
};

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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() =

View File

@ -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() =

View File

@ -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) {

View File

@ -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() =

View File

@ -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;

View File

@ -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);

View File

@ -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";
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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)");
}
}

View File

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

View File

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

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)

View File

@ -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()

View File

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

View File

@ -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()

View File

@ -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()

View File

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

View File

@ -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 ] },

View File

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