CMake ile önceden derlenmiş başlıkları kullanma


104

CMake'de önceden derlenmiş başlıklar için bazı desteklerin bir araya getirilmesiyle ilgili ağda birkaç (eski) gönderi gördüm. Hepsi her yerde görünüyor ve herkesin kendi yöntemi var. Şu anda bunu yapmanın en iyi yolu nedir?

Yanıtlar:


79

Bir yoktur 'Cotire' adlı üçüncü parti CKağıt modülü CKağıt inşa tabanlı sistemler için önceden derlenmiş başlıkları kullanımını otomatik hale getirir ve aynı zamanda birlik kurar destekler.


1
Daha kolay kullanım için burada bütünlük işlevselliğini (önceden derlenmiş başlıklar ve birlik yapıları) saran bir dizi makro oluşturdum
onqtam

2
@onqtam bu kadar kolay, o kadar büyük ki cmake ve gcc ile nasıl önceden derlenebileceğimi bile bilmiyorum
Pavel P

5
CMake 3.16, önceden derlenmiş başlıklar için yerleşik destek getirdi. Cevabımı görün stackoverflow.com/a/59514029/2799037 Artık üçüncü parti modüllere gerek yok!
usr1234567

34

Önceden derlenmiş üstbilgiler oluşturmak ve kullanmak için aşağıdaki makroyu kullanıyorum:

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)

Diyelim ki tüm kaynak dosyalarınızla bir değişken $ {MySources} var, kullanmak isteyeceğiniz kod basitçe

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

Kod, MSVC olmayan platformlarda da gayet iyi çalışacaktır. Oldukça temiz :)


2
Bu makronun 1 kusuru var. Oluşturucu MSVC tabanlı değilse, önceden derlenmiş kaynak, kaynaklar listesine eklenmeyecektir. Yani benim modifikasyonum list( APPEND ... )kapanışın dışına taşıyor endif(). Kodun tamamını burada görün: pastebin.com/84dm5rXZ
void.pointer

1
@RobertDailey: Bu aslında kasıtlı - önceden derlenmiş başlıkları kullanmadığımda önceden derlenmiş kaynak dosyasını derlemek istemiyorum - zaten herhangi bir sembol tanımlamamalı.
larsmoa

1
@Iarsam Lütfen /Yuve /FIargümanları düzeltin, olmalı ${PrecompiledHeader}ve olmamalı ${PrecompiledBinary}.
Mourad

"/ Fp" ve "/ FI" işaretlerine neden ihtiyacımız olduğunu açıklayabilir misiniz? Msdn.microsoft.com/en-us/library/z0atkd6c.aspx'e göre "/ Fp" kullanımı zorunlu değildir. Ancak, bu bayrakları makronuzdan kesersem pch ayarlanmadı.
Vram Vardanian

2
Yu ile ilgili argümanın tam anlamıyla alındığının farkında olun. Örneğin /YuC:/foo/bar.h, sizi ya /FpC:/foo/bar.hbayrağı geçirmeye ya #include <C:/foo/bar.h>da ilk include ifadesi olarak tüm .cpp dosyalarınızın en üstüne koymaya zorlar . MSVC, #includeargümanlar arasında bir dize karşılaştırması yapar, verilenle aynı dosyaya işaret edip etmediğini kontrol etmez /Yu. Ergo, #include <bar.h>çalışmayacak ve C2857 hatası vermeyecek.
Manuzor

21

CMake, PCH'ler için yeni destek kazandı, 2019-10-01 tarihinde çıkacak olan 3.16 sürümünde mevcut olacaktır:

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

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

PCH'lerin hedefler arasında paylaşılmasının desteklenmesiyle ilgili devam eden tartışma var: 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/ adresinde bazı ek içerik (motivasyon, sayılar) mevcuttur.


2
Resmi CMake belgelerine bağlantı: cmake.org/cmake/help/latest/command/…
Alex Che

2
MSVC ekibinden, PCH'ye
dair bir gönderi var

19

İşte projeniz için önceden derlenmiş başlık kullanmanıza izin verecek bir kod parçacığı. CMakeLists.txt dosyanızı değiştirerek myprecompiledheadersve myproject_SOURCE_FILESuygun şekilde aşağıdakileri ekleyin :

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)

Teşekkürler; Bunu GCC için bir tane oluşturmak için bir rehber olarak kullanacağım (eğer yapabilirsem). Cevabımı bitirir bitirmez göndereceğim. =]
58'de strager

@Jayen, Hayır; Sonunda projeyi bıraktım ve temelde bir daha asla C ++ 'ın zorluklarına girmedim.
strager

PCH'yi tüm projeye ayarlamak mümkün mü? Çünkü cmake'de otomatik olarak oluşturulmuş dosyaların listesini almak mümkün değildir with set( CMAKE_AUTOMOC ON ).
Dmitry Sazonov

Çözümünüzü kullandım, ancak maalesef derleme süresi ~ 130 dosya için 2'10 "dan 2'40" a çıktı. myprecompiledheader.cppÖnce bunun derlendiğinden emin olmalı mıyım? Bu pasajdan en son derlenmiş gibi görünüyor, bu yüzden gecikmeye neden olan belki de bu olabilir. myprecompiledheader.hyalnızca kodumun kullandığı en yaygın STL başlıklarını içerir.
Grim Fandango

13

Larsm makrosunun uyarlanmış bir sürümünü kullandım. $ (IntDir) pch yolu için kullanmak, hata ayıklama ve sürüm yapıları için önceden derlenmiş üstbilgileri ayrı tutar.

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
$ {IntDir} soyutlaması faydalıdır. Teşekkürler.
Gopalakrishna Palem

12

Dave'den uyarlanmıştır, ancak daha etkilidir (her dosya için değil, hedef özellikleri belirler):

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
Bu çözümü kullanırdım, ancak yalnızca proje yalnızca c ++ dosyaları içeriyorsa çalışır. COMPILE_FLAGS tüm kaynak dosyalara uygulandığından, c ++ PCH'den hoşlanmayan c dosyalarına (örneğin MIDL tarafından oluşturulanlar) da uygulanacaktır. Dave'in çözümünü kullanırken, get_source_file_property (_language $ {src_file} LANGUAGE) kullanabilir ve derleyici bayraklarını yalnızca gerçekten bir CXX dosyası ise ayarlayabilirsiniz.
Andreas Haferburg

Diğer çözümün esnekliğinin arka cebimde olması güzel, ama aradığım bu, teşekkürler!
kylewm

Güzel cevap. Set_source_files_properties için eksik parantezlere dikkat edin.
Arnaud


Örneğinizde ne var abc?
Sandburg

7

Tekerleği yeniden icat etmek istemiyorsanız, ya en iyi yanıtın önerdiği gibi Cotire'ı ya da daha basit olanı kullanın - cmake-precompiled-header burada . Kullanmak için sadece modülü ekleyin ve arayın:

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

5

CMake 3.16, önceden derlenmiş başlıklar için destek getirdi. target_precompile_headersKaputun altında ihtiyacınız olan her şeyi yapan yeni bir CMake komutu var. Daha fazla ayrıntı için belgelerine bakın: https://cmake.org/cmake/help/latest/command/target_precompile_headers.html


2
Bu cevap, yarım yıl önce yayınlanan janisozaur'un cevabına, muhtemelen bu cevaba bir yorum olarak eklenmesi gereken nihai resmi belgelere bağlantı dışında yeni bir şey eklemiyor .
Alex Che

4

Cmake ve Visual Studio 2015 ile önceden derlenmiş üstbilgi kullanımına bir örnek

"stdafx.h", "stdafx.cpp" - önceden derlenmiş başlık adı.

Aşağıdakileri kök cmake dosyasına koyun.

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

Aşağıdakileri proje cmake dosyasına koyun.

"src" - kaynak dosyaları içeren bir klasör.

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

3

IMHO'nun en iyi yolu, martjno'nun önerdiği gibi, gerekirse bazı kaynaklar için PCH'yi görmezden gelme yeteneği ile birlikte tüm proje için PCH'yi ayarlamaktır (örneğin oluşturulan kaynaklar):

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

Dolayısıyla, MY_TARGET hedefiniz ve oluşturulan IGNORE_PCH_SRC_LIST kaynakların listesi varsa, yapmanız gereken:

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

Bu yaklaşım test edildi ve mükemmel çalışıyor.


0

Derlemeler dört çekirdekli bir makinede 10 dakikadan fazla sürdüğünde, herhangi bir proje dosyasında tek bir satırı her değiştirdiğinizde, size pencereler için önceden derlenmiş başlıklar ekleme zamanının geldiğini söyler. * Nux'ta sadece ccache kullanırdım ve bu konuda endişelenmezdim.

Ana uygulamamda ve kullandığı birkaç kitaplıkta uyguladım. Bu noktada harika çalışıyor. Gerekli olan bir şey de, pch kaynak ve başlık dosyasını oluşturmanız ve kaynak dosyada önceden derlenmesini istediğiniz tüm başlıkları içermenizdir. Bunu 12 yıl boyunca MFC ile yaptım ama hatırlamam birkaç dakikamı aldı ..


0

En temiz yol, önceden derlenmiş seçeneği genel bir seçenek olarak eklemektir. Vcxproj dosyasında bu, her dosya için bu şekilde görünecek <PrecompiledHeader>Use</PrecompiledHeader>ve yapmayacaktır.

O zaman Createseçeneği StdAfx.cpp'ye eklemeniz gerekir . Bunu nasıl kullanıyorum:

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

Bu test edildi ve MSVC 2010 için çalışıyor ve bir MyDll.pch dosyası oluşturacak, hangi dosya adının kullanıldığından rahatsız olmadım, bu yüzden onu belirtmek için herhangi bir çaba göstermedim.


0

Önceden derlenmiş başlık seçeneği rc dosyalarında çalışmadığından, jari tarafından sağlanan makroyu ayarlamam gerekiyordu.

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

Düzenleme: Bu önceden derlenmiş başlıkların kullanımı, ana Projemin Genel derleme süresini 4 dakika 30 saniyeden 1 dakika 40 saniyeye düşürdü. Bu benim için gerçekten iyi bir şey. Ön derleme başlığında yalnızca boost / stl / Windows / mfc gibi başlıklar bulunur.


-15

Oraya gitme bile. Önceden derlenmiş başlıklar, başlıklardan biri değiştiğinde her şeyi yeniden oluşturmanız gerektiği anlamına gelir . Bunu gerçekleştiren bir yapı sisteminiz varsa şanslısınız. Önceden derlenmekte olan bir şeyi değiştirdiğinizi ve bu nedenle tam bir yeniden oluşturma yapmanız gerektiğini anlayana kadar yapınız hiç olmadığı kadar sıklıkla başarısız olacaktır. Bundan çoğunlukla, kesinlikle pozitif olduğunuz başlıkları önceden derleyerek önleyebilirsiniz, ancak daha sonra hız kazancının büyük bir bölümünü de bırakmış olursunuz.

Diğer sorun, ad alanınızın önceden derlenmiş başlıkları kullanacağınız birçok yerde bilmediğiniz veya umursamadığınız her tür sembolle kirlenmesidir.


29
Onlar başlıklarını başvuran yaparken Önceden derlenmiş başlıkları en kullanışlı değil değiştirmek ... STL, Boost, diğer üçüncü taraf şeyler. PCH'yi kendi proje başlık dosyalarınız için kullanıyorsanız, avantajın çoğunu boşa harcarsınız.
Tom

3
Kendi projenin başlıklar için PCH'yi kullanıyorsanız bile, CMake gibi bir yapı sisteminin bütün mesele emin bağımlılıkları olduğunu yapmaktır edilir saygı. Benim .h dosyası (veya bağımlılıklarından biri) değiştirirsem, istediğiniz .pch rejenere olması. Benim .h dosyası değiştirmezseniz, ben yok .pch rejenere olması istiyorum. Bence OP'nin tüm sorusu şuydu: CMake kullanarak bunu nasıl gerçekleştirebilirim?
Quuxplusone

1
Önceden derlenmiş üstbilgiler, C ++ modülleri tüm ana derleyiciler tarafından desteklenene kadar derleme sürelerini azaltmak için en iyi araçtır. Şablonların ve yalnızca başlık kitaplıklarının kullanımının artmasıyla daha da kötüleşen bir sorunu çözüyorlar. Doğru kullanıldığında ikame yoktur. Her şeye rağmen, bu sorulan soruya bir cevap oluşturmaz ve sadece görüşleri dile getirir. Olumsuz oylama ve silme oyu.
IInspectable
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.