使用CMake预编译的头文件

Glu*_*ous 99 c++ gcc cmake precompiled-headers visual-studio

我在网上看到了一些关于在CMake中对预编译头文件进行黑客攻击的一些(旧)帖子.它们看起来都有点遍布整个地方,每个人都有自己的方式.目前最好的方法是什么?

sak*_*kra 78

有一个名为"Cotire"的第三方CMake模块可以自动使用基于CMake的构建系统的预编译头,并且还支持统一构建.

  • +1导致我cotire.这是一件非常好的工作. (15认同)
  • CMake 3.16 引入了对预编译头的内置支持。请参阅我的答案 /sf/answers/4165982061/ 不再需要第三方模块! (5认同)
  • @onqtam这么简单,它是如此庞大,以至于我什至都看不到如何简单地使用cmake和gcc进行预编译 (2认同)

lar*_*moa 35

我使用以下宏来生成和使用预编译头:

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)
Run Code Online (Sandbox Code Playgroud)

假设你有一个带有所有源文件的变量$ {MySources},你想要使用的代码就是

ADD_MSVC_PRECOMPILED_HEADER("precompiled.h" "precompiled.cpp" MySources)
ADD_LIBRARY(MyLibrary ${MySources})
Run Code Online (Sandbox Code Playgroud)

代码在非MSVC平台上仍然可以正常运行.很简约 :)

  • 这个宏有 1 个缺陷。如果生成器不是基于 MSVC 的,则不会将预编译源添加到源列表中。所以我的修改只是将 `list( APPEND ... )` 移到关闭的 `endif()` 之外。在此处查看完整代码:http://pastebin.com/84dm5rXZ (2认同)
  • 请注意,/ Yu的论据是非常字面的.例如`/ YuC:/ foo/bar.h`将强制您传递`/ FpC:/ foo/bar.h`标志或将`#include <C:/foo/bar.h>`放在顶部将所有.cpp文件作为第一个include语句.MSVC对`#include`参数进行字符串比较,它不检查它是否指向与给`/ Yu`相同的文件.因此,`#include <bar.h>`将无效并发出错误C2857. (2认同)

Dav*_*ier 20

这是一个代码片段,允许您为项目使用预编译头.将以下内容添加到您的CMakeLists.txt中,myprecompiledheadersmyproject_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)
Run Code Online (Sandbox Code Playgroud)


小智 13

我最终使用了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})
Run Code Online (Sandbox Code Playgroud)

  • $ {IntDir}抽象很有帮助.谢谢. (2认同)

mar*_*jno 13

改编自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)
Run Code Online (Sandbox Code Playgroud)

  • 我曾经使用过这个解决方案,但它只适用于项目只包含c ++文件的情况.由于COMPILE_FLAGS应用于所有源文件,因此它也将应用于c文件(例如MIDL生成的文件),这些文件与c ++ PCH不同.使用Dave的解决方案时,您可以使用get_source_file_property(_language $ {src_file} LANGUAGE),并且只设置编译器标志(如果它确实是CXX文件). (2认同)
  • 可以使用[set_source_files_properties](http://www.cmake.org)[/Y- ](http://msdn.microsoft.com/en-us/library/1hy7a92h.aspx)为单个文件选择性地关闭它. /cmake/help/v2.8.12/cmake.html#command:set_source_files_properties) (2认同)

jan*_*aur 8

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...] ...])
Run Code Online (Sandbox Code Playgroud)

关于支持在目标之间共享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/中提供了一些其他上下文(动机,数字)

  • 以下是官方 CMake 文档的链接:https://cmake.org/cmake/help/latest/command/target_precompile_headers.html (3认同)
  • MSVC 团队发布了一篇关于确定 PCH 中包含哪些标头的文章:https://devblogs.microsoft.com/cppblog/faster-builds-with-pch-suggestions-from-c-build-insights/ (2认同)
  • @JoergS CMake 文档指出:“命名的 &lt;target&gt; 必须由 add_executable() 或 add_library() 等命令创建,并且不能是 ALIAS 目标”。https://cmake.org/cmake/help/latest/command/target_precompile_headers.html 只需在 'add_library(xxx ...)' 或 ' add_executable(xxx ...) 之后添加 'target_precompile_headers(xxx ...) ' (2认同)

Rom*_*lov 7

如果你不想推倒重来,只使用两种Cotire作为顶端回答暗示或简单的一个- 的cmake-预编译头 在这里.要使用它只需包含模块并调用:

include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )
Run Code Online (Sandbox Code Playgroud)


Dir*_*eld -18

甚至不要去那里。预编译标头意味着每当标头之一发生更改时,您都必须重建所有内容。如果您有一个能够实现这一点的构建系统,那么您很幸运。通常情况下,您的构建会失败,直到您意识到您更改了正在预编译的某些内容,因此您需要进行完全重建。您可以通过预编译您绝对不会改变的标头来避免这种情况,但这样您也会放弃很大一部分速度增益。

另一个问题是,您的命名空间会被各种您不知道或不关心的符号污染,而这些符号在您使用预编译头的许多地方都是不知道或不关心的。

  • 当预编译标头引用“不”改变的标头时,它们是最有用的……STL、Boost、其他第三方的东西。如果您将 PCH 用于您自己的项目头文件,那么您就浪费了大部分好处。 (32认同)
  • 即使您将 PCH 用于自己项目的标头,像 CMake 这样的构建系统的全部要点就是确保依赖关系*得到*尊重。如果我更改 .h 文件(或其依赖项之一),我*希望*重新生成 .pch。如果我不更改 .h 文件,我*不*希望重新生成 .pch。我认为OP的整个问题是:如何使用CMake实现这一点? (4认同)
  • 预编译头是减少编译时间的最佳工具,直到所有主流编译器都支持 C++ 模块。他们解决了一个随着模板和仅标头库的使用不断增加而变得更糟的问题。如果使用得当,是无可替代的。无论如何,这并不构成所问问题的答案,而只是表达意见。投了反对票和删除票。 (2认同)