Sử dụng tiêu đề được biên dịch trước với CMake


104

Tôi đã thấy một vài bài đăng (cũ) trên 'net về việc hack cùng một số hỗ trợ cho các tiêu đề được biên dịch trước trong CMake. Tất cả đều có vẻ hơi giống nhau và mỗi người đều có cách làm việc của riêng mình. Cách làm tốt nhất hiện nay là gì?

Câu trả lời:


79

Có một mô-đun CMake của bên thứ ba có tên 'Cotire' tự động hóa việc sử dụng các tiêu đề được biên dịch trước cho các hệ thống xây dựng dựa trên CMake và cũng hỗ trợ các bản dựng thống nhất.


1
Tôi đã tạo một tập hợp các macro bao hàm chức năng cotire (tiêu đề được biên dịch trước và các bản dựng thống nhất) tại đây để dễ sử dụng hơn
onqtam 29/02/16

2
@onqtam thế nào là dễ dàng hơn, nó rất khổng lồ mà tôi thậm chí không thấy làm thế nào để đơn giản được biên dịch sẵn làm việc với cmake và gcc
Pavel P

5
CMake 3.16 đã giới thiệu hỗ trợ tích hợp cho các tiêu đề được biên dịch trước. Xem câu trả lời của tôi stackoverflow.com/a/59514029/2799037 Không cần mô-đun của bên thứ ba nữa!
usr1234567 28/12/19

34

Tôi đang sử dụng macro sau để tạo và sử dụng các tiêu đề được biên dịch trước:

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)

Giả sử bạn có một biến $ {MySources} với tất cả các tệp nguồn của mình, mã bạn muốn sử dụng sẽ đơn giản là

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

Mã sẽ vẫn hoạt động tốt trên các nền tảng không phải MSVC. Khá gọn gàng :)


2
Macro này có 1 lỗ hổng. Nếu trình tạo không dựa trên MSVC, nguồn được biên dịch trước sẽ không được thêm vào danh sách nguồn. Vì vậy, sửa đổi của tôi chỉ đơn giản là di chuyển ra list( APPEND ... )bên ngoài đóng cửa endif(). Xem mã hoàn chỉnh ở đây: pastebin.com/84dm5rXZ
void.pointer

1
@RobertDailey: Điều này thực sự là có chủ ý - tôi không muốn biên dịch tệp nguồn được biên dịch trước khi không sử dụng các tiêu đề được biên dịch trước - dù sao thì nó cũng không nên xác định bất kỳ ký hiệu nào.
larsmoa,

1
@Iarsam Vui lòng sửa các đối số /Yu/FIđối số, chúng nên ${PrecompiledHeader}và không ${PrecompiledBinary}.
Mourad

bạn có thể giải thích tại sao chúng ta cần cờ "/ Fp" và "/ FI" không? Theo msdn.microsoft.com/en-us/library/z0atkd6c.aspx, việc sử dụng "/ Fp" là không bắt buộc. Tuy nhiên, nếu tôi cắt các cờ đó khỏi macro của bạn thì không có pch nào được đặt.
Vram Vardanian

2
Cần biết rằng lập luận đối với / Yu được hiểu theo nghĩa đen. Ví dụ: /YuC:/foo/bar.hsẽ buộc bạn phải vượt qua /FpC:/foo/bar.hcờ hoặc đặt #include <C:/foo/bar.h>ở đầu tất cả các tệp .cpp của bạn làm câu lệnh bao gồm đầu tiên. MSVC thực hiện một so sánh chuỗi của các #includeđối số, nó không kiểm tra xem nó có trỏ đến cùng một tệp như những gì đã được cấp cho hay không /Yu. Ergo, #include <bar.h>sẽ không hoạt động và phát ra lỗi C2857.
Manuzor

21

CMake vừa nhận được hỗ trợ cho PCH, nó sẽ có sẵn trong bản phát hành 3.16 sắp tới, do 2019-10-01:

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

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

Đang có cuộc thảo luận về việc hỗ trợ chia sẻ PCH giữa các mục tiêu: https://gitlab.kitware.com/cmake/cmake/issues/19659

Có một số ngữ cảnh bổ sung (động lực, con số) có sẵn tại https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/


2
Đây là liên kết đến tài liệu CMake chính thức: cmake.org/cmake/help/latest/command/…
Alex Che

2
Có một bài đăng từ nhóm MSVC về việc tìm ra tiêu đề nào cần đưa vào PCH: devblogs.microsoft.com/cppblog/…
janisozaur

19

Đây là đoạn mã để cho phép bạn sử dụng tiêu đề được biên dịch trước cho dự án của mình. Thêm phần sau vào CMakeLists.txt của bạn thay thế myprecompiledheadersmyproject_SOURCE_FILESnếu thích hợp:

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)

Cảm ơn; Tôi sẽ sử dụng cái này làm hướng dẫn để xây dựng một cái cho GCC (nếu tôi có thể). Tôi sẽ đăng câu trả lời của mình ngay sau khi tôi hoàn thành. =]
strager

@Jayen, Không; Cuối cùng tôi đã bỏ dự án và về cơ bản không bao giờ gặp phải sự phức tạp của C ++ nữa.
strager

Có thể đặt PCH cho toàn bộ dự án không? Vì không thể lấy danh sách các tệp được tạo tự động trong cmake with set( CMAKE_AUTOMOC ON ).
Dmitry Sazonov

Tôi đã sử dụng giải pháp của bạn, nhưng không may là thời gian biên dịch từ 2'10 "đến 2'40", cho ~ 130 tệp. Tôi có phải đảm bảo rằng nó myprecompiledheader.cppđược biên dịch trước không? Từ đoạn mã này, có vẻ như đoạn mã sẽ được biên dịch cuối cùng, vì vậy có lẽ đó là điều có thể gây ra sự chậm trễ. myprecompiledheader.hchỉ chứa các tiêu đề STL phổ biến nhất mà mã của tôi sử dụng.
Grim Fandango

13

Tôi đã sử dụng một phiên bản macro larsm đã được điều chỉnh. Sử dụng $ (IntDir) cho đường dẫn pch giữ các tiêu đề được biên dịch trước để gỡ lỗi và phát hành các bản dựng riêng biệt.

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

2
Phần trừu tượng của $ {IntDir} rất hữu ích. Cảm ơn.
Gopalakrishna Palem

12

Được điều chỉnh từ Dave, nhưng hiệu quả hơn (đặt thuộc tính mục tiêu, không phải cho từng tệp):

if (MSVC)
   set_target_properties(abc PROPERTIES COMPILE_FLAGS "/Yustd.h")
   set_source_files_properties(std.cpp PROPERTIES COMPILE_FLAGS "/Ycstd.h")
endif(MSVC)

2
Tôi đã từng sử dụng giải pháp này, nhưng nó chỉ hoạt động nếu dự án chỉ chứa các tệp c ++. Vì COMPILE_FLAGS được áp dụng cho tất cả các tệp nguồn, nó cũng sẽ được áp dụng cho các tệp c (ví dụ: các tệp được tạo bởi MIDL), điều này sẽ không giống như c ++ PCH. Khi sử dụng giải pháp của Dave, bạn có thể sử dụng get_source_file_property (_language $ {src_file} LANGUAGE) và chỉ đặt cờ trình biên dịch nếu đó thực sự là một tệp CXX.
Andreas Haferburg,

Rất vui khi có sự linh hoạt của giải pháp khác trong túi sau của tôi, nhưng đây là giải pháp tôi đang tìm kiếm, cảm ơn!
kylewm

Câu trả lời rất hay. Cẩn thận với dấu ngoặc đơn bị thiếu cho set_source_files_properties.
Arnaud

2
Nó có thể được tắt chọn lọc cho các tệp riêng lẻ có / Y- bằng cách sử dụng set_source_files_properties
mlt

Trong abcví dụ của bạn là gì?
Sandburg

7

nếu bạn không muốn phát minh lại bánh xe, chỉ cần sử dụng Cotire như câu trả lời hàng đầu gợi ý hoặc một câu trả lời đơn giản hơn - cmake-precompiled-header ở đây . Để sử dụng nó, chỉ cần bao gồm mô-đun và gọi:

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

5

CMake 3.16 đã giới thiệu hỗ trợ cho các tiêu đề được biên dịch trước. Có một lệnh CMake mới target_precompile_headersthực hiện mọi thứ bạn cần. Xem tài liệu của nó để biết thêm chi tiết: https://cmake.org/cmake/help/latest/command/target_precompile_headers.html


2
Câu trả lời này không bổ sung gì mới cho câu trả lời của janisozaur, được đăng nửa năm trước đó, ngoại trừ liên kết đến tài liệu chính thức cuối cùng, có lẽ tốt hơn nên được thêm vào dưới dạng nhận xét cho câu trả lời đó.
Alex Che

4

Ví dụ về cách sử dụng tiêu đề được biên dịch trước với cmake và Visual Studio 2015

"stdafx.h", "stdafx.cpp" - tên tiêu đề được biên dịch trước.

Đặt phần sau vào tệp cmake gốc.

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

Đặt phần sau vào tệp cmake của dự án.

"src" - một thư mục chứa các tệp nguồn.

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

3

IMHO cách tốt nhất là đặt PCH cho toàn bộ dự án, như martjno đã đề xuất, kết hợp với khả năng bỏ qua PCH cho một số nguồn nếu cần (ví dụ: nguồn được tạo):

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

Vì vậy, nếu bạn có một số MY_TARGET mục tiêu và danh sách các nguồn được tạo IGNORE_PCH_SRC_LIST, bạn sẽ chỉ cần thực hiện:

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

Aproach này đã được thử nghiệm và hoạt động hoàn hảo.


0

Vâng, khi quá trình xây dựng mất hơn 10 phút trên máy lõi tứ mỗi khi bạn thay đổi một dòng trong bất kỳ tệp dự án nào, nó sẽ cho bạn biết thời gian để thêm các tiêu đề được biên dịch trước cho cửa sổ. Trên * nux, tôi sẽ chỉ sử dụng ccache và không lo lắng về điều đó.

Tôi đã triển khai ứng dụng chính của mình và một vài thư viện mà nó sử dụng. Nó hoạt động tuyệt vời cho đến thời điểm này. Một điều cũng cần thiết là bạn phải tạo tệp tiêu đề và mã nguồn pch và trong tệp nguồn bao gồm tất cả các tiêu đề mà bạn muốn được biên dịch trước. Tôi đã làm điều này trong 12 năm với MFC nhưng tôi phải mất vài phút để nhớ lại điều đó ..


0

Cách rõ ràng nhất là thêm tùy chọn được biên dịch trước làm tùy chọn chung. Trong tệp vcxproj, điều này sẽ hiển thị như <PrecompiledHeader>Use</PrecompiledHeader>và không thực hiện việc này đối với mọi tệp riêng lẻ.

Sau đó, bạn cần thêm Createtùy chọn vào StdAfx.cpp. Sau đây là cách tôi sử dụng nó:

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

Điều này được thử nghiệm và hoạt động cho MSVC 2010 và sẽ tạo tệp MyDll.pch, tôi không bận tâm về tên tệp được sử dụng nên tôi không cố gắng chỉ định nó.


0

Vì tùy chọn tiêu đề được biên dịch trước không hoạt động cho các tệp rc, tôi cần điều chỉnh macro do jari cung cấp.

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

Chỉnh sửa: Việc sử dụng tiêu đề được biên dịch sẵn này đã giảm thời gian xây dựng Tổng thể của Dự án chính của tôi từ 4 phút 30 giây xuống 1 phút 40 giây. Đây là một điều thực sự tốt đối với tôi. Trong tiêu đề biên dịch trước chỉ có các tiêu đề như boost / stl / Windows / mfc.


-15

Đừng đến đó. Các tiêu đề được biên dịch sẵn có nghĩa là bất cứ khi nào một trong các tiêu đề thay đổi, bạn phải xây dựng lại mọi thứ . Bạn thật may mắn nếu bạn có một hệ thống xây dựng nhận ra điều này. Thường xuyên hơn không bao giờ, bản dựng của bạn sẽ chỉ thất bại cho đến khi bạn nhận ra rằng bạn đã thay đổi thứ gì đó đang được biên dịch trước, và do đó bạn cần phải xây dựng lại toàn bộ. Bạn có thể tránh điều này chủ yếu bằng cách biên dịch trước các tiêu đề mà bạn hoàn toàn tích cực sẽ không thay đổi, nhưng sau đó bạn cũng đang từ bỏ một phần lớn tốc độ tăng.

Vấn đề khác là không gian tên của bạn bị ô nhiễm với tất cả các loại ký hiệu mà bạn không biết hoặc không quan tâm ở nhiều nơi mà bạn đang sử dụng các tiêu đề được biên dịch sẵn.


29
Các tiêu đề được biên dịch trước hữu ích nhất khi chúng tham chiếu đến các tiêu đề không thay đổi ... STL, Boost, các thứ khác của bên thứ ba. Nếu bạn đang sử dụng PCH cho các tệp tiêu đề dự án của riêng mình, bạn đang lãng phí phần lớn lợi ích.
Tom

3
Ngay cả khi bạn đang sử dụng PCH cho tiêu đề dự án của riêng mình, thì toàn bộ điểm của một hệ thống xây dựng như CMake là đảm bảo rằng các yếu tố phụ thuộc được tôn trọng. Nếu tôi thay đổi tệp .h của mình (hoặc một trong các tệp phụ thuộc của nó), tôi muốn tạo lại tệp .pch. Nếu tôi không thay đổi tệp .h của mình, tôi không muốn tạo lại .pch. Tôi nghĩ toàn bộ câu hỏi của OP là: Làm thế nào để điều này xảy ra, bằng cách sử dụng CMake?
Quuxplusone

1
Các tiêu đề được biên dịch sẵn là công cụ tốt nhất để giảm thời gian biên dịch cho đến khi các mô-đun C ++ được hỗ trợ bởi tất cả các trình biên dịch chính thống. Chúng giải quyết một vấn đề trở nên tồi tệ hơn với việc sử dụng ngày càng nhiều các mẫu và thư viện chỉ dành cho tiêu đề. Khi được sử dụng đúng cách, không có gì thay thế được. Bất kể điều này không phải là một câu trả lời cho câu hỏi được đặt ra, và chỉ đơn thuần là đưa ra ý kiến. Bỏ phiếu xuống và xóa bỏ phiếu bầu.
IInspectable
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.