Respostas:
Há um módulo CMake de terceiros chamado 'Cotire' que automatiza o uso de cabeçalhos pré-compilados para sistemas de compilação baseados em CMake e também oferece suporte a compilações de unidade.
Estou usando a seguinte macro para gerar e usar cabeçalhos pré-compilados:
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 você tenha uma variável $ {MySources} com todos os seus arquivos de origem, o código que deseja usar seria simplesmente
ADD_MSVC_PRECOMPILED_HEADER("precompiled.h" "precompiled.cpp" MySources)
ADD_LIBRARY(MyLibrary ${MySources})
O código ainda funcionaria bem em plataformas não MSVC também. Muito arrumado :)
list( APPEND ... )
externa do fechamento endif()
. Veja o código completo aqui: pastebin.com/84dm5rXZ
/Yu
e /FI
, eles deveriam ser ${PrecompiledHeader}
ou não ${PrecompiledBinary}
.
/YuC:/foo/bar.h
irá forçá-lo a passar o /FpC:/foo/bar.h
sinalizador ou colocar #include <C:/foo/bar.h>
no topo de todos os seus arquivos .cpp como a primeira instrução de inclusão. O MSVC faz uma comparação de string dos #include
argumentos, não verifica se aponta para o mesmo arquivo que foi fornecido /Yu
. Ergo, #include <bar.h>
não funcionará e emitirá erro C2857.
O CMake acaba de ganhar suporte para PCHs, ele deve estar disponível na próxima versão 3.16, com vencimento em 01/10/2019:
https://gitlab.kitware.com/cmake/cmake/merge_requests/3553
target_precompile_headers(<target>
<INTERFACE|PUBLIC|PRIVATE> [header1...]
[<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])
Há uma discussão em andamento sobre o suporte de compartilhamento de PCHs entre destinos: https://gitlab.kitware.com/cmake/cmake/issues/19659
Há algum contexto adicional (motivação, números) disponível em https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/
Aqui está um trecho de código para permitir que você use o cabeçalho pré-compilado para seu projeto. Adicione o seguinte ao seu CMakeLists.txt substituindo myprecompiledheaders
e myproject_SOURCE_FILES
conforme apropriado:
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
foi compilado primeiro? A partir desse snippet, parece que ele será compilado por último, então talvez seja isso que esteja causando o atraso. myprecompiledheader.h
contém apenas os cabeçalhos STL mais comuns que meu código usa.
Acabei usando uma versão adaptada do macro larsm. Usar $ (IntDir) para o caminho pch mantém os cabeçalhos pré-compilados para depuração e versões de lançamento separados.
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})
Adaptado de Dave, mas mais eficiente (define propriedades de destino, não para cada arquivo):
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
em seu exemplo?
se você não quiser reinventar a roda, use apenas Cotire como a resposta principal sugere ou uma mais simples - cmake-pré-compilado-header aqui . Para usá-lo basta incluir o módulo e chamar:
include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )
O CMake 3.16 introduziu suporte para cabeçalhos pré-compilados. Há um novo comando CMake target_precompile_headers
que faz tudo o que você precisa nos bastidores. Consulte sua documentação para obter mais detalhes: https://cmake.org/cmake/help/latest/command/target_precompile_headers.html
"stdafx.h", "stdafx.cpp" - nome do cabeçalho pré-compilado.
Coloque o seguinte abaixo no arquivo cmake root.
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 o seguinte abaixo no arquivo cmake do projeto.
"src" - uma pasta com arquivos de origem.
set_source_files_properties(src/stdafx.cpp
PROPERTIES
COMPILE_FLAGS "/Ycstdafx.h"
)
IMHO, a melhor maneira é definir PCH para todo o projeto, como sugeriu Martjno, combinado com a capacidade de ignorar PCH para algumas fontes, se necessário (por exemplo, fontes geradas):
# 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)
Portanto, se você tiver algum alvo MY_TARGET e uma lista de fontes geradas IGNORE_PCH_SRC_LIST, você simplesmente fará:
SET_TARGET_PRECOMPILED_HEADER(MY_TARGET stdafx.h stdafx.cpp)
IGNORE_PRECOMPILED_HEADER(IGNORE_PCH_SRC_LIST)
Esta abordagem foi testada e funciona perfeitamente.
Bem, quando as compilações levam mais de 10 minutos em uma máquina quad core, toda vez que você altera uma única linha em qualquer um dos arquivos do projeto, é hora de adicionar cabeçalhos pré-compilados para janelas. No * nux, eu apenas usaria o ccache e não me preocuparia com isso.
Eu implementei em meu aplicativo principal e algumas das bibliotecas que ele usa. Funciona muito bem neste ponto. Uma coisa que também é necessária é criar o arquivo de origem e de cabeçalho do pch e incluir no arquivo de origem todos os cabeçalhos que deseja pré-compilados. Fiz isso por 12 anos com o MFC, mas demorei alguns minutos para me lembrar disso.
A maneira mais limpa é adicionar a opção pré-compilada como uma opção global. No arquivo vcxproj, isso aparecerá <PrecompiledHeader>Use</PrecompiledHeader>
e não fará isso para cada arquivo individual.
Em seguida, você precisa adicionar a Create
opção ao StdAfx.cpp. O seguinte é como eu o 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})
Ele foi testado e funciona para o MSVC 2010 e criará um arquivo MyDll.pch, não estou incomodado com o nome do arquivo usado, então não fiz nenhum esforço para especificá-lo.
Como a opção de cabeçalho pré-compilado não funciona para arquivos rc, eu precisei ajustar a macro fornecida 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)
Edit: O uso desses cabeçalhos pré-compilados reduziu o tempo de compilação geral do meu projeto principal de 4min 30s para 1min 40s. Isso é para mim uma coisa muito boa. No cabeçalho de pré-compilação, existem apenas cabeçalhos como boost / stl / Windows / mfc.
Nem vá lá. Cabeçalhos pré-compilados significam que sempre que um dos cabeçalhos muda, você deve reconstruir tudo . Você tem sorte se tiver um sistema de compilação que perceba isso. Mais frequentemente do que nunca, sua construção irá falhar até que você perceba que alterou algo que está sendo pré-compilado e, portanto, precisa fazer uma reconstrução completa. Você pode evitar isso principalmente ao pré-compilar os cabeçalhos que você tem certeza absoluta de que não vão mudar, mas também está desistindo de uma grande parte do ganho de velocidade.
O outro problema é que seu namespace fica poluído com todos os tipos de símbolos que você não conhece ou com os quais você não se importa em muitos lugares onde você usaria os cabeçalhos pré-compilados.