Usando encabezados precompilados con CMake

104

He visto algunas publicaciones (antiguas) en la red sobre cómo piratear algo de soporte para encabezados precompilados en CMake. Todos parecen un poco dispersos y cada uno tiene su propia forma de hacerlo. ¿Cuál es la mejor forma de hacerlo actualmente?

Glutinoso
fuente

Respuestas:

79

Existe un módulo CMake de terceros llamado 'Cotire' que automatiza el uso de encabezados precompilados para sistemas de compilación basados ​​en CMake y también admite compilaciones unitarias.

sakra
fuente
1
He creado un conjunto de macros que envuelven la funcionalidad cotire (encabezados precompilados y compilaciones unitarias) aquí para facilitar su uso
onqtam
2
@onqtam, ¿cómo es eso más fácil? Es tan gigante que ni siquiera veo cómo simplemente precompilarlo trabajando con cmake y gcc
Pavel P
5
CMake 3.16 introdujo soporte integrado para encabezados precompilados. Vea mi respuesta stackoverflow.com/a/59514029/2799037 ¡ Ya no se necesitan módulos de terceros!
usr1234567
34

Estoy usando la siguiente macro para generar y usar encabezados precompilados:

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)

Digamos que tiene una variable $ {MySources} con todos sus archivos fuente, el código que le gustaría usar sería simplemente

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

El código también funcionaría bien en plataformas que no sean MSVC. Con buena pinta :)

larsmoa
fuente
2
Esta macro tiene 1 defecto. Si el generador no está basado en MSVC, la fuente precompilada no se agregará a la lista de fuentes. Entonces mi modificación simplemente mueve el list( APPEND ... )exterior del cierre endif(). Vea el código completo aquí: pastebin.com/84dm5rXZ
void.pointer
1
@RobertDailey: Esto es realmente deliberado; no quiero compilar el archivo fuente precompilado cuando no utilizo encabezados precompilados; de todos modos, no debería definir ningún símbolo.
larsmoa
1
@Iarsam Por favor, corrija los argumentos /Yuy /FI, deberían serlo ${PrecompiledHeader}y no ${PrecompiledBinary}.
Mourad
¿Puede explicar por qué necesitamos los indicadores "/ Fp" y "/ FI"? Según msdn.microsoft.com/en-us/library/z0atkd6c.aspx, el uso de "/ Fp" no es obligatorio. Sin embargo, si elimino esas banderas de su macro, no se establece ningún pch.
Vram Vardanian
2
Sin embargo, tenga en cuenta que el argumento de / Yu se toma muy literalmente. Por ejemplo, /YuC:/foo/bar.hlo obligará a pasar la /FpC:/foo/bar.hbandera o colocarlo #include <C:/foo/bar.h>en la parte superior de todos sus archivos .cpp como la primera declaración de inclusión. MSVC hace una comparación de cadenas de los #includeargumentos, no verifica si apunta al mismo archivo que se le dio /Yu. Ergo, #include <bar.h>no funcionará y emitirá el error C2857.
Manuzor
21

CMake acaba de obtener soporte para PCH, debería estar disponible en la próxima versión 3.16, con fecha de 2019-10-01:

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

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

Existe un debate en curso sobre el apoyo a compartir PCH entre objetivos: https://gitlab.kitware.com/cmake/cmake/issues/19659

Hay un contexto adicional (motivación, números) disponible en https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/

Janisozaur
fuente
2
Aquí está el enlace a la documentación oficial de CMake: cmake.org/cmake/help/latest/command/…
Alex Che
2
Hay una publicación del equipo de MSVC sobre cómo averiguar qué encabezados incluir en PCH: devblogs.microsoft.com/cppblog/…
janisozaur
19

Aquí hay un fragmento de código que le permite utilizar un encabezado precompilado para su proyecto. Agregue lo siguiente a su CMakeLists.txt reemplazando myprecompiledheadersy myproject_SOURCE_FILESsegún corresponda:

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
fuente
Gracias; Usaré esto como guía para crear uno para GCC (si puedo). Publicaré mi respuesta tan pronto como termine. =]
extraño
@ Jayen, no; Finalmente abandoné el proyecto y nunca volví a tener problemas con C ++, básicamente.
extraño
¿Es posible configurar PCH para todo el proyecto? Porque no es posible obtener una lista de archivos generados automáticamente en cmake with set( CMAKE_AUTOMOC ON ).
Dmitry Sazonov
He usado su solución, pero desafortunadamente el tiempo de compilación pasó de 2'10 "a 2'40", para ~ 130 archivos. ¿Tengo que asegurarme de que myprecompiledheader.cppse compile primero? A partir de este fragmento, parece que se compilará en último lugar, por lo que quizás eso sea lo que podría estar causando el retraso. myprecompiledheader.hcontiene solo los encabezados STL más comunes que usa mi código.
Grim Fandango
13

Terminé usando una versión adaptada de larsm macro. El uso de $ (IntDir) para la ruta pch mantiene separados los encabezados precompilados para depurar y publicar versiones.

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
fuente
2
La abstracción $ {IntDir} es útil. Gracias.
Gopalakrishna Palem
12

Adaptado de Dave, pero más eficiente (establece propiedades de destino, no para cada archivo):

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
fuente
2
Solía ​​usar esta solución, pero solo funciona si el proyecto solo contiene archivos c ++. Dado que COMPILE_FLAGS se aplica a todos los archivos fuente, también se aplicará a los archivos c (por ejemplo, los generados por MIDL), a los que no les gustará c ++ PCH. Cuando use la solución de Dave, puede usar get_source_file_property (_language $ {src_file} LANGUAGE), y solo establecer las banderas del compilador si es realmente un archivo CXX.
Andreas Haferburg
Es bueno tener la flexibilidad de la otra solución en mi bolsillo trasero, pero esta es la que estaba buscando, ¡gracias!
kylewm
Buena respuesta. Tenga cuidado con los paréntesis que faltan para set_source_files_properties.
Arnaud
2
Puede desactivarse selectivamente para archivos individuales con / Y- usando set_source_files_properties
mlt
¿Qué hay abcen tu ejemplo?
Sandburg
7

si no desea reinventar la rueda, simplemente use Cotire como sugiere la respuesta principal o una más simple: cmake-precompiled-header aquí . Para usarlo solo incluye el módulo y llama:

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

CMake 3.16 introdujo soporte para encabezados precompilados. Hay un nuevo comando CMake target_precompile_headersque hace todo lo que necesita bajo el capó. Consulte su documentación para obtener más detalles: https://cmake.org/cmake/help/latest/command/target_precompile_headers.html

usr1234567
fuente
2
Esta respuesta no agrega nada nuevo a la respuesta de janisozaur, publicada medio año antes, excepto el enlace a la documentación oficial final, que probablemente debería agregarse mejor como comentario a esa respuesta.
Alex Che
4

Un ejemplo de uso de encabezado precompilado con cmake y Visual Studio 2015

"stdafx.h", "stdafx.cpp": nombre de encabezado precompilado.

Coloque lo siguiente a continuación en el archivo cmake raíz.

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

Ponga lo siguiente a continuación en el archivo cmake del proyecto.

"src": una carpeta con archivos de origen.

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

En mi humilde opinión, la mejor manera es configurar PCH para todo el proyecto, como sugirió martjno, combinado con la capacidad de ignorar PCH para algunas fuentes si es necesario (por ejemplo, fuentes generadas):

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

Entonces, si tiene un MY_TARGET objetivo y una lista de fuentes generadas IGNORE_PCH_SRC_LIST, simplemente hará:

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

Este enfoque está probado y funciona perfectamente.

Vram Vardanian
fuente
0

Bueno, cuando las compilaciones toman más de 10 minutos en una máquina de cuatro núcleos, cada vez que cambia una sola línea en cualquiera de los archivos del proyecto, le indica que es hora de agregar encabezados precompilados para Windows. En * nux solo usaría ccache y no me preocuparía por eso.

Lo he implementado en mi aplicación principal y en algunas de las bibliotecas que utiliza. Funciona muy bien en este punto. Una cosa que también es necesaria es que debe crear el archivo fuente y de encabezado pch y en el archivo fuente incluir todos los encabezados que desea que se compilen previamente. Hice esto durante 12 años con MFC, pero me tomó unos minutos recordarlo ...


fuente
0

La forma más limpia es agregar la opción precompilada como una opción global. En el archivo vcxproj esto se mostrará como<PrecompiledHeader>Use</PrecompiledHeader> y no lo hará para cada archivo individual.

Luego, debe agregar la Createopción a StdAfx.cpp. Lo siguiente es cómo lo uso:

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

Esto está probado y funciona para MSVC 2010 y creará un archivo MyDll.pch, no me molesta qué nombre de archivo se usa, así que no hice ningún esfuerzo para especificarlo.

tío
fuente
0

Como la opción de encabezado precompilado no funciona para archivos rc, necesitaba ajustar la macro proporcionada por 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)

Editar: El uso de estos encabezados precompilados redujo el tiempo de compilación general de mi proyecto principal de 4min 30s a 1min 40s. Esto es para mí algo realmente bueno. En el encabezado de precompilación solo hay encabezados como boost / stl / Windows / mfc.

schorsch_76
fuente
-15

Ni siquiera vayas allí. Los encabezados precompilados significan que cada vez que cambia uno de los encabezados, debe reconstruir todo . Tienes suerte si tienes un sistema de construcción que se dé cuenta de esto. La mayoría de las veces, su compilación fallará hasta que se dé cuenta de que cambió algo que se está compilando previamente y, por lo tanto, debe realizar una reconstrucción completa. Puede evitar esto principalmente compilando los encabezados de los que está absolutamente seguro de que no cambiarán, pero también está renunciando a una gran parte de la ganancia de velocidad.

El otro problema es que su espacio de nombres se contamina con todo tipo de símbolos que no conoce o que no le interesan en muchos lugares donde estaría usando los encabezados precompilados.

Dirk Groeneveld
fuente
29
Los encabezados precompilados son más útiles cuando hacen referencia a encabezados que no cambian ... STL, Boost, otras cosas de terceros. Si está utilizando PCH para sus propios archivos de encabezado de proyecto, está desperdiciando la mayor parte del beneficio.
Tom
3
Incluso si está utilizando PCH para los encabezados de su propio proyecto, el objetivo de un sistema de compilación como CMake es asegurarse de que se respeten las dependencias . Si cambio mi archivo .h (o una de sus dependencias), quiero que se regenere el .pch. Si no cambio mi archivo .h, no quiero que se regenere el .pch. Creo que toda la pregunta del OP era: ¿Cómo hago para que esto suceda, usando CMake?
Quuxplusone
1
Los encabezados precompilados son la mejor herramienta para reducir los tiempos de compilación hasta que los módulos C ++ sean compatibles con todos los compiladores convencionales. Resuelven un problema que empeoró con el uso cada vez mayor de plantillas y bibliotecas de solo encabezado. Cuando se usa correctamente, no hay sustituto. Independientemente, esto no constituye una respuesta a la pregunta que se hace, y simplemente expresa una opinión. Votar en contra y eliminar.
Inspectable