J'ai vu quelques (anciens) messages sur le net concernant le piratage d'un support pour les en-têtes précompilés dans CMake. Ils semblent tous 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 nommé 'Cotire' qui automatise l'utilisation des 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 également sur les plates-formes non MSVC. Génial :)
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
selon le cas:
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)
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 cibles, 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 simplement Cotire comme le suggère la réponse du haut ou une 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 )
À mon humble avis, le meilleur moyen est de définir PCH pour l'ensemble du projet, comme l'a suggéré martjno, combiné avec la possibilité 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 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.
"stdafx.h", "stdafx.cpp" - nom d'en-tête précompilé.
Mettez ce qui suit ci-dessous dans le fichier racine cmake.
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 des fichiers sources.
set_source_files_properties(src/stdafx.cpp
PROPERTIES
COMPILE_FLAGS "/Ycstdafx.h"
)
Eh bien, lorsque les builds prennent plus de 10 minutes sur une machine quadricœur à 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, je voudrais simplement utiliser ccache et ne pas m'inquiéter à ce sujet.
J'ai implémenté dans mon application principale et quelques-unes des bibliothèques qu'elle utilise. Cela fonctionne très bien à ce point. Une chose qui est également nécessaire est que vous devez créer le fichier source et en-tête pch et inclure dans le fichier source tous les en-têtes que vous souhaitez précompiler. Je l'ai fait pendant 12 ans avec MFC mais il m'a fallu quelques minutes pour me rappeler que ..
CMake vient de prendre en charge les PCH, il devrait être disponible dans la prochaine version 3.16, prévue le 2019-10-01:
https://gitlab.kitware.com/cmake/cmake/merge_requests/355
target_precompile_headers(<target>
<INTERFACE|PUBLIC|PRIVATE> [header1...]
[<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])
Il y a une discussion en cours sur la prise en charge du partage des PCH entre les cibles: https://gitlab.kitware.com/cmake/cmake/issues/19659
Il y a un contexte supplémentaire (motivation, chiffres) disponible sur https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming- cmake /
La façon la plus simple consiste à ajouter l'option précompilée en tant qu'option globale. Dans le fichier vcxproj, cela apparaîtra comme <PrecompiledHeader>Use</PrecompiledHeader>
et ne pas le faire pour chaque fichier individuel.
Ensuite, vous devez ajouter 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 me soucie pas du 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'avais besoin 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 4 min 30 s à 1 min 40 s. C'est pour moi une très bonne chose. Dans l'en-tête de précompilation, il n'y a que des en-têtes comme boost/stl/Windows/mfc.