112

我在网上看到了一些(旧)帖子,这些帖子是关于在 CMake 中将一些对预编译头文件的支持结合在一起的。他们似乎都有点到处都是,每个人都有自己的做事方式。目前最好的方法是什么?

4

13 回答 13

80

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

于 2012-03-23T11:46:22.170 回答
36

CMake 刚刚获得了对 PCH(预编译头文件)的支持,它应该在即将到来的 3.16 版本中可用,到期时间为 2019 年 10 月 1 日:

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/提供了一些额外的上下文(动机、数字)

于 2019-08-29T19:13:17.633 回答
34

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

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 平台上仍然可以正常运行。挺整洁的 :)

于 2009-09-04T15:46:04.090 回答
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)
于 2008-11-03T23:47:56.673 回答
13

我最终使用了 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})
于 2010-06-02T09:28:27.277 回答
12

改编自 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)
于 2010-07-09T08:26:42.473 回答
7

如果您不想重新发明轮子,只需按照最佳答案的建议使用 Cotire 或使用更简单的一个 - cmake-precompiled-header here。要使用它,只需包含模块并调用:

include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )
于 2017-02-02T14:44:04.203 回答
4

使用 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"
)
于 2016-05-13T17:27:35.520 回答
3

恕我直言,最好的方法是为整个项目设置 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)

这种方法经过测试并且效果很好。

于 2015-04-16T10:38:18.357 回答
0

好吧,当您每次更改任何项目文件中的一行时,在四核机器上构建需要 10 多分钟,它会告诉您是时候为 Windows 添加预编译头文件了。在 *nux 上,我只会使用 ccache 而不必担心。

我已经在我的主应用程序和它使用的一些库中实现了。在这一点上它工作得很好。还需要一件事是您必须创建 pch 源文件和头文件,并在源文件中包含您想要预编译的所有头文件。我在 MFC 上做了 12 年,但我花了几分钟才想起那件事。

于 2009-04-22T16:03:42.770 回答
0

最干净的方法是将预编译选项添加为全局选项。在 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 文件,我不介意使用什么文件名,所以我没有努力指定它。

于 2015-01-07T08:33:13.220 回答
0

由于预编译头选项不适用于 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 这样的头文件。

于 2016-09-08T09:42:35.447 回答
-15

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

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

于 2008-10-07T21:23:58.233 回答