Vooraf gecompileerde headers gebruiken met CMake

Ik heb een paar (oude) berichten op het ‘net gezien over het samen hacken van ondersteuning voor vooraf gecompileerde headers in CMake. Ze lijken allemaal een beetje overal en iedereen heeft zijn eigen manier om het te doen. Wat is momenteel de beste manier om dit te doen?


Antwoord 1, autoriteit 100%

Er is een CMake-module van derden genaamd ‘Cotire’die het gebruik van voorgecompileerde headers voor CMake automatiseert gebaseerde build-systemen en ondersteunt ook unity-builds.


Antwoord 2, autoriteit 44%

CMake heeft zojuist ondersteuning gekregen voor PCH’s (vooraf gecompileerde headers), het zou beschikbaar moeten zijn in de komende 3.16-release, gepland voor 01-10-2019:

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

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

Er is een voortdurende discussie over het ondersteunen van het delen van PCH’s tussen doelen: https://gitlab.kitware .com/cmake/cmake/issues/19659

Er is wat extra context (motivatie, cijfers) beschikbaar op https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/


Antwoord 3, autoriteit 42%

Ik gebruik de volgende macro om vooraf gecompileerde headers te genereren en te gebruiken:

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)

Stel dat je een variabele ${MySources} hebt met al je bronbestanden, de code die je zou willen gebruiken zou zijn:

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

De code zou ook prima werken op niet-MSVC-platforms. Best netjes 🙂


Antwoord 4, autoriteit 25%

Hier is een codefragment waarmee u een voorgecompileerde koptekst voor uw project kunt gebruiken.
Voeg het volgende toe aan uw CMakeLists.txt en vervang waar nodig myprecompiledheadersen myproject_SOURCE_FILES:

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)

Antwoord 5, autoriteit 16%

Ik heb uiteindelijk een aangepaste versie van larsm macro gebruikt. Door $(IntDir) voor pch-pad te gebruiken, blijven vooraf gecompileerde headers voor debug- en release-builds gescheiden.

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

Antwoord 6, autoriteit 15%

Aangepast van Dave, maar efficiënter (stelt doeleigenschappen in, niet voor elk bestand):

if (MSVC)
   set_target_properties(abc PROPERTIES COMPILE_FLAGS "/Yustd.h")
   set_source_files_properties(std.cpp PROPERTIES COMPILE_FLAGS "/Ycstd.h")
endif(MSVC)

Antwoord 7, autoriteit 9%

als je het wiel niet opnieuw wilt uitvinden, gebruik dan ofwel Cotire zoals het bovenste antwoord suggereert, of een eenvoudiger antwoord – cmake-precompiled-headerhier. Om het te gebruiken, voegt u gewoon de module toe en roept u:

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

Antwoord 8, autoriteit 5%

Een voorbeeld van gebruik van een voorgecompileerde header met cmake en Visual Studio 2015

“stdafx.h”, “stdafx.cpp” – voorgecompileerde headernaam.

Zet het volgende hieronder in het root cmake-bestand.

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

Zet het volgende hieronder in het cmake-bestand van het project.

“src” – een map met bronbestanden.

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

Antwoord 9, autoriteit 4%

IMHO is de beste manier om PCH in te stellen voor het hele project, zoals martjno suggereerde, gecombineerd met de mogelijkheid om PCH voor sommige bronnen te negeren indien nodig (bijv. gegenereerde bronnen):

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

Dus, als je een doel MY_TARGET hebt, en een lijst met gegenereerde bronnen IGNORE_PCH_SRC_LIST, doe je gewoon:

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

Deze aanpak is getest en werkt perfect.


Antwoord 10

Als het bouwen meer dan 10 minuten duurt op een quad core-machine, elke keer dat je een enkele regel in een van de projectbestanden wijzigt, vertelt het je dat het tijd is om vooraf gecompileerde headers voor Windows toe te voegen. Op *nux zou ik gewoon ccache gebruiken en me daar geen zorgen over maken.

Ik heb het geïmplementeerd in mijn hoofdtoepassing en een paar van de bibliotheken die het gebruikt. Het werkt tot nu toe uitstekend. Een ding dat ook nodig is, is dat je het pch-bron- en headerbestand moet maken en in het bronbestand alle headers moet opnemen die je wilt voorcompileren. Ik heb dit 12 jaar gedaan met MFC, maar het kostte me een paar minuten om me dat te herinneren..


Antwoord 11

De schoonste manier is om de vooraf gecompileerde optie toe te voegen als een algemene optie. In het vcxproj-bestand wordt dit weergegeven als <PrecompiledHeader>Use</PrecompiledHeader>en niet voor elk afzonderlijk bestand.

Vervolgens moet u de optie Createtoevoegen aan de StdAfx.cpp. Het volgende is hoe ik het gebruik:

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

Dit is getest en werkt voor MSVC 2010 en zal een MyDll.pch-bestand maken. Het maakt me niet uit welke bestandsnaam wordt gebruikt, dus ik heb geen moeite gedaan om het op te geven.


Antwoord 12

Omdat de voorgecompileerde header-optie niet werkt voor rc-bestanden, moest ik de door jari geleverde macro aanpassen.

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

Bewerken: het gebruik van deze voorgecompileerde headers verminderde de totale bouwtijd van mijn hoofdproject van 4 min. 30s tot 1 min. 40s. Dit is voor mij echt een goede zaak. In de precompileerheader zijn alleen headers zoals boost/stl/Windows/mfc.


Antwoord 13

Ga daar niet eens heen. Voorgecompileerde headers betekenen dat wanneer een van de headers verandert, u allesopnieuw moet opbouwen. Je hebt geluk als je een bouwsysteem hebt dat dit realiseert. Vaker dan nooit zal je build gewoon mislukken totdat je je realiseert dat je iets hebt gewijzigd dat vooraf wordt gecompileerd, en daarom moet je een volledige herbouw uitvoeren. Je kunt dit meestal voorkomen door de headers vooraf te compileren waarvan je absoluut zeker weet dat ze niet zullen veranderen, maar dan geef je ook een groot deel van de snelheidswinst op.

Het andere probleem is dat je naamruimte vervuild raakt met allerlei symbolen die je niet kent of waar je niet om geeft op veel plaatsen waar je de voorgecompileerde headers zou gebruiken.

Other episodes