Utilizzo di intestazioni precompilate con CMake


104

Ho visto alcuni (vecchi) post in rete sull'hacking insieme del supporto per gli header precompilati in CMake. Sembrano tutti un po 'ovunque e ognuno ha il proprio modo di farlo. Qual è il modo migliore per farlo attualmente?

Risposte:


79

Esiste un modulo CMake di terze parti denominato "Cotire" che automatizza l'uso di intestazioni precompilate per i sistemi di compilazione basati su CMake e supporta anche i build di unità.


1
Ho creato una serie di macro che racchiudono la funzionalità cotire (intestazioni precompilate e build di unità) qui per un utilizzo più semplice
onqtam

2
@onqtam com'è più facile, è così grande che non vedo nemmeno come essere semplicemente precompilato lavorando con cmake e gcc
Pavel P

5
CMake 3.16 ha introdotto il supporto integrato per le intestazioni precompilate. Vedi la mia risposta stackoverflow.com/a/59514029/2799037 Non sono più necessari moduli di terze parti!
usr1234567

34

Sto usando la seguente macro per generare e utilizzare intestazioni precompilate:

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)

Diciamo che hai una variabile $ {MySources} con tutti i tuoi file sorgente, il codice che vorresti usare sarebbe semplicemente

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

Il codice funzionerebbe comunque perfettamente anche su piattaforme non MSVC. Piuttosto pulito :)


2
Questa macro ha 1 difetto. Se il generatore non è basato su MSVC, l'origine precompilata non verrà aggiunta all'elenco delle origini. Quindi la mia modifica sposta semplicemente l' list( APPEND ... )esterno della chiusura endif(). Vedi il codice completo qui: pastebin.com/84dm5rXZ
void.pointer

1
@RobertDailey: Questo è in realtà deliberato - non voglio compilare il file sorgente precompilato quando non si usano intestazioni precompilate - non dovrebbe comunque definire alcun simbolo.
larsmoa

1
@ Iarsam Per favore correggi gli argomenti /Yue /FI, dovrebbero essere ${PrecompiledHeader}e non ${PrecompiledBinary}.
Mourad

puoi spiegare perché abbiamo bisogno dei flag "/ Fp" e "/ FI"? Secondo msdn.microsoft.com/en-us/library/z0atkd6c.aspx l' uso di "/ Fp" non è obbligatorio. Comunque se ritaglio quei flag dalla tua macro non viene impostato nessun pch.
Vram Vardanian

2
Tieni presente però che l'argomento a / Yu è preso alla lettera. Ad esempio, /YuC:/foo/bar.hti costringerà a passare il /FpC:/foo/bar.hflag oa metterlo #include <C:/foo/bar.h>all'inizio di tutti i tuoi file .cpp come prima istruzione include. MSVC esegue un confronto delle stringhe degli #includeargomenti, non controlla se punta allo stesso file a cui è stato assegnato /Yu. Ergo, #include <bar.h>non funzionerà ed emetterà l'errore C2857.
Manuzor

21

CMake ha appena ottenuto il supporto per PCH, dovrebbe essere disponibile nella prossima versione 3.16, prevista per il 2019-10-01:

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

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

È in corso una discussione sul supporto della condivisione di PCH tra i target: https://gitlab.kitware.com/cmake/cmake/issues/19659

C'è qualche contesto aggiuntivo (motivazione, numeri) disponibile su https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/


2
Ecco il link alla documentazione ufficiale di CMake: cmake.org/cmake/help/latest/command/…
Alex Che

2
C'è un post del team di MSVC su come capire quali intestazioni includere in PCH: devblogs.microsoft.com/cppblog/…
janisozaur

19

Ecco uno snippet di codice per consentirti di utilizzare l'intestazione precompilata per il tuo progetto. Aggiungi quanto segue al tuo CMakeLists.txt in sostituzione myprecompiledheaderse myproject_SOURCE_FILEScome appropriato:

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)

Grazie; Lo userò come guida per crearne uno per GCC (se posso). Pubblicherò la mia risposta non appena avrò finito. =]
strager

@Jayen, no; Alla fine ho abbandonato il progetto e non sono mai più entrato nei problemi di C ++, in pratica.
Strager

È possibile impostare PCH sull'intero progetto? Perché non è possibile ottenere l'elenco dei file generati automaticamente in cmake with set( CMAKE_AUTOMOC ON ).
Dmitry Sazonov

Ho usato la tua soluzione, ma sfortunatamente il tempo di compilazione è passato da 2'10 "a 2'40", per ~ 130 file. Devo assicurarmi che myprecompiledheader.cppsia compilato prima? Da questo frammento sembra che verrà compilato per ultimo, quindi forse è questo che potrebbe causare il ritardo. myprecompiledheader.hcontiene solo le intestazioni STL più comuni utilizzate dal mio codice.
Grim Fandango

13

Ho finito per utilizzare una versione adattata della macro larsm. L'utilizzo di $ (IntDir) per pch path mantiene separate le intestazioni precompilate per le build di debug e di rilascio.

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
L'astrazione di $ {IntDir} è utile. Grazie.
Gopalakrishna Palem

12

Adattato da Dave, ma più efficiente (imposta le proprietà di destinazione, non per ogni 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)

2
Usavo questa soluzione, ma funziona solo se il progetto contiene solo file c ++. Poiché COMPILE_FLAGS è applicato a tutti i file sorgente, verrà applicato anche ai file c (ad esempio quelli generati da MIDL), che non gradiranno il c ++ PCH. Quando si utilizza la soluzione di Dave, è possibile utilizzare get_source_file_property (_language $ {src_file} LANGUAGE) e impostare i flag del compilatore solo se è davvero un file CXX.
Andreas Haferburg,

Bello avere la flessibilità dell'altra soluzione nella tasca posteriore dei pantaloni, ma questa è quella che stavo cercando, grazie!
kylewm

Bella risposta. Fai attenzione alle parentesi mancanti per set_source_files_properties.
Arnaud

2
Può essere disattivato selettivamente per i singoli file con / Y- utilizzando set_source_files_properties
mlt

Qual è il abctuo esempio?
Sandburg

7

se non vuoi reinventare la ruota, usa Cotire come suggerisce la risposta in alto o uno più semplice - cmake-precompiled-header qui . Per usarlo basta includere il modulo e chiamare:

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

5

CMake 3.16 ha introdotto il supporto per le intestazioni precompilate. C'è un nuovo comando CMake target_precompile_headersche fa tutto ciò di cui hai bisogno sotto il cofano. Consulta la sua documentazione per ulteriori dettagli: https://cmake.org/cmake/help/latest/command/target_precompile_headers.html


2
Questa risposta non aggiunge nulla di nuovo alla risposta di janisozaur, pubblicata sei mesi prima, tranne il link alla documentazione ufficiale finale, che probabilmente dovrebbe essere aggiunta meglio come commento a quella risposta.
Alex Che

4

Un esempio di utilizzo dell'intestazione precompilata con cmake e Visual Studio 2015

"stdafx.h", "stdafx.cpp" - nome dell'intestazione precompilato.

Metti quanto segue di seguito nel file cmake di root.

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

Inserisci quanto segue di seguito nel file cmake del progetto.

"src" - una cartella con i file di origine.

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

3

IMHO il modo migliore è impostare PCH per l'intero progetto, come suggerito da martjno, combinato con la capacità di ignorare PCH per alcune fonti, se necessario (ad es. Sorgenti generate):

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

Quindi, se hai un MY_TARGET di destinazione e un elenco di sorgenti generate IGNORE_PCH_SRC_LIST, farai semplicemente:

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

Questo approccio è testato e funziona perfettamente.


0

Bene, quando le build richiedono più di 10 minuti su una macchina quad core ogni volta che cambi una singola riga in uno dei file di progetto, ti dice che è ora di aggiungere intestazioni precompilate per Windows. Su * nux userei semplicemente ccache e non mi preoccuperei di questo.

Ho implementato nella mia applicazione principale e in alcune delle librerie che utilizza. Funziona benissimo fino a questo punto. Una cosa che è anche necessaria è che devi creare il sorgente pch e il file di intestazione e nel file di origine includere tutte le intestazioni che desideri siano precompilate. L'ho fatto per 12 anni con MFC, ma mi ci sono voluti alcuni minuti per ricordarlo ..


0

Il modo più pulito è aggiungere l'opzione precompilata come opzione globale. Nel file vcxproj questo verrà visualizzato come <PrecompiledHeader>Use</PrecompiledHeader>e non lo farà per ogni singolo file.

Quindi è necessario aggiungere l' Createopzione a StdAfx.cpp. Quello che segue è come lo uso:

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

Questo è testato e funziona per MSVC 2010 e creerà un file MyDll.pch, non sono preoccupato del nome del file utilizzato, quindi non ho fatto alcuno sforzo per specificarlo.


0

Poiché l'opzione di intestazione precompilata non funziona per i file rc, avevo bisogno di regolare la macro fornita da 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)

Modifica: l'utilizzo di queste intestazioni precompilate ha ridotto il tempo di costruzione complessivo del mio progetto principale da 4 minuti e 30 secondi a 1 minuto e 40 secondi. Questa è per me davvero una buona cosa. Nell'intestazione precompilata ci sono solo intestazioni come boost / stl / Windows / mfc.


-15

Non andarci nemmeno. Le intestazioni precompilate significano che ogni volta che una delle intestazioni cambia, devi ricostruire tutto . Sei fortunato se hai un sistema di compilazione che lo realizza. Più spesso che mai, la tua build fallirà fino a quando non ti renderai conto di aver cambiato qualcosa che è stato precompilato e quindi devi fare una ricostruzione completa. Puoi evitarlo principalmente precompilando le intestazioni che sei assolutamente positivo non cambierà, ma poi stai rinunciando anche a gran parte del guadagno di velocità.

L'altro problema è che il tuo spazio dei nomi viene inquinato da tutti i tipi di simboli che non conosci o che non ti interessano in molti punti in cui useresti le intestazioni precompilate.


29
Le intestazioni precompilate sono più utili quando fanno riferimento a intestazioni che non cambiano ... STL, Boost, altre cose di terze parti. Se stai usando PCH per i tuoi file di intestazione del progetto, stai sprecando la maggior parte del vantaggio.
Tom

3
Anche se stai usando PCH per le intestazioni del tuo progetto, il punto centrale di un sistema di compilazione come CMake è assicurarsi che le dipendenze siano rispettate. Se cambio il mio file .h (o una delle sue dipendenze), voglio che il .pch venga rigenerato. Se non cambio il mio file .h, non voglio che il .pch venga rigenerato. Penso che l'intera domanda dell'OP fosse: come faccio a farlo accadere, usando CMake?
Quuxplusone

1
Gli header precompilati sono lo strumento migliore per ridurre i tempi di compilazione fino a quando i moduli C ++ non saranno supportati da tutti i compilatori mainstream. Risolvono un problema che è appena peggiorato con l'uso sempre crescente di modelli e librerie di sole intestazioni. Se usato correttamente, non ci sono sostituti. Indipendentemente da ciò, questo non costituisce una risposta alla domanda che viene posta e si limita a esprimere l'opinione. Votato per difetto e per cancellazione.
Rilevabile
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.