Antworten:
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.
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 :)
list( APPEND ... )
Außenseite des Schließens endif()
. Gesamten Code hier: pastebin.com/84dm5rXZ
/Yu
und /FI
Argumente, sie sollten sein ${PrecompiledHeader}
und nicht ${PrecompiledBinary}
.
/YuC:/foo/bar.h
Zum Beispiel werden Sie gezwungen, entweder das /FpC:/foo/bar.h
Flag 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 #include
Argumente 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.
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.
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 myprecompiledheaders
und ersetzen Sie sie myproject_SOURCE_FILES
entsprechend:
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
das zuerst kompiliert wird? Aus diesem Snippet geht hervor, dass es zuletzt kompiliert wird. Vielleicht ist es das, was die Verzögerung verursacht. myprecompiledheader.h
enthält nur die häufigsten STL-Header, die mein Code verwendet.
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})
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)
abc
in deinem Beispiel?
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 )
CMake 3.16 führte die Unterstützung für vorkompilierte Header ein. Es gibt einen neuen CMake-Befehl, target_precompile_headers
der 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
"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"
)
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.
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.
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 Create
Option 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.
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.
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.