Réponses:
Il existe un module CMake tiers nommé «Cotire» qui automatise l'utilisation d'en-têtes précompilés pour les systèmes de build basés sur CMake et prend également en charge les builds unitaires.
J'utilise la macro suivante pour générer et utiliser des en-têtes précompilés:
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)
Disons que vous avez une variable $ {MySources} avec tous vos fichiers source, le code que vous voudriez utiliser serait simplement
ADD_MSVC_PRECOMPILED_HEADER("precompiled.h" "precompiled.cpp" MySources)
ADD_LIBRARY(MyLibrary ${MySources})
Le code fonctionnerait toujours très bien sur les plates-formes non MSVC également. Génial :)
list( APPEND ... )
extérieur de la fermeture endif()
. Voir le code complet ici: pastebin.com/84dm5rXZ
/Yu
et /FI
, ils devraient l'être ${PrecompiledHeader}
et non ${PrecompiledBinary}
.
/YuC:/foo/bar.h
, vous forcera à passer l' /FpC:/foo/bar.h
indicateur ou à placer #include <C:/foo/bar.h>
en haut de tous vos fichiers .cpp comme première instruction d'inclusion. MSVC effectue une comparaison de chaîne des #include
arguments, il ne vérifie pas s'il pointe vers le même fichier que celui auquel il a été donné /Yu
. Ergo, #include <bar.h>
ne fonctionnera pas et émettra l'erreur C2857.
CMake vient d'obtenir le support des PCH, il devrait être disponible dans la prochaine version 3.16, prévue pour le 01/10/2019:
https://gitlab.kitware.com/cmake/cmake/merge_requests/3553
target_precompile_headers(<target>
<INTERFACE|PUBLIC|PRIVATE> [header1...]
[<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])
Des discussions sont en cours sur la prise en charge du partage des PCH entre les cibles: https://gitlab.kitware.com/cmake/cmake/issues/19659
Un contexte supplémentaire (motivation, chiffres) est disponible sur https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/
Voici un extrait de code pour vous permettre d'utiliser un en-tête précompilé pour votre projet. Ajoutez ce qui suit à votre CMakeLists.txt en remplaçant myprecompiledheaders
et myproject_SOURCE_FILES
le cas échéant:
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
est compilé en premier? D'après cet extrait de code, il semble qu'il sera compilé en dernier, c'est peut-être ce qui pourrait causer le retard. myprecompiledheader.h
contient uniquement les en-têtes STL les plus courants que mon code utilise.
J'ai fini par utiliser une version adaptée de la macro larsm. L'utilisation de $ (IntDir) pour le chemin pch permet de séparer les en-têtes précompilés pour les versions de débogage et de publication.
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})
Adapté de Dave, mais plus efficace (définit les propriétés de la cible, pas pour chaque fichier):
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
dans votre exemple?
si vous ne voulez pas réinventer la roue, utilisez simplement Cotire comme le suggère la première réponse ou un plus simple - cmake-precompiled-header ici . Pour l'utiliser, il suffit d'inclure le module et d'appeler:
include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )
CMake 3.16 a introduit la prise en charge des en-têtes précompilés. Il existe une nouvelle commande CMake target_precompile_headers
qui fait tout ce dont vous avez besoin sous le capot. Voir sa documentation pour plus de détails: https://cmake.org/cmake/help/latest/command/target_precompile_headers.html
"stdafx.h", "stdafx.cpp" - nom d'en-tête précompilé.
Mettez ce qui suit ci-dessous dans le fichier cmake racine.
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()
Mettez ce qui suit ci-dessous dans le fichier cmake du projet.
"src" - un dossier avec les fichiers source.
set_source_files_properties(src/stdafx.cpp
PROPERTIES
COMPILE_FLAGS "/Ycstdafx.h"
)
À mon humble avis, le meilleur moyen est de définir PCH pour l'ensemble du projet, comme martjno l'a suggéré, combiné avec la capacité d'ignorer PCH pour certaines sources si nécessaire (par exemple, les sources générées):
# 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)
Donc, si vous avez une cible MY_TARGET et une liste de sources générées IGNORE_PCH_SRC_LIST, vous ferez simplement:
SET_TARGET_PRECOMPILED_HEADER(MY_TARGET stdafx.h stdafx.cpp)
IGNORE_PRECOMPILED_HEADER(IGNORE_PCH_SRC_LIST)
Cette approche est testée et fonctionne parfaitement.
Eh bien, lorsque les builds prennent plus de 10 minutes sur une machine quad core chaque fois que vous modifiez une seule ligne dans l'un des fichiers de projet, il vous indique qu'il est temps d'ajouter des en-têtes précompilés pour Windows. Sur * nux, j'utiliserais simplement ccache et je ne m'en soucierais pas.
J'ai implémenté dans mon application principale et quelques-unes des bibliothèques qu'elle utilise. Cela fonctionne très bien jusqu'à présent. Une chose qui est également nécessaire est que vous devez créer la source pch et le fichier d'en-tête et inclure dans le fichier source tous les en-têtes que vous souhaitez précompiler. J'ai fait ça pendant 12 ans avec MFC mais il m'a fallu quelques minutes pour m'en souvenir.
Le moyen le plus simple est d'ajouter l'option précompilée en tant qu'option globale. Dans le fichier vcxproj, cela apparaîtra comme <PrecompiledHeader>Use</PrecompiledHeader>
et ne le fera pas pour chaque fichier individuel.
Ensuite, vous devez ajouter l' Create
option à StdAfx.cpp. Voici comment je l'utilise:
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})
Ceci est testé et fonctionne pour MSVC 2010 et créera un fichier MyDll.pch, je ne suis pas dérangé par le nom de fichier utilisé, je n'ai donc fait aucun effort pour le spécifier.
Comme l'option d'en-tête précompilée ne fonctionne pas pour les fichiers rc, j'ai dû ajuster la macro fournie par 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)
Edit: L'utilisation de ces en-têtes précompilés a réduit le temps de construction global de mon projet principal de 4min 30s à 1min 40s. C'est pour moi une très bonne chose. Dans l'en-tête de précompilation se trouvent uniquement des en-têtes tels que boost / stl / Windows / mfc.
N'y allez même pas. Les en-têtes précompilés signifient que chaque fois qu'un des en-têtes change, vous devez tout reconstruire . Vous avez de la chance si vous avez un système de construction qui s'en rend compte. Plus souvent que jamais, votre build échouera jusqu'à ce que vous vous rendiez compte que vous avez changé quelque chose qui est en cours de précompilation, et que vous devez donc effectuer une reconstruction complète. Vous pouvez éviter cela principalement en précompilant les en-têtes dont vous êtes absolument sûr qu'ils ne changeront pas, mais vous abandonnez également une grande partie du gain de vitesse.
L'autre problème est que votre espace de noms est pollué par toutes sortes de symboles que vous ne connaissez pas ou dont vous ne vous souciez pas dans de nombreux endroits où vous utiliseriez les en-têtes précompilés.