Verwenden vorkompilierter Header mit CMake

104

Ich habe einige (alte) Posts im Internet gesehen, in denen es darum ging, Unterstützung für vorkompilierte Header in CMake zusammen zu hacken. Sie scheinen alle ein bisschen überall zu sein und jeder hat seine eigene Art, es zu tun. Was ist derzeit der beste Weg, dies zu tun?

Klebrig
quelle

Antworten:

79

Es gibt ein CMake-Modul eines Drittanbieters namens "Cotire", das die Verwendung vorkompilierter Header für CMake-basierte Build-Systeme automatisiert und auch Unity-Builds unterstützt.

Sakra
quelle
1
Ich habe eine Reihe von Makros erstellt, die die Cotire-Funktionalität (vorkompilierte Header und Unity-Builds) hier zur einfacheren Verwendung
einschließen
2
@onqtam wie ist das einfacher, es ist so riesig, dass ich nicht einmal sehe, wie man einfach vorkompiliert mit cmake und gcc arbeitet
Pavel P
5
CMake 3.16 führte eine integrierte Unterstützung für vorkompilierte Header ein. Siehe meine Antwort stackoverflow.com/a/59514029/2799037 Es werden keine Module von Drittanbietern mehr benötigt!
usr1234567
34

Ich benutze das folgende Makro, um vorkompilierte Header zu generieren und zu verwenden:

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)

Nehmen wir an, Sie haben eine Variable $ {MySources} mit all Ihren Quelldateien. Der Code, den Sie verwenden möchten, ist einfach

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

Der Code würde auch auf Nicht-MSVC-Plattformen weiterhin einwandfrei funktionieren. Ziemlich ordentlich :)

Larsmoa
quelle
2
Dieses Makro hat 1 Fehler. Wenn der Generator nicht MSVC-basiert ist, wird die vorkompilierte Quelle nicht zur Liste der Quellen hinzugefügt. Meine Modifikation verschiebt also einfach die list( APPEND ... )Außenseite des Schließens endif(). Gesamten Code hier: pastebin.com/84dm5rXZ
void.pointer
1
@RobertDailey: Dies ist eigentlich absichtlich - ich möchte die vorkompilierte Quelldatei nicht kompilieren, wenn keine vorkompilierten Header verwendet werden - es sollte sowieso keine Symbole definieren.
Larsmoa
1
@Iarsam Bitte korrigieren Sie die /Yuund /FIArgumente, sie sollten sein ${PrecompiledHeader}und nicht ${PrecompiledBinary}.
Mourad
Können Sie erklären, warum wir die Flags "/ Fp" und "/ FI" benötigen? Laut msdn.microsoft.com/en-us/library/z0atkd6c.aspx ist die Verwendung von "/ Fp" nicht obligatorisch. Wenn ich jedoch diese Flags aus Ihrem Makro ausschneide, wird kein pch gesetzt.
Vram Vardanian
2
Beachten Sie jedoch, dass das Argument zu / Yu sehr wörtlich genommen wird. /YuC:/foo/bar.hZum Beispiel werden Sie gezwungen, entweder das /FpC:/foo/bar.hFlag zu übergeben oder #include <C:/foo/bar.h>als erste include-Anweisung oben in alle Ihre .cpp-Dateien zu setzen. MSVC führt einen Zeichenfolgenvergleich der #includeArgumente durch und prüft nicht, ob es auf dieselbe Datei verweist, für die es angegeben wurde /Yu. Ergo #include <bar.h>wird nicht funktionieren und Fehler C2857 ausgeben.
Manuzor
21

CMake hat gerade Unterstützung für PCHs erhalten. Es sollte in der kommenden Version 3.16 verfügbar sein, die am 01.10.2019 erscheinen soll:

https://gitlab.kitware.com/cmake/cmake/merge_requests/3553

  target_precompile_headers(<target>
    <INTERFACE|PUBLIC|PRIVATE> [header1...]
    [<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])

Es gibt derzeit Diskussionen über die Unterstützung der gemeinsamen Nutzung von PCHs zwischen Zielen: https://gitlab.kitware.com/cmake/cmake/issues/19659

Unter https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/ steht ein zusätzlicher Kontext (Motivation, Zahlen) zur Verfügung.

Janisozaur
quelle
2
Hier ist der Link zur offiziellen CMake-Dokumentation: cmake.org/cmake/help/latest/command/…
Alex Che
2
Es gibt einen Beitrag des MSVC-Teams, in dem es darum geht, herauszufinden, welche Header in PCH aufgenommen werden sollen: devblogs.microsoft.com/cppblog/…
janisozaur
19

Hier ist ein Code-Snippet, mit dem Sie vorkompilierte Header für Ihr Projekt verwenden können. Fügen Sie Ihrer CMakeLists.txt Folgendes hinzu myprecompiledheadersund ersetzen Sie sie myproject_SOURCE_FILESentsprechend:

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
quelle
Vielen Dank; Ich werde dies als Leitfaden verwenden, um einen für GCC zu erstellen (wenn ich kann). Ich werde meine Antwort veröffentlichen, sobald ich fertig bin. =]
Strager
@ Jayen, Nein; Ich habe das Projekt schließlich fallen lassen und bin im Grunde nie wieder in die Probleme von C ++ geraten.
Strager
Ist es möglich, PCH auf das gesamte Projekt einzustellen? Weil es nicht möglich ist, eine Liste der automatisch generierten Dateien in cmake abzurufen with set( CMAKE_AUTOMOC ON ).
Dmitry Sazonov
Ich habe Ihre Lösung verwendet, aber leider wurde die Kompilierungszeit für ~ 130 Dateien von 2'10 "auf 2'40" erhöht. Muss ich sicherstellen, dass myprecompiledheader.cppdas zuerst kompiliert wird? Aus diesem Snippet geht hervor, dass es zuletzt kompiliert wird. Vielleicht ist es das, was die Verzögerung verursacht. myprecompiledheader.henthält nur die häufigsten STL-Header, die mein Code verwendet.
Grim Fandango
13

Am Ende habe ich eine angepasste Version des Larsm-Makros verwendet. Durch die Verwendung von $ (IntDir) für den pch-Pfad werden vorkompilierte Header für Debug- und Release-Builds getrennt.

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
quelle
2
Die Abstraktion $ {IntDir} ist hilfreich. Vielen Dank.
Gopalakrishna Palem
12

Angepasst von Dave, aber effizienter (legt Zieleigenschaften fest, nicht für jede Datei):

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
quelle
2
Früher habe ich diese Lösung verwendet, aber sie funktioniert nur, wenn das Projekt nur C ++ - Dateien enthält. Da COMPILE_FLAGS auf alle Quelldateien angewendet wird, wird es auch auf c-Dateien angewendet (z. B. die von MIDL generierten), denen der c ++ PCH nicht gefällt. Wenn Sie Daves Lösung verwenden, können Sie get_source_file_property (_language $ {src_file} LANGUAGE) verwenden und die Compiler-Flags nur setzen, wenn es sich wirklich um eine CXX-Datei handelt.
Andreas Haferburg
Schön, die Flexibilität der anderen Lösung in meiner Gesäßtasche zu haben, aber dies ist die, nach der ich gesucht habe, danke!
Kylewm
Gute Antwort. Beachten Sie die fehlende Klammer für set_source_files_properties.
Arnaud
2
Es kann selektiv für einzelne Dateien mit / Y- mit set_source_files_properties deaktiviert werden
mlt
Was ist abcin deinem Beispiel?
Sandburg
7

Wenn Sie das Rad nicht neu erfinden möchten, verwenden Sie entweder Cotire, wie in der oberen Antwort angegeben, oder einen einfacheren - cmake-vorkompilierten Header hier . Um es zu verwenden, fügen Sie einfach das Modul hinzu und rufen Sie auf:

include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )
Roman Kruglov
quelle
5

CMake 3.16 führte die Unterstützung für vorkompilierte Header ein. Es gibt einen neuen CMake-Befehl, target_precompile_headersder alles unter der Haube erledigt, was Sie brauchen. Weitere Informationen finden Sie in der Dokumentation: https://cmake.org/cmake/help/latest/command/target_precompile_headers.html

usr1234567
quelle
2
Diese Antwort fügt der Antwort von Janisozaur, die ein halbes Jahr zuvor veröffentlicht wurde, nichts Neues hinzu , außer dem Link zur endgültigen offiziellen Dokumentation, die wahrscheinlich besser als Kommentar zu dieser Antwort hinzugefügt werden sollte.
Alex Che
4

Ein Beispiel für die Verwendung eines vorkompilierten Headers mit cmake und Visual Studio 2015

"stdafx.h", "stdafx.cpp" - vorkompilierter Headername.

Fügen Sie Folgendes in die Root-cmake-Datei ein.

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

Fügen Sie Folgendes in die Projekt-cmake-Datei ein.

"src" - ein Ordner mit Quelldateien.

set_source_files_properties(src/stdafx.cpp
    PROPERTIES
    COMPILE_FLAGS "/Ycstdafx.h"
)
Maks
quelle
3

IMHO ist der beste Weg, PCH für das gesamte Projekt festzulegen, wie von Martjno vorgeschlagen, kombiniert mit der Fähigkeit, PCH für einige Quellen bei Bedarf zu ignorieren (z. B. generierte Quellen):

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

Wenn Sie also ein Ziel MY_TARGET und eine Liste der generierten Quellen IGNORE_PCH_SRC_LIST haben, gehen Sie einfach wie folgt vor:

SET_TARGET_PRECOMPILED_HEADER(MY_TARGET stdafx.h stdafx.cpp)
IGNORE_PRECOMPILED_HEADER(IGNORE_PCH_SRC_LIST)

Dieser Ansatz ist getestet und funktioniert einwandfrei.

Vram Vardanian
quelle
0

Wenn Builds auf einem Quad-Core-Computer jedes Mal mehr als 10 Minuten dauern, wenn Sie eine einzelne Zeile in einer der Projektdateien ändern, werden Sie aufgefordert, vorkompilierte Header für Windows hinzuzufügen. Unter * nux würde ich nur ccache verwenden und mir darüber keine Sorgen machen.

Ich habe in meiner Hauptanwendung und einigen der von ihr verwendeten Bibliotheken implementiert. Bis jetzt funktioniert es großartig. Eine Sache, die auch benötigt wird, ist, dass Sie die pch-Quell- und Header-Datei erstellen müssen und in der Quelldatei alle Header enthalten, die vorkompiliert werden sollen. Ich habe das 12 Jahre lang mit MFC gemacht, aber ich habe ein paar Minuten gebraucht, um mich daran zu erinnern.


quelle
0

Am saubersten ist es, die vorkompilierte Option als globale Option hinzuzufügen. In der vcxproj-Datei wird dies als angezeigt<PrecompiledHeader>Use</PrecompiledHeader> und nicht für jede einzelne Datei.

Dann müssen Sie die CreateOption zur Datei StdAfx.cpp hinzufügen. Folgendes benutze ich:

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

Dies ist getestet und funktioniert für MSVC 2010 und erstellt eine MyDll.pch-Datei. Es stört mich nicht, welcher Dateiname verwendet wird, daher habe ich mich nicht bemüht, ihn anzugeben.

uncletall
quelle
0

Da die vorkompilierte Header-Option für RC-Dateien nicht funktioniert, musste ich das von Jari bereitgestellte Makro anpassen.

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

Bearbeiten: Die Verwendung dieser vorkompilierten Header reduzierte die Gesamtaufbauzeit meines Hauptprojekts von 4 Minuten 30 Sekunden auf 1 Minute 40 Sekunden. Das ist für mich eine wirklich gute Sache. Im Precompile-Header befinden sich nur Header wie boost / stl / Windows / mfc.

schorsch_76
quelle
-15

Geh nicht mal dorthin. Vorkompilierte Header bedeuten, dass Sie bei jeder Änderung eines der Header alles neu erstellen müssen . Sie haben Glück, wenn Sie ein Build-System haben, das dies erkennt. Meistens schlägt Ihr Build nur fehl, bis Sie feststellen, dass Sie etwas geändert haben, das vorkompiliert wird, und daher eine vollständige Neuerstellung durchführen müssen. Sie können dies größtenteils vermeiden, indem Sie die Header vorkompilieren, von denen Sie absolut sicher sind, dass sie sich nicht ändern. Dann geben Sie jedoch auch einen großen Teil des Geschwindigkeitsgewinns auf.

Das andere Problem ist, dass Ihr Namespace an vielen Stellen, an denen Sie die vorkompilierten Header verwenden würden, mit allen Arten von Symbolen verschmutzt wird, die Sie nicht kennen oder die Sie nicht interessieren.

Dirk Groeneveld
quelle
29
Vorkompilierte Header sind am nützlichsten, wenn sie auf Header verweisen, die sich nicht ändern ... STL, Boost, andere Inhalte von Drittanbietern. Wenn Sie PCH für Ihre eigenen Projektheaderdateien verwenden, verschwenden Sie den größten Teil des Nutzens.
Tom
3
Auch wenn Sie PCH für Ihr eigenes Projekt - Header, der ganze Sinn eines Build - System wie CMake verwenden sind , ist sicherzustellen, dass die Abhängigkeiten werden respektiert. Wenn ich meine .h-Datei (oder eine ihrer Abhängigkeiten) ändere , möchte ich , dass die .pch- Datei neu generiert wird. Wenn ich meine .h-Datei nicht ändere, werde ich möchte ich die .pch nicht neu generieren lassen. Ich denke, die ganze Frage des OP war: Wie kann ich das mit CMake erreichen?
Quuxplusone
1
Vorkompilierte Header sind das beste Tool, um die Kompilierungszeiten zu verkürzen, bis C ++ - Module von allen gängigen Compilern unterstützt werden. Sie lösen ein Problem, das durch die zunehmende Verwendung von Vorlagen und Nur-Header-Bibliotheken immer schlimmer wurde. Bei sachgemäßer Anwendung gibt es keinen Ersatz. Unabhängig davon stellt dies keine Antwort auf die gestellte Frage dar und äußert lediglich eine Meinung. Down- und Delete-Voted.
Unsichtbarer