【发布时间】:2010-09-13 23:55:15
【问题描述】:
我在 'net 上看到了一些(旧)帖子,这些帖子是关于在 CMake 中将一些对预编译头文件的支持结合在一起的。他们似乎都有点到处都是,每个人都有自己的做事方式。目前最好的方法是什么?
【问题讨论】:
标签: c++ visual-studio gcc cmake precompiled-headers
我在 'net 上看到了一些(旧)帖子,这些帖子是关于在 CMake 中将一些对预编译头文件的支持结合在一起的。他们似乎都有点到处都是,每个人都有自己的做事方式。目前最好的方法是什么?
【问题讨论】:
标签: c++ visual-studio gcc cmake precompiled-headers
有一个third party CMake module named 'Cotire' 可以自动为基于 CMake 的构建系统使用预编译头文件,并且还支持统一构建。
【讨论】:
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/ 上提供了一些额外的背景信息(动机、数字)
【讨论】:
target_precompile_headers(MyTarget PUBLIC "$<$<COMPILE_LANGUAGE:OBJC,OBJCXX>:${CMAKE_CURRENT_LIST_DIR}/src/MyPrefixHeader.pch>") 之类的语法 - 如果您的标头不支持 C/C++,或者只是添加支持(通过将任何 #import 包装在 #ifdef __OBJC__ 检查中)。
Cannot specify precompile headers for target "xxx," which is not built by this project 的错误消息。很可能是因为 CMakeLists.txt 包含在其他地方。也许有人有一个如何在多项目环境中使用它的例子?
我使用以下宏来生成和使用预编译头文件:
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 文件的顶部。 MSVC 对#include 参数进行字符串比较,它不检查它是否指向与/Yu 相同的文件。因此,#include <bar.h> 将不起作用并发出错误 C2857。
这里有一个代码 sn-p 允许您为您的项目使用预编译头文件。
将以下内容添加到您的 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?从这个 sn-p 看起来它将最后编译,所以也许这可能是导致延迟的原因。 myprecompiledheader.h 仅包含我的代码使用的最常见的 STL 标头。
我最终使用了 larsm 宏的改编版本。对 pch 路径使用 $(IntDir) 可以将用于调试和发布构建的预编译头文件分开。
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-precompiled-header here。要使用它,只需包含模块并调用:
include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )
【讨论】:
"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"
)
【讨论】:
恕我直言,最好的方法是按照 martjno 的建议为整个项目设置 PCH,并结合在需要时忽略某些源的 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)
编辑:使用此预编译头文件将我的主项目的总体构建时间从 4 分钟 30 秒减少到 1 分钟 40 秒。这对我来说真的是一件好事。在预编译头文件中只有 boost/stl/Windows/mfc 之类的头文件。
【讨论】:
甚至不要去那里。预编译的标头意味着每当其中一个标头发生更改时,您都必须重新构建所有内容。如果你有一个能够实现这一点的构建系统,你就很幸运了。通常情况下,您的构建会失败,直到您意识到您更改了正在预编译的内容,因此您需要进行完全重建。您可以通过预编译您绝对肯定不会更改的标头来避免这种情况,但是您也放弃了大部分速度增益。
另一个问题是你的命名空间被各种你不知道或不关心的符号污染了,在很多地方你会使用预编译的头文件。
【讨论】: