Відповіді:
Існує сторонній модуль CMake під назвою "Cotire", який автоматизує використання попередньо складених заголовків для систем побудови на основі CMake, а також підтримує побудову єдності.
Я використовую наступний макрос для створення та використання попередньо складених заголовків:
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)
Скажімо, у вас є змінна $ {MySources} з усіма вихідними файлами, код, який ви хочете використовувати, буде просто
ADD_MSVC_PRECOMPILED_HEADER("precompiled.h" "precompiled.cpp" MySources)
ADD_LIBRARY(MyLibrary ${MySources})
Код все ще буде функціонувати чудово і на не-MSVC платформах. Досить акуратно :)
list( APPEND ... )
зовнішнє закриття endif()
. Дивіться повний код тут: pastebin.com/84dm5rXZ
/Yu
і /FI
аргументи, вони повинні бути ${PrecompiledHeader}
і ні ${PrecompiledBinary}
.
/YuC:/foo/bar.h
ви змусите вас або пропустити /FpC:/foo/bar.h
прапор, або поставити #include <C:/foo/bar.h>
у верхній частині всіх своїх .cpp-файлів як перший оператор include. MSVC робить рядкове порівняння #include
аргументів, воно не перевіряє, чи вказує він на той самий файл, що і дано /Yu
. Ergo, #include <bar.h>
не буде працювати і випромінює помилку C2857.
CMake щойно отримав підтримку PCH, він повинен бути доступний у майбутньому випуску 3.16, до 2019-10-01:
https://gitlab.kitware.com/cmake/cmake/merge_requests/3553
target_precompile_headers(<target>
<INTERFACE|PUBLIC|PRIVATE> [header1...]
[<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])
Триває дискусія щодо підтримки обміну PCHs між цілями: https://gitlab.kitware.com/cmake/cmake/isissue/19659
Існує деякий додатковий контекст (мотивація, номери), доступний на https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/
Ось фрагмент коду, який дозволяє використовувати попередньо складений заголовок для вашого проекту. Додайте наступний рядок в ваш CMakeLists.txt замінює myprecompiledheaders
і 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)
with set( CMAKE_AUTOMOC ON )
.
myprecompiledheader.cpp
це скомпільовано спочатку? З цього фрагмента виглядає, що він буде складений останнім, тому, можливо, саме це може спричинити затримку. myprecompiledheader.h
містить лише найпоширеніші заголовки STL, якими використовується мій код.
Я в кінцевому рахунку використовував адаптовану версію макросу larsm. Використання $ (IntDir) для pch path зберігає попередньо складені заголовки для налагодження та версії версій окремо.
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})
Адаптований з Дейва, але більш ефективний (встановлює цільові властивості, не для кожного файлу):
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
у вашому прикладі?
якщо ви не хочете винаходити колесо, просто використовуйте або Cotire, як підказує головна відповідь, або більш простий - cmake-попередньо складений заголовок тут . Для його використання просто включіть модуль та зателефонуйте:
include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )
CMake 3.16 представив підтримку попередньо складених заголовків. Існує нова команда CMake, target_precompile_headers
яка виконує все необхідне під кришкою. Детальнішу інформацію див. У його документації: https://cmake.org/cmake/help/latest/command/target_precompile_headers.html
"stdafx.h", "stdafx.cpp" - попередньо складене ім'я заголовка.
Подайте наступне нижче у файл кореневого 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()
Помістіть наступне нижче у файл cmake проекту.
"src" - папка з вихідними файлами.
set_source_files_properties(src/stdafx.cpp
PROPERTIES
COMPILE_FLAGS "/Ycstdafx.h"
)
IMHO найкращим способом є встановлення PCH для всього проекту, як запропонував martjno, поєднаний із можливістю ігнорування PCH для деяких джерел, якщо це необхідно (наприклад, згенеровані джерела):
# 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)
Отже, якщо у вас є якийсь цільовий MY_TARGET і список створених джерел IGNORE_PCH_SRC_LIST, ви просто зробите:
SET_TARGET_PRECOMPILED_HEADER(MY_TARGET stdafx.h stdafx.cpp)
IGNORE_PRECOMPILED_HEADER(IGNORE_PCH_SRC_LIST)
Цей підхід перевірений і працює чудово.
Добре, коли збірки займають 10+ хвилин на чотирьохядерній машині щоразу, коли ви змінюєте один рядок у будь-якому з файлів проекту, це повідомляє вам про час додавати попередньо складені заголовки для Windows. На * nux я б просто використовував ccache і не хвилювався з цього приводу.
Я реалізував у своєму головному додатку та декількох бібліотеках, які він використовує. Це чудово працює до цього моменту. Одне, що також потрібно - це створити файл pch-джерела та заголовок, а у вихідний файл включити всі заголовки, які ви хочете попередньо скласти. Я робив це протягом 12 років з MFC, але мені знадобилося кілька хвилин, щоб згадати про це.
Найчистіший спосіб - додати попередньо складений варіант як глобальний варіант. У файлі vcxproj це відображатиметься як <PrecompiledHeader>Use</PrecompiledHeader>
і не робити цього для кожного окремого файлу.
Потім вам потрібно додати Create
параметр до StdAfx.cpp. Далі, як я ним користуюся:
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})
Це тестується і працює для MSVC 2010 і створить файл MyDll.pch, я не турбуюсь, яке ім'я файлу використовується, тому я не доклав жодних зусиль, щоб його вказати.
Оскільки попередньо компільований варіант заголовка не працює для rc-файлів, мені потрібно було відрегулювати макрос, наданий 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)
Редагувати: Використання цих попередньо складених заголовків скоротило загальний час моєго основного проекту з 4 хв 30 до 1 х 40 х. Це для мене дійсно гарна річ. У заголовку прекомпіляції є лише заголовки, такі як boost / stl / Windows / mfc.
Навіть не туди. Попередньо складені заголовки означають, що кожного разу, коли один із заголовків змінюється, вам доведеться все перебудовувати . Вам пощастило, якщо у вас є система побудови, яка це усвідомлює. Частіше, ніж ніколи, ваша збірка буде просто невдалою, поки ви не зрозумієте, що ви змінили те, що попередньо складено, і тому вам потрібно зробити повне відновлення. Цього можна уникнути здебільшого, попередньо склавши заголовки, які ви абсолютно позитивні, не зміниться, але тоді ви відмовитесь також від значної частини збільшення швидкості.
Інша проблема полягає в тому, що ваш простір імен забруднюється всілякими символами, які ви не знаєте і не цікавите в багатьох місцях, де ви б використовували попередньо складені заголовки.