Я видел несколько (старых) сообщений в сети о том, как совместить некоторую поддержку предварительно скомпилированных заголовков в CMake. Все они кажутся немного разрозненными, и у каждого свой способ делать это. Как лучше всего это сделать в настоящее время?





Даже не ходи туда. Предварительно скомпилированные заголовки означают, что всякий раз, когда один из заголовков изменяется, вы должны перестраивать все. Вам повезло, если у вас есть система сборки, которая это понимает. Чаще, чем никогда, ваша сборка просто терпит неудачу, пока вы не поймете, что вы изменили что-то, что предварительно скомпилировано, и поэтому вам нужно выполнить полную перестройку. Вы можете избежать этого в основном, предварительно скомпилировав заголовки, которые, как вы абсолютно уверены, не изменятся, но тогда вы также отказываетесь от значительной части прироста скорости.
Другая проблема заключается в том, что ваше пространство имен загрязняется всевозможными символами, которые вы не знаете или не заботитесь во многих местах, где вы использовали бы предварительно скомпилированные заголовки.
Даже если вы используете PCH для заголовков своего собственного проекта, весь смысл системы сборки, такой как CMake, состоит в том, чтобы убедиться, что зависимости являются соблюдаются. Если я изменю свой файл .h (или одну из его зависимостей), я хочу, чтобы восстановить .pch. Если я не изменю свой файл .h, я не хочу восстановить .pch. Я думаю, что весь вопрос OP заключался в следующем: как мне добиться этого, используя CMake?
Предварительно скомпилированные заголовки - лучший инструмент для сокращения времени компиляции до тех пор, пока модули C++ не будут поддерживаться всеми основными компиляторами. Они решают проблему, которая только усугубилась из-за все более широкого использования шаблонов и библиотек только для заголовков. При правильном использовании заменителя нет. Тем не менее, это не является ответом на заданный вопрос, а просто выражает мнение. Проголосовали "против" и "за удаление".
Вот фрагмент кода, позволяющий использовать предварительно скомпилированный заголовок для вашего проекта.
Добавьте следующее в свой 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)
Спасибо; Я буду использовать это как руководство для создания одного из GCC (если смогу). Я отправлю свой ответ, как только закончу. знак равно
@ Джейен, нет; В конце концов я отказался от проекта и больше никогда не сталкивался с проблемами C++.
Можно ли настроить PCH на весь проект? Потому что невозможно получить список автоматически сгенерированных файлов в cmake with set( CMAKE_AUTOMOC ON ).
Я использовал ваше решение, но, к сожалению, время компиляции увеличилось с 2'10 "до 2'40" для ~ 130 файлов. Должен ли я сначала убедиться, что myprecompiledheader.cpp скомпилирован? Судя по этому фрагменту, он будет скомпилирован последним, так что, возможно, именно это может быть причиной задержки. myprecompiledheader.h содержит только самые распространенные заголовки STL, которые использует мой код.
Хорошо, когда сборка занимает 10+ минут на четырехъядерном компьютере каждый раз, когда вы меняете одну строку в любом из файлов проекта, он сообщает вам, что пора добавить предварительно скомпилированные заголовки для Windows. В * nux я бы просто использовал ccache и не беспокоился об этом.
Я реализовал свое основное приложение и несколько библиотек, которые оно использует. На данный момент он отлично работает. Одна вещь, которая также необходима, - это создать исходный файл pch и файл заголовка, а в исходный файл включить все заголовки, которые вы хотите предварительно скомпилировать. Я делал это в течение 12 лет с MFC, но мне потребовалось несколько минут, чтобы вспомнить это ..
Я использую следующий макрос для создания и использования предварительно скомпилированных заголовков:
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. Довольно аккуратно :)
У этого макроса 1 недостаток. Если генератор не основан на MSVC, предварительно скомпилированный исходный код не будет добавлен в список источников. Так что моя модификация просто перемещает list( APPEND ... ) за пределы закрывающего endif (). Смотрите полный код здесь: pastebin.com/84dm5rXZ
@RobertDailey: На самом деле это сделано намеренно - я не хочу компилировать предварительно скомпилированный исходный файл, если не используются предварительно скомпилированные заголовки - он в любом случае не должен определять никаких символов.
@Iarsam Исправьте аргументы /Yu и /FI, они должны быть ${PrecompiledHeader}, а не ${PrecompiledBinary}.
Вы можете объяснить, зачем нам флаги "/ Fp" и "/ FI"? Согласно msdn.microsoft.com/en-us/library/z0atkd6c.aspx использование «/ Fp» не обязательно. Однако, если я вырежу эти флаги из вашего макроса, pch не будет установлен.
Однако имейте в виду, что аргумент в пользу / Yu понимается очень буквально. Например. /YuC:/foo/bar.h заставит вас либо передать флаг /FpC:/foo/bar.h, либо поместить #include <C:/foo/bar.h> в начало всех ваших файлов .cpp в качестве первого оператора include. MSVC выполняет строковое сравнение аргументов #include, но не проверяет, указывает ли он на тот же файл, что и /Yu. Ergo, #include <bar.h> не будет работать и выдаст ошибку C2857.
В итоге я использовал адаптированную версию макроса larsm. Использование $ (IntDir) в качестве пути к pch позволяет разделить предварительно скомпилированные заголовки для отладочных и выпускных сборок.
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})
Абстракция $ {IntDir} полезна. Спасибо.
Адаптировано от Дэйва, но более эффективно (задает целевые свойства, а не для каждого файла):
if (MSVC)
set_target_properties(abc PROPERTIES COMPILE_FLAGS "/Yustd.h")
set_source_files_properties(std.cpp PROPERTIES COMPILE_FLAGS "/Ycstd.h")
endif (MSVC)
Раньше я использовал это решение, но оно работает, только если проект содержит только файлы C++. Поскольку COMPILE_FLAGS применяется ко всем исходным файлам, он также будет применен к файлам c (например, сгенерированным MIDL), которым не понравится C++ PCH. При использовании решения Дейва вы можете использовать get_source_file_property (_language $ {src_file} LANGUAGE) и устанавливать флаги компилятора только в том случае, если это действительно файл CXX.
Приятно иметь гибкость другого решения в моем заднем кармане, но это то, что я искал, спасибо!
Хороший ответ. Остерегайтесь отсутствующих скобок для set_source_files_properties.
Его можно выборочно отключить для отдельных файлов с / Y- с помощью set_source_files_properties
Что такое abc в вашем примере?
abc - целевой проект, в котором нужно установить флаги
Существует сторонний модуль CMake с именем Cotire, который автоматизирует использование предварительно скомпилированных заголовков для систем сборки на основе CMake, а также поддерживает сборки Unity.
Я создал набор макросов, которые обертывают общую функциональность (предварительно скомпилированные заголовки и сборки единства) здесь для упрощения использования
@onqtam, как это проще, он настолько велик, что я даже не вижу, как просто предварительно скомпилировать работу с cmake и gcc
CMake 3.16 представил встроенную поддержку предварительно скомпилированных заголовков. Смотрите мой ответ stackoverflow.com/a/59514029/2799037 Больше нет необходимости в сторонних модулях!
Самый простой способ - добавить предварительно скомпилированный параметр в качестве глобального параметра. В файле 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, меня не беспокоит, какое имя файла используется, поэтому я не пытался его указать.
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)
Этот подход проверен и отлично работает.
"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"
)
Поскольку параметр предварительно скомпилированного заголовка не работает для файлов 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.
если вы не хотите изобретать велосипед, просто используйте либо Cotire, как предлагает верхний ответ, либо более простой - cmake-предварительно скомпилированный заголовокздесь. Чтобы использовать его, просто включите модуль и вызовите:
include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )
CMake только что получил поддержку PCH, он должен быть доступен в следующем выпуске 3.16, который выйдет 01.10.2019:
https://gitlab.kitware.com/cmake/cmake/merge_requests/3553
target_precompile_headers(<target>
<INTERFACE|PUBLIC|PRIVATE> [header1...]
[<INTERFACE|PUBLIC|PRIVATE> [header2...] ...])
Продолжается обсуждение поддержки совместного использования PCH между целями: https://gitlab.kitware.com/cmake/cmake/issues/19659
Дополнительный контекст (мотивация, цифры) доступен на https://blog.qt.io/blog/2019/08/01/precompiled-headers-and-unity-jumbo-builds-in-upcoming-cmake/
Вот ссылка на официальную документацию CMake: cmake.org/cmake/help/latest/command/…
Есть сообщение от команды MSVC о том, какие заголовки включать в PCH: devblogs.microsoft.com/cppblog/…
CMake 3.16 представил поддержку предварительно скомпилированных заголовков. Есть новая команда CMake target_precompile_headers, которая делает все, что вам нужно, под капотом. Дополнительную информацию см. В документации: https://cmake.org/cmake/help/latest/command/target_precompile_headers.html
Этот ответ не добавляет ничего нового к ответу janisozaur, опубликовал полгода назад, кроме ссылки на окончательную официальную документацию, которую, вероятно, лучше добавить в качестве комментария к этому ответу.
Предварительно скомпилированные заголовки наиболее полезны, когда они ссылаются на заголовки, которые изменяет не ... STL, Boost и другие сторонние материалы. Если вы используете PCH для файлов заголовков своих собственных проектов, вы теряете большую часть преимуществ.