Utiliser des en-têtes pré-compilés avec CMake

J’ai vu quelques (anciens) messages sur le net concernant le piratage de certains supports pour les en-têtes précompilés dans CMake. Ils ont tous l’air un peu partout et chacun a sa propre façon de le faire. Quelle est la meilleure façon de le faire actuellement?

Il existe un module CMake tiers appelé ‘Cotire’ qui automatise l’utilisation des en-têtes précompilés pour les systèmes de construction basés sur CMake et prend également en charge les builds d’unité.

Im utiliser la macro suivante pour générer et utiliser des en-têtes précompilés:

MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecomstackdHeader PrecomstackdSource SourcesVar) IF(MSVC) GET_FILENAME_COMPONENT(PrecomstackdBasename ${PrecomstackdHeader} NAME_WE) SET(PrecomstackdBinary "${CMAKE_CURRENT_BINARY_DIR}/${PrecomstackdBasename}.pch") SET(Sources ${${SourcesVar}}) SET_SOURCE_FILES_PROPERTIES(${PrecomstackdSource} PROPERTIES COMPILE_FLAGS "/Yc\"${PrecomstackdHeader}\" /Fp\"${PrecomstackdBinary}\"" OBJECT_OUTPUTS "${PrecomstackdBinary}") SET_SOURCE_FILES_PROPERTIES(${Sources} PROPERTIES COMPILE_FLAGS "/Yu\"${PrecomstackdHeader}\" /FI\"${PrecomstackdHeader}\" /Fp\"${PrecomstackdBinary}\"" OBJECT_DEPENDS "${PrecomstackdBinary}") # Add precomstackd header to SourcesVar LIST(APPEND ${SourcesVar} ${PrecomstackdSource}) ENDIF(MSVC) ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER) 

Disons que vous avez une variable $ {MySources} avec tous vos fichiers source, le code que vous souhaitez utiliser serait simplement être

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

Le code fonctionnerait toujours très bien sur les plates-formes non-MSVC aussi. Génial 🙂

Voici un extrait de code permettant d’utiliser l’en-tête précompilé pour votre projet. Ajoutez ce qui suit à votre CMakeLists.txt en remplaçant myprecomstackdheaders et myprecomstackdheaders comme il convient:

 if (MSVC) set_source_files_properties(myprecomstackdheaders.cpp PROPERTIES COMPILE_FLAGS "/Ycmyprecomstackdheaders.h" ) foreach( src_file ${myproject_SOURCE_FILES} ) set_source_files_properties( ${src_file} PROPERTIES COMPILE_FLAGS "/Yumyprecomstackdheaders.h" ) endforeach( src_file ${myproject_SOURCE_FILES} ) list(APPEND myproject_SOURCE_FILES myprecomstackdheaders.cpp) endif (MSVC) 

J’ai fini par utiliser une version adaptée de la macro larsm. L’utilisation de $ (IntDir) pour le chemin d’access pch permet de conserver les en-têtes précompilés des versions de débogage et de publication.

 MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecomstackdHeader PrecomstackdSource SourcesVar) IF(MSVC) GET_FILENAME_COMPONENT(PrecomstackdBasename ${PrecomstackdHeader} NAME_WE) SET(PrecomstackdBinary "$(IntDir)/${PrecomstackdBasename}.pch") SET(Sources ${${SourcesVar}}) SET_SOURCE_FILES_PROPERTIES(${PrecomstackdSource} PROPERTIES COMPILE_FLAGS "/Yc\"${PrecomstackdHeader}\" /Fp\"${PrecomstackdBinary}\"" OBJECT_OUTPUTS "${PrecomstackdBinary}") SET_SOURCE_FILES_PROPERTIES(${Sources} PROPERTIES COMPILE_FLAGS "/Yu\"${PrecomstackdHeader}\" /FI\"${PrecomstackdHeader}\" /Fp\"${PrecomstackdBinary}\"" OBJECT_DEPENDS "${PrecomstackdBinary}") # Add precomstackd header to SourcesVar LIST(APPEND ${SourcesVar} ${PrecomstackdSource}) 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) 

Si vous ne voulez pas réinventer la roue, utilisez soit Cotire comme le suggère la réponse supérieure, soit un en -tête plus simple: cmake-precomstackd . Pour l’utiliser, il suffit d’inclure le module et d’appeler:

 include( cmake-precomstackd-header/PrecomstackdHeader.cmake ) add_precomstackd_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp ) 

Un exemple d’en-tête précompilé d’utilisation avec cmake et Visual Studio 2015

“stdafx.h”, “stdafx.cpp” – nom d’en-tête précompilé.

Placez les éléments suivants dans le fichier racine cmake.

 if (MSVC) # For precomstackd header. # Set # "Precomstackd Header" to "Use (/Yu)" # "Precomstackd Header File" to "stdafx.h" set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Yustdafx.h /FIstdafx.h") endif() 

Mettez les éléments suivants dans le fichier cmake du projet.

“src” – un dossier contenant des 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 suggéré par martjno, combiné à la possibilité d’ignorer PCH pour certaines sources si nécessaire (par exemple, sources générées):

 # set PCH for VS project function(SET_TARGET_PRECOMPILED_HEADER Target PrecomstackdHeader PrecomstackdSource) if(MSVC) SET_TARGET_PROPERTIES(${Target} PROPERTIES COMPILE_FLAGS "/Yu${PrecomstackdHeader}") set_source_files_properties(${PrecomstackdSource} PROPERTIES COMPILE_FLAGS "/Yc${PrecomstackdHeader}") 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 la liste des 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 versions 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, elle vous indique l’heure d’append des en-têtes précompilés pour Windows. Sur * nux je voudrais juste utiliser ccache et ne pas m’inquiéter à ce sujet.

J’ai implémenté dans mon application principale et quelques bibliothèques qu’il utilise. Cela fonctionne très bien sur ce point. Une chose est également nécessaire: vous devez créer le fichier source et le fichier d’en-tête pch et inclure dans le fichier source tous les en-têtes que vous souhaitez précomstackr. Je l’ai fait pendant 12 ans avec MFC mais il m’a fallu quelques minutes pour me souvenir de ça ..

La méthode la plus propre consiste à append l’option précompilée en tant qu’option globale. Dans le fichier vcxproj, cela apparaîtra sous la forme

Use et ne le fera pas pour chaque fichier individuel.

Ensuite, vous devez append l’option Create au fichier 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é, donc je n’ai fait aucun effort pour le spécifier.

Comme l’option d’en-tête précompilé ne fonctionne pas pour les fichiers rc, j’ai dû ajuster la macro fournie par jari.

 ####################################################################### # Makro for precomstackd header ####################################################################### MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecomstackdHeader PrecomstackdSource SourcesVar) IF(MSVC) GET_FILENAME_COMPONENT(PrecomstackdBasename ${PrecomstackdHeader} NAME_WE) SET(PrecomstackdBinary "$(IntDir)/${PrecomstackdBasename}.pch") SET(Sources ${${SourcesVar}}) # generate the precomstackd header SET_SOURCE_FILES_PROPERTIES(${PrecomstackdSource} PROPERTIES COMPILE_FLAGS "/Zm500 /Yc\"${PrecomstackdHeader}\" /Fp\"${PrecomstackdBinary}\"" OBJECT_OUTPUTS "${PrecomstackdBinary}") # 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\"${PrecomstackdHeader}\" /FI\"${PrecomstackdHeader}\" /Fp\"${PrecomstackdBinary}\"" OBJECT_DEPENDS "${PrecomstackdBinary}") ENDIF( NOT ${fname} MATCHES ".*rc$" ) ENDFOREACH(fname) # Add precomstackd header to SourcesVar LIST(APPEND ${SourcesVar} ${PrecomstackdSource}) ENDIF(MSVC) ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER) 

Edit: L’utilisation de ces en-têtes précompilés a permis de réduire le temps de compilation 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, il n’ya que des en-têtes comme boost / stl / Windows / mfc.

Ne va même pas là-bas. Les en-têtes précompilés signifient que chaque fois que l’un des en-têtes change, vous devez tout reconstruire. Vous avez de la chance si vous avez un système de construction qui le réalise. Plus souvent que jamais, votre build échouera jusqu’à ce que vous réalisiez que vous avez modifié quelque chose qui est précompilé et que vous devez donc effectuer une reconstruction complète. Vous pouvez éviter cela en précompilant les en-têtes que vous êtes absolument certain de ne pas changer, mais vous perdez é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.