简体   繁体   English

在 CMake 中使用预编译的头文件

[英]Using pre-compiled headers with CMake

I have seen a few (old) posts on the 'net about hacking together some support for pre-compiled headers in CMake.我在网上看到了一些(旧的)帖子,关于在 CMake 中对预编译头的一些支持进行黑客攻击。 They all seem a bit all-over the place and everyone has their own way of doing it.他们似乎都有点无处不在,每个人都有自己的做法。 What is the best way of doing it currently?目前最好的方法是什么?

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

Im using the following macro to generate and use precompiled headers:我使用以下宏来生成和使用预编译头:

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)

Lets say you have a variable ${MySources} with all your sourcefiles, the code you would want to use would be simply be假设您有一个包含所有源文件的变量 ${MySources},您想要使用的代码就是

ADD_MSVC_PRECOMPILED_HEADER("precompiled.h" "precompiled.cpp" MySources)
ADD_LIBRARY(MyLibrary ${MySources})

The code would still function just fine on non-MSVC platforms too.该代码在非 MSVC 平台上仍然可以正常运行。 Pretty neat :)漂亮整齐 :)

CMake has just gained support for PCHs (pre-compiled headers), it should be available in the upcoming 3.16 release, due 2019-10-01: CMake 刚刚获得了对 PCH(预编译头)的支持,它应该会在即将到来的 3.16 版本中可用,由于 2019-10-01:

https://gitlab.kitware.com/cmake/cmake/merge_requests/3553 https://gitlab.kitware.com/cmake/cmake/merge_requests/3553

  target_precompile_headers(<target>
    <INTERFACE|PUBLIC|PRIVATE> [header1...]
    [<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])

There is ongoing discussion on supporting sharing PCHs between targets: https://gitlab.kitware.com/cmake/cmake/issues/19659正在讨论支持在目标之间共享 PCH: https ://gitlab.kitware.com/cmake/cmake/issues/19659

There is some additional context (motivation, numbers) available at https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/ https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/提供了一些额外的上下文(动机、数字)

Here is a code snippet to allow you to use precompiled header for your project.这是一个代码片段,允许您为您的项目使用预编译头。 Add the following to your CMakeLists.txt replacing myprecompiledheaders and myproject_SOURCE_FILES as appropriate:将以下内容添加到 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)

I ended up using an adapted version of larsm macro.我最终使用了 larsm 宏的改编版本。 Using $(IntDir) for pch path keeps precompiled headers for debug and release builds separate.对 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})

Adapted from Dave, but more efficient (sets target properties, not for each file):改编自 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)

if you don't wanna reinvent the wheel, just use either Cotire as the top answer suggests or a simpler one - cmake-precompiled-header here .如果您不想重新发明轮子,只需使用 Cotire 作为最佳答案建议或更简单的一个 - cmake-precompiled-header here To use it just include the module and call:要使用它,只需包含模块并调用:

include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )

I have seen a few (old) posts on the 'net about hacking together some support for pre-compiled headers in CMake.我在网上看到一些(旧的)帖子,内容涉及一起破解CMake中对预编译标头的一些支持。 They all seem a bit all-over the place and everyone has their own way of doing it.他们似乎遍地都是,每个人都有自己的做事方式。 What is the best way of doing it currently?目前最好的方法是什么?

An example of usage precompiled header with cmake and Visual Studio 2015使用 cmake 和 Visual Studio 2015 预编译头的示例

"stdafx.h", "stdafx.cpp" - precompiled header name. "stdafx.h", "stdafx.cpp" - 预编译的头文件名。

Put the following below in the root cmake file.将以下内容放在根 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()

Put the following below in the project cmake file.将以下内容放入项目 cmake 文件中。

"src" - a folder with source files. “src” - 包含源文件的文件夹。

set_source_files_properties(src/stdafx.cpp
    PROPERTIES
    COMPILE_FLAGS "/Ycstdafx.h"
)

IMHO the best way is to set PCH for whole project, as martjno suggested, combined with ability of ignoring PCH for some sources if needed (eg generated sources):恕我直言,最好的方法是为整个项目设置 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)

So, if you have some target MY_TARGET, and list of generated sources IGNORE_PCH_SRC_LIST you'll simply do:因此,如果您有一些目标 MY_TARGET 和生成的源列表 IGNORE_PCH_SRC_LIST,您只需执行以下操作:

SET_TARGET_PRECOMPILED_HEADER(MY_TARGET stdafx.h stdafx.cpp)
IGNORE_PRECOMPILED_HEADER(IGNORE_PCH_SRC_LIST)

This aproach is tested and works perfectly.这种方法已经过测试并且运行良好。

The cleanest way is to add the precompiled option as a global option.最干净的方法是将预编译选项添加为全局选项。 In the vcxproj file this will show up as <PrecompiledHeader>Use</PrecompiledHeader> and not do this for every individual file.在 vcxproj 文件中,这将显示为<PrecompiledHeader>Use</PrecompiledHeader>而不是对每个单独的文件都这样做。

Then you need to add the Create option to the StdAfx.cpp.然后您需要将Create选项添加到 StdAfx.cpp。 The following is how I use it:以下是我如何使用它:

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

This is tested and works for MSVC 2010 and will create a MyDll.pch file, I am not bothered what file name is used so I didn't make any effort to specify it.这已经过测试并适用于 MSVC 2010 并将创建一个 MyDll.pch 文件,我不关心使用什么文件名,所以我没有努力指定它。

As the precompiled header option doesnt work for rc files, i needed to adjust the macro supplied by jari.由于预编译头选项不适用于 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)

Edit: The usage of this precompiled headers reduced the Overall build time of my main Project from 4min 30s down to 1min 40s.编辑:使用这个预编译头文件将我的主项目的总体构建时间从 4 分 30 秒减少到 1 分 40 秒。 This is for me a really good thing.这对我来说真的是一件好事。 In the precompile header are only headers like boost/stl/Windows/mfc.在预编译头文件中只有像 boost/stl/Windows/mfc 这样的头文件。

Well when builds take 10+ minutes on a quad core machine every time you change a single line in any of the project files it tells you its time to add precompiled headers for windows.好吧,当您每次更改任何项目文件中的一行时,在四核机器上构建需要 10 分钟以上时,它会告诉您是时候为 Windows 添加预编译头文件了。 On *nux I would just use ccache and not worry about that.在 *nux 上,我只会使用 ccache 而不必担心。

I have implemented in my main application and a few of the libraries that it uses.我已经在我的主应用程序和它使用的一些库中实现了。 It works great to this point.在这一点上它工作得很好。 One thing that also is needed is you have to create the pch source and header file and in the source file include all the headers that you want to be precompiled.还需要做的一件事是您必须创建 pch 源文件和头文件,并在源文件中包含所有要预编译的头文件。 I did this for 12 years with MFC but it took me a few minutes to recall that..我用 MFC 这样做了 12 年,但我花了几分钟才回忆起来。

Don't even go there.甚至不要去那里。 Precompiled headers mean that whenever one of the headers changes, you have to rebuild everything .预编译头意味着只要其中一个头发生变化,您就必须重建所有内容 You're lucky if you have a build system that realizes this.如果你有一个实现这一点的构建系统,你就很幸运了。 More often than never, your build will just fail until you realize that you changed something that is being precompiled, and therefore you need to do a full rebuild.通常情况下,您的构建会失败,直到您意识到您更改了正在预编译的内容,因此您需要进行完整的重建。 You can avoid this mostly by precompiling the headers that you are absolutely positive won't change, but then you're giving up a large part of the speed gain as well.您可以通过预编译您绝对肯定不会改变的标头来避免这种情况,但是您也放弃了大部分速度增益。

The other problem is that your namespace gets polluted with all kinds of symbols that you don't know or care about in many places where you'd be using the precompiled headers.另一个问题是,在许多使用预编译头文件的地方,您的命名空间被各种您不知道或不关心的符号所污染。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM