Использование предварительно скомпилированных заголовков с CMake

104

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

Клейкий
источник

Ответы:

79

Существует сторонний модуль CMake под названием Cotire, который автоматизирует использование предварительно скомпилированных заголовков для систем сборки на основе CMake, а также поддерживает сборки Unity.

сакра
источник
1
Я создал набор макросов, функциональность обертка cotire (предварительно скомпилированных заголовков и единство строит) здесь для более легкого использования
onqtam
2
@onqtam, как это проще, он настолько велик, что я даже не понимаю, как просто предварительно скомпилировать работу с cmake и gcc
Павел П.
5
CMake 3.16 представил встроенную поддержку предварительно скомпилированных заголовков. См. Мой ответ stackoverflow.com/a/59514029/2799037 Больше нет необходимости в сторонних модулях!
usr1234567
34

Я использую следующий макрос для создания и использования предварительно скомпилированных заголовков:

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. Довольно аккуратно :)

Larsmoa
источник
2
У этого макроса 1 недостаток. Если генератор не основан на MSVC, предварительно скомпилированный исходный код не будет добавлен в список источников. Так что моя модификация просто перемещает list( APPEND ... )закрытие наружу endif(). Смотрите полный код здесь: pastebin.com/84dm5rXZ
void.pointer
1
@RobertDailey: На самом деле это сделано намеренно - я не хочу компилировать предварительно скомпилированный исходный файл, если не используются предварительно скомпилированные заголовки - он в любом случае не должен определять никаких символов.
larsmoa
1
@Iarsam Исправьте, пожалуйста, /Yuи /FIаргументы, их должно быть ${PrecompiledHeader}и нет ${PrecompiledBinary}.
Mourad
Вы можете объяснить, зачем нам флаги "/ Fp" и "/ FI"? Согласно msdn.microsoft.com/en-us/library/z0atkd6c.aspx использование «/ Fp» не обязательно. Однако, если я вырежу эти флаги из вашего макроса, pch не будет установлен.
Врам Варданян
2
Однако имейте в виду, что аргумент в пользу / Yu понимается очень буквально. Например /YuC:/foo/bar.h, вы будете вынуждены либо передать /FpC:/foo/bar.hфлаг, либо поместить его #include <C:/foo/bar.h>в начало всех ваших файлов .cpp в качестве первого оператора include. MSVC выполняет строковое сравнение #includeаргументов, но не проверяет, указывает ли он на тот же файл, что и был задан /Yu. Ergo, #include <bar.h>не будет работать и выдаст ошибку C2857.
Манузор
21

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...] ...])

Продолжается обсуждение поддержки совместного использования ПКП между целями: 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/

янисозавр
источник
2
Вот ссылка на официальную документацию CMake: cmake.org/cmake/help/latest/command/…
Алекс Че
2
Есть сообщение команды MSVC о том, какие заголовки включать в PCH: devblogs.microsoft.com/cppblog/…
janisozaur
19

Вот фрагмент кода, позволяющий использовать предварительно скомпилированный заголовок для вашего проекта. Добавьте следующее в свой 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 (если смогу). Я отправлю свой ответ, как только закончу. =]
strager 02
@ Джейен, нет; В конце концов я отказался от проекта и больше никогда не сталкивался с проблемами C ++.
Strager
Можно ли настроить PCH на весь проект? Потому что в cmake невозможно получить список автоматически сгенерированных файлов with set( CMAKE_AUTOMOC ON ).
Дмитрий Сазонов
Я использовал ваше решение, но, к сожалению, время компиляции увеличилось с 2'10 "до 2'40" для ~ 130 файлов. Должен ли я myprecompiledheader.cppсначала убедиться, что он скомпилирован? Судя по этому фрагменту, он будет скомпилирован последним, так что, возможно, именно это может быть причиной задержки. myprecompiledheader.hсодержит только самые распространенные заголовки STL, которые использует мой код.
Grim Fandango
13

В итоге я использовал адаптированную версию макроса 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})
яри
источник
2
Абстракция $ {IntDir} полезна. Спасибо.
Гопалакришна Палем
12

Адаптировано от Дэйва, но более эффективно (задает целевые свойства, а не для каждого файла):

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
источник
2
Раньше я использовал это решение, но оно работает, только если проект содержит только файлы c ++. Поскольку COMPILE_FLAGS применяется ко всем исходным файлам, он также будет применяться к файлам c (например, сгенерированным MIDL), что не понравится c ++ PCH. При использовании решения Дейва вы можете использовать get_source_file_property (_language $ {src_file} LANGUAGE) и устанавливать флаги компилятора только в том случае, если это действительно файл CXX.
Андреас Хафербург
Приятно иметь гибкость другого решения в моем заднем кармане, но это то, что я искал, спасибо!
kylewm
Хороший ответ. Остерегайтесь отсутствующих скобок для set_source_files_properties.
Arnaud
2
Его можно выборочно отключить для отдельных файлов с помощью / Y- с помощью set_source_files_properties
mlt
Что abcв твоем примере?
Сандберг
7

если вы не хотите изобретать велосипед, просто используйте либо Cotire, как предлагает верхний ответ, либо более простой - cmake-precompiled-header здесь . Чтобы использовать его, просто включите модуль и вызовите:

include( cmake-precompiled-header/PrecompiledHeader.cmake )
add_precompiled_header( targetName StdAfx.h FORCEINCLUDE SOURCE_CXX StdAfx.cpp )
Роман Круглов
источник
5

CMake 3.16 представил поддержку предварительно скомпилированных заголовков. Есть новая команда CMake, target_precompile_headersкоторая делает все, что вам нужно, под капотом. См. Его документацию для получения дополнительных сведений: https://cmake.org/cmake/help/latest/command/target_precompile_headers.html

usr1234567
источник
2
Этот ответ не добавляет ничего нового к ответу janisozaur, опубликованному полгода назад, кроме ссылки на окончательную официальную документацию, которую, вероятно, лучше добавить в качестве комментария к этому ответу.
Алекс Че
4

Пример использования предварительно скомпилированного заголовка с cmake и Visual Studio 2015

"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"
)
Макс
источник
3

IMHO, лучший способ - установить PCH для всего проекта, как предложил Мартино, в сочетании с возможностью игнорировать 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)

Этот подход проверен и отлично работает.

Врам Варданян
источник
0

Хорошо, когда сборка занимает 10+ минут на четырехъядерном компьютере каждый раз, когда вы меняете одну строку в любом из файлов проекта, он сообщает вам, что пора добавить предварительно скомпилированные заголовки для Windows. В * nux я бы просто использовал ccache и не беспокоился об этом.

Я реализовал свое основное приложение и несколько библиотек, которые оно использует. На данный момент он отлично работает. Также необходимо создать исходный файл pch и файл заголовка, а в исходный файл включить все заголовки, которые вы хотите предварительно скомпилировать. Я делал это с MFC 12 лет, но мне потребовалось несколько минут, чтобы вспомнить это ..


источник
0

Самый простой способ - добавить предварительно скомпилированный параметр в качестве глобального параметра. В файле 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, меня не беспокоит, какое имя файла используется, поэтому я не пытался его указать.

дядя
источник
0

Поскольку параметр предварительно скомпилированного заголовка не работает для файлов 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.

schorsch_76
источник
-15

Даже не ходи туда. Предварительно скомпилированные заголовки означают, что всякий раз, когда изменяется один из заголовков, вам нужно все перестраивать . Вам повезло, если у вас есть система сборки, которая это понимает. Чаще, чем никогда, ваша сборка просто терпит неудачу, пока вы не поймете, что изменили что-то, что предварительно скомпилировано, и поэтому вам нужно выполнить полную перестройку. Вы можете избежать этого в основном, предварительно скомпилировав заголовки, которые, как вы абсолютно уверены, не изменятся, но тогда вы также отказываетесь от значительной части прироста скорости.

Другая проблема заключается в том, что ваше пространство имен загрязняется всевозможными символами, которые вы не знаете или не заботитесь во многих местах, где вы будете использовать предварительно скомпилированные заголовки.

Дирк Греневельд
источник
29
Предварительно скомпилированные заголовки наиболее полезны, когда они ссылаются на заголовки, которые не меняются ... STL, Boost и другие сторонние материалы. Если вы используете PCH для файлов заголовков своих собственных проектов, вы теряете большую часть преимуществ.
Tom
3
Даже если вы используете PCH для заголовков вашего собственного проекта, вся суть системы сборки , как CMake, чтобы убедиться , что зависимости будут соблюдены. Если я изменю свой файл .h (или одну из его зависимостей), я хочу восстановить .pch. Если я не изменю свой файл .h, я не хочу регенерировать .pch. Я думаю, что весь вопрос OP заключался в следующем: как мне добиться этого, используя CMake?
Quuxplusone
1
Предварительно скомпилированные заголовки - лучший инструмент для сокращения времени компиляции до тех пор, пока модули C ++ не будут поддерживаться всеми основными компиляторами. Они решают проблему, которая только усугублялась из-за все более широкого использования шаблонов и библиотек только для заголовков. При правильном использовании заменителя нет. Тем не менее, это не является ответом на заданный вопрос, а просто выражает мнение. Проголосовали против и за удаление.
Inspectable