Menggunakan header yang telah dikompilasi sebelumnya dengan CMake

104

Saya telah melihat beberapa posting (lama) di 'net tentang meretas bersama beberapa dukungan untuk header yang telah dikompilasi sebelumnya di CMake. Mereka semua tampak agak di mana-mana dan setiap orang memiliki cara mereka sendiri untuk melakukannya. Apa cara terbaik untuk melakukannya saat ini?

Lengket
sumber

Jawaban:

79

Ada modul CMake pihak ketiga bernama 'Cotire' yang mengotomatiskan penggunaan header yang telah dikompilasi untuk sistem build berbasis CMake dan juga mendukung build unity.

sakra
sumber
1
Saya telah membuat satu set makro yang membungkus fungsionalitas cotire (header yang telah dikompilasi dan membangun kesatuan) di sini untuk penggunaan yang lebih mudah
onqtam
2
@onqtam bagaimana itu lebih mudah, itu sangat besar sehingga saya bahkan tidak melihat bagaimana cara mendapatkan prakompilasi bekerja dengan cmake dan gcc
Pavel P
5
CMake 3.16 memperkenalkan dukungan bawaan untuk header yang telah dikompilasi sebelumnya. Lihat jawaban saya stackoverflow.com/a/59514029/2799037 Tidak perlu lagi modul pihak ketiga!
usr1234567
34

Saya menggunakan makro berikut untuk menghasilkan dan menggunakan header yang telah dikompilasi sebelumnya:

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)

Katakanlah Anda memiliki variabel $ {MySources} dengan semua file sumber Anda, kode yang ingin Anda gunakan hanyalah

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

Kode tersebut masih akan berfungsi dengan baik pada platform non-MSVC juga. Cukup rapi :)

larsmoa.dll
sumber
2
Makro ini memiliki 1 cacat. Jika generator tidak berbasis MSVC, sumber yang telah dikompilasi sebelumnya tidak akan ditambahkan ke daftar sumber. Jadi modifikasi saya hanya bergerak di list( APPEND ... )luar penutupan endif(). Lihat kode lengkap di sini: pastebin.com/84dm5rXZ
void.pointer
1
@RobertDailey: Ini sebenarnya disengaja - Saya tidak ingin mengkompilasi file sumber yang telah dikompilasi sebelumnya ketika tidak menggunakan header yang telah dikompilasi - itu tidak boleh mendefinisikan simbol apa pun.
larsmoa
1
@Iarsam Harap perbaiki argumen /Yudan /FI, mereka harus ${PrecompiledHeader}dan tidak ${PrecompiledBinary}.
Mourad
dapatkah Anda menjelaskan mengapa kita membutuhkan flag "/ Fp" dan "/ FI"? Menurut msdn.microsoft.com/en-us/library/z0atkd6c.aspx penggunaan "/ Fp" tidak wajib. Namun, jika saya memotong bendera tersebut dari makro Anda, tidak ada pch yang disetel.
Vram Vardanian
2
Sadarilah bahwa argumen untuk / Yu dipahami secara harfiah. Misalnya /YuC:/foo/bar.hakan memaksa Anda untuk mengoper /FpC:/foo/bar.hbendera atau meletakkan #include <C:/foo/bar.h>di bagian atas semua file .cpp Anda sebagai pernyataan include pertama. MSVC melakukan perbandingan string dari #includeargumen, tidak memeriksa apakah itu menunjuk ke file yang sama seperti yang diberikan /Yu. Ergo, #include <bar.h>tidak akan berfungsi dan mengeluarkan error C2857.
Manuzor
21

CMake baru saja mendapatkan dukungan untuk PCH, itu akan tersedia dalam rilis 3.16 mendatang, karena 2019-10-01:

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

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

Ada diskusi yang sedang berlangsung untuk mendukung berbagi PCH antar target: https://gitlab.kitware.com/cmake/cmake/issues/19659

Ada beberapa konteks tambahan (motivasi, angka) yang tersedia di https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/

janisozaur.dll
sumber
2
Berikut ini tautan ke dokumentasi resmi CMake: cmake.org/cmake/help/latest/command/…
Alex Che
2
Ada postingan dari tim MSVC tentang mencari tahu header mana yang akan disertakan dalam PCH: devblogs.microsoft.com/cppblog/…
janisozaur
19

Berikut adalah potongan kode untuk memungkinkan Anda menggunakan header yang telah dikompilasi untuk proyek Anda. Tambahkan berikut ini ke CMakeLists.txt Anda yang menggantikan myprecompiledheadersdan yang myproject_SOURCE_FILESsesuai:

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)
Dave Hillier
sumber
Terima kasih; Saya akan menggunakan ini sebagai panduan untuk membuat satu untuk GCC (jika saya bisa). Saya akan memposting jawaban saya segera setelah saya selesai. =]
strager
@Jayen, Tidak; Saya akhirnya membatalkan proyek dan tidak pernah mengalami kerepotan C ++ lagi, pada dasarnya.
strager
Apakah mungkin untuk mengatur PCH ke seluruh proyek? Karena tidak mungkin mendapatkan daftar file yang dibuat secara otomatis di cmake with set( CMAKE_AUTOMOC ON ).
Dmitry Sazonov
Saya telah menggunakan solusi Anda, tapi sayangnya waktu kompilasi dari 2'10 "menjadi 2'40", untuk ~ 130 file. Apakah saya harus memastikan bahwa myprecompiledheader.cppdikompilasi terlebih dahulu? Dari cuplikan ini, sepertinya cuplikan tersebut akan dikompilasi terakhir kali, jadi mungkin itulah yang mungkin menyebabkan keterlambatan. myprecompiledheader.hhanya berisi header STL paling umum yang digunakan kode saya.
Grim Fandango
13

Saya akhirnya menggunakan versi makro larsm yang diadaptasi. Menggunakan $ (IntDir) untuk jalur pch membuat header yang telah dikompilasi untuk debug dan rilis rilis tetap terpisah.

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})
jari
sumber
2
Abstraksi $ {IntDir} sangat membantu. Terima kasih.
Gopalakrishna Palem
12

Diadaptasi dari Dave, tetapi lebih efisien (menetapkan properti target, bukan untuk setiap file):

if (MSVC)
   set_target_properties(abc PROPERTIES COMPILE_FLAGS "/Yustd.h")
   set_source_files_properties(std.cpp PROPERTIES COMPILE_FLAGS "/Ycstd.h")
endif(MSVC)
martjno
sumber
2
Saya dulu menggunakan solusi ini, tetapi hanya berfungsi jika proyek hanya berisi file c ++. Karena COMPILE_FLAGS diterapkan ke semua file sumber, itu juga akan diterapkan ke file c (misalnya yang dihasilkan oleh MIDL), yang tidak akan menyukai c ++ PCH. Saat menggunakan solusi Dave, Anda bisa menggunakan get_source_file_property (_language $ {src_file} LANGUAGE), dan hanya menyetel tanda compiler jika itu benar-benar file CXX.
Andreas Haferburg
Senang memiliki fleksibilitas solusi lain di saku belakang saya, tetapi inilah yang saya cari, terima kasih!
kylewm
Jawaban bagus. Waspadai tanda kurung yang hilang untuk set_source_files_properties.
Arnaud
2
Hal ini dapat selektif dimatikan untuk file individu dengan / Y menggunakan set_source_files_properties
MLT
Apa abccontoh Anda?
Sandburg
7

jika Anda tidak ingin menemukan kembali roda, cukup gunakan Cotire sebagai jawaban atas menyarankan atau yang lebih sederhana - cmake-precompiled-header di sini . Untuk menggunakannya cukup sertakan modul dan panggil:

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

CMake 3.16 memperkenalkan dukungan untuk header yang telah dikompilasi sebelumnya. Ada perintah CMake baru target_precompile_headersyang melakukan semua yang Anda butuhkan di bawah tenda. Lihat dokumentasinya untuk detail lebih lanjut: https://cmake.org/cmake/help/latest/command/target_precompile_headers.html

usr1234567
sumber
2
Jawaban ini tidak menambah hal baru pada jawaban janisozaur, diposting setengah tahun sebelumnya, kecuali link ke dokumentasi resmi terakhir, yang mungkin sebaiknya ditambahkan sebagai komentar untuk jawaban tersebut.
Alex Che
4

Contoh penggunaan header dikompilasi dengan cmake dan Visual Studio 2015

"stdafx.h", "stdafx.cpp" - nama header yang telah dikompilasi sebelumnya.

Letakkan yang berikut di bawah ini di file root 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()

Letakkan yang berikut di bawah ini di file cmake proyek.

"src" - folder dengan file sumber.

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

IMHO cara terbaik adalah mengatur PCH untuk keseluruhan proyek, seperti yang disarankan martjno, dikombinasikan dengan kemampuan mengabaikan PCH untuk beberapa sumber jika diperlukan (misalnya sumber yang dihasilkan):

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

Jadi, jika Anda memiliki beberapa target MY_TARGET, dan daftar sumber yang dihasilkan IGNORE_PCH_SRC_LIST Anda cukup melakukan:

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

Pendekatan ini diuji dan bekerja dengan sempurna.

Vram Vardanian
sumber
0

Nah, ketika build membutuhkan waktu 10+ menit pada mesin quad core setiap kali Anda mengubah satu baris di salah satu file proyek, ia memberi tahu Anda waktunya untuk menambahkan header yang telah dikompilasi untuk windows. Di * nux saya hanya akan menggunakan ccache dan tidak khawatir tentang itu.

Saya telah menerapkannya di aplikasi utama saya dan beberapa pustaka yang digunakannya. Ini bekerja dengan baik untuk saat ini. Satu hal yang juga diperlukan adalah Anda harus membuat file sumber dan header pch dan di file sumber menyertakan semua header yang ingin Anda prakompilasi. Saya melakukan ini selama 12 tahun dengan MFC tetapi butuh beberapa menit untuk mengingatnya ..


sumber
0

Cara terbersih adalah dengan menambahkan opsi prakompilasi sebagai opsi global. Dalam file vcxproj ini akan muncul sebagai <PrecompiledHeader>Use</PrecompiledHeader>dan tidak melakukan ini untuk setiap file individu.

Kemudian Anda perlu menambahkan Createopsi ke StdAfx.cpp. Berikut cara saya menggunakannya:

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

Ini diuji dan berfungsi untuk MSVC 2010 dan akan membuat file MyDll.pch, saya tidak peduli nama file apa yang digunakan jadi saya tidak berusaha untuk menentukannya.

uncletall
sumber
0

Karena opsi header yang telah dikompilasi tidak berfungsi untuk file rc, saya perlu menyesuaikan makro yang disediakan oleh 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)

Sunting: Penggunaan tajuk yang telah dikompilasi ini mengurangi waktu pembuatan Keseluruhan Proyek utama saya dari 4 menit 30-an menjadi 1 menit 40-an. Bagi saya ini hal yang sangat bagus. Dalam header precompile hanya ada header seperti boost / stl / Windows / mfc.

schorsch_76
sumber
-15

Jangan pergi kesana. Header yang telah dikompilasi berarti bahwa setiap kali salah satu header berubah, Anda harus membangun kembali semuanya . Anda beruntung jika memiliki sistem build yang menyadari hal ini. Lebih sering daripada tidak sama sekali, build Anda hanya akan gagal sampai Anda menyadari bahwa Anda mengubah sesuatu yang sedang dikompilasi sebelumnya, dan oleh karena itu Anda perlu melakukan pembangunan kembali sepenuhnya. Anda dapat menghindari hal ini sebagian besar dengan mengkompilasi header yang Anda benar-benar positif tidak akan berubah, tetapi kemudian Anda juga melepaskan sebagian besar dari penambahan kecepatan.

Masalah lainnya adalah ruang nama Anda tercemar dengan semua jenis simbol yang tidak Anda ketahui atau pedulikan di banyak tempat di mana Anda akan menggunakan header yang telah dikompilasi sebelumnya.

Dirk Groeneveld
sumber
29
Header prakompilasi paling berguna saat mereferensikan header yang tidak berubah ... STL, Boost, hal-hal pihak ketiga lainnya. Jika Anda menggunakan PCH untuk file header proyek Anda sendiri, Anda akan menyia-nyiakan sebagian besar manfaatnya.
Tom
3
Bahkan jika Anda menggunakan PCH untuk header proyek Anda sendiri, seluruh titik membangun sistem seperti CMake adalah untuk memastikan bahwa dependensi yang dihormati. Jika saya mengubah file .h saya (atau salah satu dependensinya), saya ingin agar .pch dibuat ulang. Jika saya tidak mengubah file .h saya, saya tidak ingin .pch dibuat ulang. Saya pikir seluruh pertanyaan OP adalah: Bagaimana cara mewujudkannya, menggunakan CMake?
Quuxplusone
1
Header prakompilasi adalah alat terbaik untuk mengurangi waktu kompilasi hingga modul C ++ didukung oleh semua kompiler utama. Mereka memecahkan masalah yang semakin parah dengan penggunaan templat dan pustaka khusus header yang terus meningkat. Jika digunakan dengan benar, tidak ada penggantinya. Terlepas dari itu, ini bukan merupakan jawaban atas pertanyaan yang diajukan, dan hanya menyuarakan pendapat. Pilih bawah dan hapus.
IInspectable