将预编译的头与CMake一起使用
Using pre-compiled headers with CMake
我在网上看到了一些(旧的)帖子,关于在CMake中破解对预编译头的一些支持。他们似乎到处都是,每个人都有自己的方式。目前最好的方式是什么?
CMake刚刚获得了对PCH(预编译头)的支持,从3.16(2019年10月发布)起提供:
https://gitlab.kitware.com/cmake/cmake/merge_requests/3553
target_precompile_headers(<target>
<INTERFACE|PUBLIC|PRIVATE> [header1...]
[<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])
通过REUSE_FROM
关键字(例如此处)支持在目标之间共享PCH。
在https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/
Im使用以下宏生成并使用预编译头:
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平台上仍然可以正常运行。非常整洁:)
这里有一个代码片段,允许您在项目中使用预编译头。将以下内容添加到您的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)
我最终使用了一个经过调整的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)
如果你不想重新发明轮子,只需使用Cotire作为最重要的答案,或者使用更简单的答案-cmake预编译头。要使用它,只需包含模块并调用:
include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )
cmake和Visual Studio 2015使用预编译头的示例
"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"
)
IMHO最好的方法是为整个项目设置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分钟以上时,每次您更改任何项目文件中的一行,它都会告诉您为窗口添加预编译头的时间。在*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)
编辑:使用这个预编译的头文件将我的主要项目的总体构建时间从4分钟30秒减少到1分钟40秒。这对我来说是一件非常好的事情。预编译头中只有boost/stl/Windows/mfc这样的头。
甚至不要去那里。预编译的标头意味着,只要其中一个标头发生更改,就必须重新生成所有。如果你有一个实现这一点的构建系统,那你就很幸运了。通常情况下,您的构建会失败,直到您意识到您更改了正在预编译的内容,因此需要进行完全的重建。你可以避免这种情况,主要是通过预编译你绝对肯定不会改变的头,但你也会放弃很大一部分速度增益。
另一个问题是,在使用预编译头的许多地方,你的命名空间会被各种你不知道或不关心的符号污染。
- 将--whole archive链接器选项与CMake和具有其他库依赖项的库一起使用
- 如何将GTest与CMake一起使用?遵循谷歌指南时的链接问题
- 如何将动态链接库与CMake一起使用
- SSE 标志应该如何与现代 CMake 一起添加?
- 将外部C++库与 CMake 一起使用时出错
- 如何将 Pybind11 与 CMAKE 一起使用以链接 2 个模块
- 如何将 c++20 模块与 CMake 一起使用
- 如何将编译的 wxwidgets 与 cmake 一起使用
- 当它们不与GCC和CMAKE一起使用时,如何解析未定义的引用
- 如何将库与CMAKE一起包含在OSX捆绑包中
- 当将QT创建者与CMAKE一起使用时,为什么我不必手动链接MSVC库
- 将特征与cmake一起使用
- 在将QT与Cmake一起使用时解决链接错误
- 如何将外部库与 CMake 一起使用
- 如何与CMAKE一起使用VS 2017编译的Boost Filesystem库
- 如何将GCOV与Cmake一起使用
- 如何将OpenCV的测试框架与CMake一起使用?
- 如何将PJSIP库与CMake一起使用
- 如何将该工具与 CMake 一起使用'Include What You Use'来检测未使用的标头?
- 将预编译的头与CMake一起使用