c++ - Usar encabezados precompilados con CMake
visual-studio gcc (12)
He visto algunas (antiguas) publicaciones en la red sobre la piratería de algún soporte para encabezados precompilados en CMake. Todos parecen un poco omnipresentes y todos tienen su propia manera de hacerlo. ¿Cuál es la mejor manera de hacerlo actualmente?
Un ejemplo de uso de encabezado precompilado con cmake y Visual Studio 2015
"stdafx.h", "stdafx.cpp" - nombre del encabezado precompilado.
Coloque lo siguiente a continuación en el archivo raíz cmake.
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()
Coloque lo siguiente a continuación en el archivo cmake del proyecto.
"src" - una carpeta con archivos fuente.
set_source_files_properties(src/stdafx.cpp
PROPERTIES
COMPILE_FLAGS "/Ycstdafx.h"
)
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)
Aquí hay un fragmento de código que le permite usar el encabezado precompilado para su proyecto. Agregue lo siguiente a su CMakeLists.txt reemplazando myprecompiledheaders
y myproject_SOURCE_FILES
segú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)
Bueno, cuando las compilaciones demoran más de 10 minutos en una máquina de cuatro núcleos cada vez que cambias una sola línea en cualquiera de los archivos del proyecto, te dice que es hora de agregar encabezados precompilados para Windows. En * nux, simplemente usaría ccache y no me preocuparía.
Lo he implementado en mi aplicación principal y en algunas de las bibliotecas que usa. Funciona muy bien hasta este punto. Una cosa que también se necesita es crear el archivo fuente y encabezado pch y en el archivo fuente incluir todos los encabezados que desea precompilar. Hice esto por 12 años con MFC, pero me tomó unos minutos recordar que ...
Como la opción del encabezado precompilado no funciona para los 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 total de compilación de mi proyecto principal de 4 minutos y 30 minutos a 1 minuto 40. Esto es para mí algo realmente bueno. En el encabezado de precompilación solo hay encabezados como boost / stl / Windows / mfc.
En mi humilde opinión, la mejor manera es establecer 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)
Por lo tanto, si tiene algún objetivo MY_TARGET y una lista de fuentes generadas IGNORE_PCH_SRC_LIST, simplemente lo hará:
SET_TARGET_PRECOMPILED_HEADER(MY_TARGET stdafx.h stdafx.cpp)
IGNORE_PRECOMPILED_HEADER(IGNORE_PCH_SRC_LIST)
Este enfoque se prueba y funciona perfectamente.
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)
Supongamos que tiene una variable $ {MySources} con todos sus archivos de origen, el código que desea usar sería simplemente ser
ADD_MSVC_PRECOMPILED_HEADER("precompiled.h" "precompiled.cpp" MySources)
ADD_LIBRARY(MyLibrary ${MySources})
El código aún funcionaría bien en plataformas que no sean de MSVC. Con buena pinta :)
Hay 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.
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 hará esto para cada archivo individual.
Luego debe agregar la opción Create
a StdAfx.cpp. El 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 utiliza, así que no hice ningún esfuerzo para especificarlo.
Ni siquiera vayas allí. Los encabezados precompilados significan que cada vez que uno de los encabezados cambia, debe reconstruir todo . Tienes suerte si tienes un sistema de compilación que se da cuenta de esto. Con más frecuencia que nunca, su compilación simplemente fallará hasta que se dé cuenta de que ha cambiado algo que está siendo precompilado y, por lo tanto, debe realizar una reconstrucción completa. Puede evitar esto principalmente precompilando los encabezados 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 tu espacio de nombres se contamina con todo tipo de símbolos que no conoces o te importan en muchos lugares donde estarías usando los encabezados precompilados.
Terminé usando una versión adaptada de larsm macro. El uso de $ (IntDir) para pch path mantiene los encabezados precompilados para las versiones de depuración y liberación por separado.
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})
si no quiere reinventar la rueda, simplemente use Cotire como sugiere la respuesta superior o una más simple: cmake-precompiled-header here . 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 )