Answers:
有一个名为“ Cotire”的第三方CMake模块,它可以自动对基于CMake的构建系统使用预编译的头,并且还支持统一构建。
我使用以下宏来生成和使用预编译头文件:
MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecompiledHeader PrecompiledSource SourcesVar)
IF(MSVC)
GET_FILENAME_COMPONENT(PrecompiledBasename ${PrecompiledHeader} NAME_WE)
SET(PrecompiledBinary "${CMAKE_CURRENT_BINARY_DIR}/${PrecompiledBasename}.pch")
SET(Sources ${${SourcesVar}})
SET_SOURCE_FILES_PROPERTIES(${PrecompiledSource}
PROPERTIES COMPILE_FLAGS "/Yc\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\""
OBJECT_OUTPUTS "${PrecompiledBinary}")
SET_SOURCE_FILES_PROPERTIES(${Sources}
PROPERTIES COMPILE_FLAGS "/Yu\"${PrecompiledHeader}\" /FI\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\""
OBJECT_DEPENDS "${PrecompiledBinary}")
# Add precompiled header to SourcesVar
LIST(APPEND ${SourcesVar} ${PrecompiledSource})
ENDIF(MSVC)
ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER)
假设您的所有源文件都有一个变量$ {MySources},那么您想要使用的代码就是
ADD_MSVC_PRECOMPILED_HEADER("precompiled.h" "precompiled.cpp" MySources)
ADD_LIBRARY(MyLibrary ${MySources})
该代码在非MSVC平台上仍然可以正常运行。漂亮整齐 :)
list( APPEND ... )
外部移至结束位置endif()
。在此处查看完整的代码:pastebin.com/84dm5rXZ
/Yu
和/FI
参数,它们应该是,${PrecompiledHeader}
而不是${PrecompiledBinary}
。
/YuC:/foo/bar.h
将迫使您通过/FpC:/foo/bar.h
标志或将#include <C:/foo/bar.h>
所有.cpp文件的顶部作为第一个include语句。MSVC对#include
参数进行字符串比较,不检查它是否指向与指定文件相同的文件/Yu
。Ergo,#include <bar.h>
将无法正常工作并发出错误C2857。
CMake刚刚获得了对PCH的支持,它将在即将于2019年10月1日发布的3.16版本中提供:
https://gitlab.kitware.com/cmake/cmake/merge_requests/3553
target_precompile_headers(<target>
<INTERFACE|PUBLIC|PRIVATE> [header1...]
[<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])
关于支持在目标之间共享PCH的讨论正在进行中:https ://gitlab.kitware.com/cmake/cmake/issues/19659
https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/中提供了一些其他上下文(动机,数字)
这是一个代码段,可让您为项目使用预编译的标头。将以下内容添加到您的CMakeLists.txt中,myprecompiledheaders
并myproject_SOURCE_FILES
根据需要添加:
if (MSVC)
set_source_files_properties(myprecompiledheaders.cpp
PROPERTIES
COMPILE_FLAGS "/Ycmyprecompiledheaders.h"
)
foreach( src_file ${myproject_SOURCE_FILES} )
set_source_files_properties(
${src_file}
PROPERTIES
COMPILE_FLAGS "/Yumyprecompiledheaders.h"
)
endforeach( src_file ${myproject_SOURCE_FILES} )
list(APPEND myproject_SOURCE_FILES myprecompiledheaders.cpp)
endif (MSVC)
with set( CMAKE_AUTOMOC ON )
。
myprecompiledheader.cpp
先编译吗?从该代码段来看,它似乎将在最后编译,所以也许这就是造成延迟的原因。myprecompiledheader.h
仅包含我的代码使用的最常见的STL标头。
我最终使用了适应版本的larsm宏。将$(IntDir)用于pch路径可将调试和发布版本的预编译头分开。
MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecompiledHeader PrecompiledSource SourcesVar)
IF(MSVC)
GET_FILENAME_COMPONENT(PrecompiledBasename ${PrecompiledHeader} NAME_WE)
SET(PrecompiledBinary "$(IntDir)/${PrecompiledBasename}.pch")
SET(Sources ${${SourcesVar}})
SET_SOURCE_FILES_PROPERTIES(${PrecompiledSource}
PROPERTIES COMPILE_FLAGS "/Yc\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\""
OBJECT_OUTPUTS "${PrecompiledBinary}")
SET_SOURCE_FILES_PROPERTIES(${Sources}
PROPERTIES COMPILE_FLAGS "/Yu\"${PrecompiledHeader}\" /FI\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\""
OBJECT_DEPENDS "${PrecompiledBinary}")
# Add precompiled header to SourcesVar
LIST(APPEND ${SourcesVar} ${PrecompiledSource})
ENDIF(MSVC)
ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER)
ADD_MSVC_PRECOMPILED_HEADER("stdafx.h" "stdafx.cpp" MY_SRCS)
ADD_EXECUTABLE(MyApp ${MY_SRCS})
改编自Dave,但效率更高(设置目标属性,而不是为每个文件设置):
if (MSVC)
set_target_properties(abc PROPERTIES COMPILE_FLAGS "/Yustd.h")
set_source_files_properties(std.cpp PROPERTIES COMPILE_FLAGS "/Ycstd.h")
endif(MSVC)
abc
你的榜样?
如果您不想重新发明轮子,只需使用Cotire作为最佳答案,或者在此处使用一个更简单的cmake预编译标题 。要使用它,只需包含模块并调用:
include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )
CMake 3.16引入了对预编译头的支持。有一个新的CMake命令target_precompile_headers
可以在后台执行您需要的所有操作。有关更多详细信息,请参见其文档:https : //cmake.org/cmake/help/latest/command/target_precompile_headers.html
“ stdafx.h”,“ stdafx.cpp”-预编译的标头名称。
将以下内容放在根cmake文件中。
if (MSVC)
# For precompiled header.
# Set
# "Precompiled Header" to "Use (/Yu)"
# "Precompiled Header File" to "stdafx.h"
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Yustdafx.h /FIstdafx.h")
endif()
将以下内容放入项目cmake文件中。
“ src”-包含源文件的文件夹。
set_source_files_properties(src/stdafx.cpp
PROPERTIES
COMPILE_FLAGS "/Ycstdafx.h"
)
恕我直言,最好的方法是为整个项目设置PCH(如martjno所建议的),并结合在需要时忽略某些来源(例如生成的来源)的PCH的能力:
# set PCH for VS project
function(SET_TARGET_PRECOMPILED_HEADER Target PrecompiledHeader PrecompiledSource)
if(MSVC)
SET_TARGET_PROPERTIES(${Target} PROPERTIES COMPILE_FLAGS "/Yu${PrecompiledHeader}")
set_source_files_properties(${PrecompiledSource} PROPERTIES COMPILE_FLAGS "/Yc${PrecompiledHeader}")
endif(MSVC)
endfunction(SET_TARGET_PRECOMPILED_HEADER)
# ignore PCH for a specified list of files
function(IGNORE_PRECOMPILED_HEADER SourcesVar)
if(MSVC)
set_source_files_properties(${${SourcesVar}} PROPERTIES COMPILE_FLAGS "/Y-")
endif(MSVC)
endfunction(IGNORE_PRECOMPILED_HEADER)
因此,如果您有一些目标MY_TARGET以及生成的源IGNORE_PCH_SRC_LIST的列表,则只需执行以下操作:
SET_TARGET_PRECOMPILED_HEADER(MY_TARGET stdafx.h stdafx.cpp)
IGNORE_PRECOMPILED_HEADER(IGNORE_PCH_SRC_LIST)
此方法已通过测试,可完美运行。
好了,每当您在任何项目文件中更改一行时,在四核计算机上进行构建都需要10分钟以上的时间,它会告诉您添加Windows预编译头文件的时间。在* nux上,我只使用ccache而不用担心。
我已经在我的主应用程序及其使用的一些库中实现了。到目前为止,它的工作原理非常好。还需要做的一件事是,您必须创建pch源文件和头文件,并且在源文件中包括要预编译的所有头文件。我在MFC上做了12年,但是花了我几分钟的时间来回忆。
最干净的方法是将预编译选项添加为全局选项。在vcxproj文件中,这将显示为,<PrecompiledHeader>Use</PrecompiledHeader>
而不是对每个文件都执行此操作。
然后,您需要将该Create
选项添加到StdAfx.cpp。以下是我的用法:
MACRO(ADD_MSVC_PRECOMPILED_HEADER SourcesVar)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /YuStdAfx.h")
set_source_files_properties(StdAfx.cpp
PROPERTIES
COMPILE_FLAGS "/YcStdAfx.h"
)
list(APPEND ${${SourcesVar}} StdAfx.cpp)
ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER)
file(GLOB_RECURSE MYDLL_SRC
"*.h"
"*.cpp"
"*.rc")
ADD_MSVC_PRECOMPILED_HEADER(MYDLL_SRC)
add_library(MyDll SHARED ${MYDLL_SRC})
这已经过测试,并且适用于MSVC 2010,并且将创建MyDll.pch文件。我不介意使用什么文件名,因此我没有做出任何努力来指定它。
由于预编译的头文件选项不适用于rc文件,因此我需要调整jari提供的宏。
#######################################################################
# Makro for precompiled header
#######################################################################
MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecompiledHeader PrecompiledSource SourcesVar)
IF(MSVC)
GET_FILENAME_COMPONENT(PrecompiledBasename ${PrecompiledHeader} NAME_WE)
SET(PrecompiledBinary "$(IntDir)/${PrecompiledBasename}.pch")
SET(Sources ${${SourcesVar}})
# generate the precompiled header
SET_SOURCE_FILES_PROPERTIES(${PrecompiledSource}
PROPERTIES COMPILE_FLAGS "/Zm500 /Yc\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\""
OBJECT_OUTPUTS "${PrecompiledBinary}")
# set the usage of this header only to the other files than rc
FOREACH(fname ${Sources})
IF ( NOT ${fname} MATCHES ".*rc$" )
SET_SOURCE_FILES_PROPERTIES(${fname}
PROPERTIES COMPILE_FLAGS "/Zm500 /Yu\"${PrecompiledHeader}\" /FI\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\""
OBJECT_DEPENDS "${PrecompiledBinary}")
ENDIF( NOT ${fname} MATCHES ".*rc$" )
ENDFOREACH(fname)
# Add precompiled header to SourcesVar
LIST(APPEND ${SourcesVar} ${PrecompiledSource})
ENDIF(MSVC)
ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER)
编辑:使用此预编译的头文件将我的主项目的总体构建时间从4min 30s减少到1min 40s。对我来说,这确实是一件好事。在预编译头中,只有诸如boost / stl / Windows / mfc之类的头。
甚至不要去那里。预编译标头意味着,只要其中一个标头发生更改,就必须重新构建所有内容。如果您拥有能够实现这一目标的构建系统,那么您将很幸运。通常,您的构建会失败,直到您意识到更改了预编译的内容,因此您需要进行完全重建。您可以通过预编译绝对肯定不会改变的标头来避免这种情况,但同时您也会放弃很大一部分速度增益。
另一个问题是,在许多使用预编译标头的地方,您的命名空间被各种您不知道或不关心的符号污染。