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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
105
0
70 459
14

Ответы 14

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

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

Предварительно скомпилированные заголовки наиболее полезны, когда они ссылаются на заголовки, которые изменяет не ... STL, Boost и другие сторонние материалы. Если вы используете PCH для файлов заголовков своих собственных проектов, вы теряете большую часть преимуществ.

Tom 15.03.2009 18:47

Даже если вы используете PCH для заголовков своего собственного проекта, весь смысл системы сборки, такой как CMake, состоит в том, чтобы убедиться, что зависимости являются соблюдаются. Если я изменю свой файл .h (или одну из его зависимостей), я хочу, чтобы восстановить .pch. Если я не изменю свой файл .h, я не хочу восстановить .pch. Я думаю, что весь вопрос OP заключался в следующем: как мне добиться этого, используя CMake?

Quuxplusone 30.05.2014 03:36

Предварительно скомпилированные заголовки - лучший инструмент для сокращения времени компиляции до тех пор, пока модули C++ не будут поддерживаться всеми основными компиляторами. Они решают проблему, которая только усугубилась из-за все более широкого использования шаблонов и библиотек только для заголовков. При правильном использовании заменителя нет. Тем не менее, это не является ответом на заданный вопрос, а просто выражает мнение. Проголосовали "против" и "за удаление".

IInspectable 16.01.2018 00:02

Вот фрагмент кода, позволяющий использовать предварительно скомпилированный заголовок для вашего проекта. Добавьте следующее в свой 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.01.2009 04:58

@ Джейен, нет; В конце концов я отказался от проекта и больше никогда не сталкивался с проблемами C++.

strager 21.11.2010 10:11

Можно ли настроить PCH на весь проект? Потому что невозможно получить список автоматически сгенерированных файлов в cmake with set( CMAKE_AUTOMOC ON ).

Dmitry Sazonov 19.03.2014 17:54

Я использовал ваше решение, но, к сожалению, время компиляции увеличилось с 2'10 "до 2'40" для ~ 130 файлов. Должен ли я сначала убедиться, что myprecompiledheader.cpp скомпилирован? Судя по этому фрагменту, он будет скомпилирован последним, так что, возможно, именно это может быть причиной задержки. myprecompiledheader.h содержит только самые распространенные заголовки STL, которые использует мой код.

Grim Fandango 06.08.2014 01:02

Хорошо, когда сборка занимает 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

void.pointer 30.10.2011 23:05

@RobertDailey: На самом деле это сделано намеренно - я не хочу компилировать предварительно скомпилированный исходный файл, если не используются предварительно скомпилированные заголовки - он в любом случае не должен определять никаких символов.

larsmoa 31.10.2011 10:54

@Iarsam Исправьте аргументы /Yu и /FI, они должны быть ${PrecompiledHeader}, а не ${PrecompiledBinary}.

Mourad 28.06.2013 17:34

Вы можете объяснить, зачем нам флаги "/ Fp" и "/ FI"? Согласно msdn.microsoft.com/en-us/library/z0atkd6c.aspx использование «/ Fp» не обязательно. Однако, если я вырежу эти флаги из вашего макроса, pch не будет установлен.

Vram Vardanian 14.04.2015 18:56

Однако имейте в виду, что аргумент в пользу / Yu понимается очень буквально. Например. /YuC:/foo/bar.h заставит вас либо передать флаг /FpC:/foo/bar.h, либо поместить #include <C:/foo/bar.h> в начало всех ваших файлов .cpp в качестве первого оператора include. MSVC выполняет строковое сравнение аргументов #include, но не проверяет, указывает ли он на тот же файл, что и /Yu. Ergo, #include <bar.h> не будет работать и выдаст ошибку C2857.

Manuzor 19.09.2015 15:05

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

Gopalakrishna Palem 26.05.2014 10:34

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

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.

Andreas Haferburg 16.02.2012 13:36

Приятно иметь гибкость другого решения в моем заднем кармане, но это то, что я искал, спасибо!

kylewm 29.02.2012 01:58

Хороший ответ. Остерегайтесь отсутствующих скобок для set_source_files_properties.

Arnaud 13.01.2014 01:42

Его можно выборочно отключить для отдельных файлов с / Y- с помощью set_source_files_properties

mlt 15.02.2014 11:04

Что такое abc в вашем примере?

Sandburg 05.03.2019 17:31

abc - целевой проект, в котором нужно установить флаги

martjno 06.03.2019 06:51

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

Я создал набор макросов, которые обертывают общую функциональность (предварительно скомпилированные заголовки и сборки единства) здесь для упрощения использования

onqtam 29.02.2016 08:24

@onqtam, как это проще, он настолько велик, что я даже не вижу, как просто предварительно скомпилировать работу с cmake и gcc

Pavel P 07.06.2017 04:54

CMake 3.16 представил встроенную поддержку предварительно скомпилированных заголовков. Смотрите мой ответ stackoverflow.com/a/59514029/2799037 Больше нет необходимости в сторонних модулях!

usr1234567 28.12.2019 22:05

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

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

Пример использования предварительно скомпилированного заголовка с 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"
)

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

Alex Che 08.05.2020 18:38

Есть сообщение от команды MSVC о том, какие заголовки включать в PCH: devblogs.microsoft.com/cppblog/…

janisozaur 09.05.2020 21:04

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

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

Alex Che 08.05.2020 18:42

Другие вопросы по теме