Risposte:
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à.
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 :)
list( APPEND ... )
esterno della chiusura endif()
. Vedi il codice completo qui: pastebin.com/84dm5rXZ
/Yu
e /FI
, dovrebbero essere ${PrecompiledHeader}
e non ${PrecompiledBinary}
.
/YuC:/foo/bar.h
ti costringerà a passare il /FpC:/foo/bar.h
flag 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 #include
argomenti, non controlla se punta allo stesso file a cui è stato assegnato /Yu
. Ergo, #include <bar.h>
non funzionerà ed emetterà l'errore C2857.
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/
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 myprecompiledheaders
e myproject_SOURCE_FILES
come 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)
with set( CMAKE_AUTOMOC ON )
.
myprecompiledheader.cpp
sia compilato prima? Da questo frammento sembra che verrà compilato per ultimo, quindi forse è questo che potrebbe causare il ritardo. myprecompiledheader.h
contiene solo le intestazioni STL più comuni utilizzate dal mio codice.
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})
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)
abc
tuo esempio?
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 )
CMake 3.16 ha introdotto il supporto per le intestazioni precompilate. C'è un nuovo comando CMake target_precompile_headers
che 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
"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"
)
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.
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 ..
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' Create
opzione 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.
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.
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.