簡體   English   中英

在 CMake 中使用預編譯的頭文件

[英]Using pre-compiled headers with CMake

我在網上看到了一些(舊的)帖子,關於在 CMake 中對預編譯頭的一些支持進行黑客攻擊。 他們似乎都有點無處不在,每個人都有自己的做法。 目前最好的方法是什么?

有一個名為“Cotire”的第三方 CMake 模塊,它可以為基於 CMake 的構建系統自動使用預編譯頭文件,並且還支持統一構建。

我使用以下宏來生成和使用預編譯頭:

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 平台上仍然可以正常運行。 漂亮整齊 :)

CMake 剛剛獲得了對 PCH(預編譯頭)的支持,它應該會在即將到來的 3.16 版本中可用,由於 2019-10-01:

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/提供了一些額外的上下文(動機、數字)

這是一個代碼片段,允許您為您的項目使用預編譯頭。 將以下內容添加到 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)

我最終使用了 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)

如果您不想重新發明輪子,只需使用 Cotire 作為最佳答案建議或更簡單的一個 - cmake-precompiled-header here 要使用它,只需包含模塊並調用:

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

我在網上看到一些(舊的)帖子,內容涉及一起破解CMake中對預編譯標頭的一些支持。 他們似乎遍地都是,每個人都有自己的做事方式。 目前最好的方法是什么?

使用 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"
)

恕我直言,最好的方法是為整個項目設置 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)

這種方法已經過測試並且運行良好。

最干凈的方法是將預編譯選項添加為全局選項。 在 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 這樣的頭文件。

好吧,當您每次更改任何項目文件中的一行時,在四核機器上構建需要 10 分鍾以上時,它會告訴您是時候為 Windows 添加預編譯頭文件了。 在 *nux 上,我只會使用 ccache 而不必擔心。

我已經在我的主應用程序和它使用的一些庫中實現了。 在這一點上它工作得很好。 還需要做的一件事是您必須創建 pch 源文件和頭文件,並在源文件中包含所有要預編譯的頭文件。 我用 MFC 這樣做了 12 年,但我花了幾分鍾才回憶起來。

甚至不要去那里。 預編譯頭意味着只要其中一個頭發生變化,您就必須重建所有內容 如果你有一個實現這一點的構建系統,你就很幸運了。 通常情況下,您的構建會失敗,直到您意識到您更改了正在預編譯的內容,因此您需要進行完整的重建。 您可以通過預編譯您絕對肯定不會改變的標頭來避免這種情況,但是您也放棄了大部分速度增益。

另一個問題是,在許多使用預編譯頭文件的地方,您的命名空間被各種您不知道或不關心的符號所污染。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM