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

104

J'ai vu quelques (vieux) messages sur le net sur le piratage ensemble, 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 faire. Quelle est la meilleure façon de le faire actuellement?

Glutineux
la source

Réponses:

79

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.

Sakra
la source
1
J'ai créé un ensemble de macros qui enveloppent la fonctionnalité cotire (en-têtes précompilés et builds unitaire) ici pour une utilisation plus facile
onqtam
2
@onqtam comment est-ce plus facile, c'est tellement géant que je ne vois même pas comment simplement être précompilé en travaillant avec cmake et gcc
Pavel P
5
CMake 3.16 a introduit la prise en charge intégrée des en-têtes précompilés. Voir ma réponse stackoverflow.com/a/59514029/2799037 Plus besoin de modules tiers!
usr1234567
34

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

Larsmoa
la source
2
Cette macro a 1 défaut. Si le générateur n'est pas basé sur MSVC, la source précompilée ne sera pas ajoutée à la liste des sources. Donc ma modification déplace simplement l' list( APPEND ... )extérieur de la fermeture endif(). Voir le code complet ici: pastebin.com/84dm5rXZ
void.pointer
1
@RobertDailey: C'est en fait délibéré - je ne veux pas compiler le fichier source précompilé lorsque je n'utilise pas d'en-têtes précompilés - il ne devrait de toute façon définir aucun symbole.
larsmoa
1
@Iarsam Veuillez corriger les arguments /Yuet /FI, ils devraient l'être ${PrecompiledHeader}et non ${PrecompiledBinary}.
Mourad
pouvez-vous expliquer pourquoi nous avons besoin des indicateurs "/ Fp" et "/ FI"? Selon msdn.microsoft.com/en-us/library/z0atkd6c.aspx, l' utilisation de "/ Fp" n'est pas obligatoire. Cependant, si je coupe ces drapeaux de votre macro, aucun pch n'est défini.
Vram Vardanian
2
Sachez cependant que l'argument de / Yu est pris au pied de la lettre. Par exemple /YuC:/foo/bar.h, vous forcera à passer l' /FpC:/foo/bar.hindicateur 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 #includearguments, 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.
Manuzor le
21

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/

janisozaur
la source
2
Voici le lien vers la documentation officielle de CMake: cmake.org/cmake/help/latest/command/…
Alex Che
2
Il y a un article de l'équipe MSVC sur la détermination des en-têtes à inclure dans PCH: devblogs.microsoft.com/cppblog
...
19

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 myprecompiledheaderset myproject_SOURCE_FILESle 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)
Dave Hillier
la source
Merci; J'utiliserai ceci comme guide pour en créer un pour GCC (si je peux). Je posterai ma réponse dès que j'aurai terminé. =]
strager
@Jayen, non; J'ai finalement abandonné le projet et je n'ai plus jamais eu les tracas du C ++, en gros.
strager
Est-il possible de définir PCH sur l'ensemble du projet? Parce qu'il n'est pas possible d'obtenir la liste des fichiers générés automatiquement dans cmake with set( CMAKE_AUTOMOC ON ).
Dmitry Sazonov
J'ai utilisé votre solution, mais malheureusement, le temps de compilation est passé de 2'10 "à 2'40", pour ~ 130 fichiers. Dois-je m'assurer que cela myprecompiledheader.cppest 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.hcontient uniquement les en-têtes STL les plus courants que mon code utilise.
Grim Fandango
13

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})
jari
la source
2
L'abstraction $ {IntDir} est utile. Merci.
Gopalakrishna Palem
12

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)
Martjno
la source
2
J'avais l'habitude d'utiliser cette solution, mais cela ne fonctionne que si le projet ne contient que des fichiers C ++. Puisque COMPILE_FLAGS est appliqué à tous les fichiers source, il sera également appliqué aux fichiers c (par exemple ceux générés par MIDL), qui n'aimeront pas le c ++ PCH. Lorsque vous utilisez la solution de Dave, vous pouvez utiliser get_source_file_property (_language $ {src_file} LANGUAGE), et ne définir les indicateurs du compilateur que s'il s'agit vraiment d'un fichier CXX.
Andreas Haferburg
C'est bien d'avoir la flexibilité de l'autre solution dans ma poche arrière, mais c'est celle que je recherchais, merci!
kylewm
Bonne réponse. Méfiez-vous des parenthèses manquantes pour set_source_files_properties.
Arnaud
2
Il peut être désactivé de manière sélective pour les fichiers individuels avec / Y- en utilisant set_source_files_properties
mlt
Qu'y a-t-il abcdans votre exemple?
Sandburg
7

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 )
Roman Kruglov
la source
5

CMake 3.16 a introduit la prise en charge des en-têtes précompilés. Il existe une nouvelle commande CMake target_precompile_headersqui 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

usr1234567
la source
2
Cette réponse n'ajoute rien de nouveau à la réponse de janisozaur, publiée six mois plus tôt, à l'exception du lien vers la documentation officielle finale, qui devrait probablement être mieux ajoutée en commentaire à cette réponse.
Alex Che
4

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

"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"
)
Maks
la source
3

À 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.

Vram Vardanian
la source
0

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.


la source
0

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' Createoption à 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.

déshabillé
la source
0

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.

schorsch_76
la source
-15

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.

Dirk Groeneveld
la source
29
Les en-têtes précompilés sont plus utiles lorsqu'ils font référence à des en-têtes qui ne changent pas ... STL, Boost, d'autres éléments tiers. Si vous utilisez PCH pour vos propres fichiers d'en-tête de projet, vous perdez la plupart des avantages.
Tom
3
Même si vous utilisez PCH pour les en-têtes de votre propre projet, tout l'intérêt d'un système de construction comme CMake est de s'assurer que les dépendances sont respectées. Si je change mon fichier .h (ou l'une de ses dépendances), je veux que le .pch soit régénéré. Si je ne change pas mon fichier .h, je ne veux pas que le .pch soit régénéré. Je pense que toute la question du PO était: comment faire pour que cela se produise, en utilisant CMake?
Quuxplusone
1
Les en-têtes précompilés sont le meilleur outil pour réduire les temps de compilation jusqu'à ce que les modules C ++ soient pris en charge par tous les compilateurs traditionnels. Ils résolvent un problème qui s'est aggravé avec l'utilisation toujours croissante de modèles et de bibliothèques d'en-tête uniquement. Lorsqu'il est utilisé correctement, il n'y a pas de substitut. Quoi qu'il en soit, cela ne constitue pas une réponse à la question posée et exprime simplement une opinion. Votes à la baisse et à la suppression.
IInspectable