Використання попередньо складених заголовків з CMake


104

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

Відповіді:


79

Існує сторонній модуль CMake під назвою "Cotire", який автоматизує використання попередньо складених заголовків для систем побудови на основі CMake, а також підтримує побудову єдності.


1
Я створив набір макросів, функціональність обгортка cotire (попередньо скомпільовані заголовків і єдність будує) тут для більш легкого використання
onqtam

2
@onqtam як це простіше, він настільки гігантський, що я навіть не бачу, як просто заздалегідь скласти роботу з cmake та gcc
Pavel P

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 платформах. Досить акуратно :)


2
Цей макрос має 1 недолік. Якщо генератор не базується на MSVC, попередньо складене джерело не буде додано до списку джерел. Тож моя модифікація просто переміщує list( APPEND ... )зовнішнє закриття endif(). Дивіться повний код тут: pastebin.com/84dm5rXZ
void.pointer

1
@RobertDailey: Це насправді навмисно - я не хочу компілювати попередньо скомпільований вихідний файл, коли не використовуються попередньо складені заголовки - він не повинен визначати жодних символів.
larsmoa

1
@Iarsam Будь ласка, виправте /Yuі /FIаргументи, вони повинні бути ${PrecompiledHeader}і ні ${PrecompiledBinary}.
Мурад

Ви можете пояснити, для чого нам потрібні "/ Fp" та "/ FI" прапори? Відповідно до msdn.microsoft.com/en-us/library/z0atkd6c.aspx використання "/ Fp" не є обов'язковим. Однак, якщо я вирізаю ці прапори з вашого макросу, жоден pch не встановлений.
Врам Варданян

2
Будьте в курсі, що аргумент до / Ю приймається дуже буквально Наприклад, /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, до 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/


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 (якщо зможу). Я опублікую свою відповідь, як тільки я закінчу. =]
страгер

@Jayen, Nope; Зрештою я відмовився від проекту і більше ніколи не потрапляв у клопоти на C ++, в основному.
страгер

Чи можна встановити PCH на весь проект? Тому що неможливо отримати список автогенерованих файлів у cmake with set( CMAKE_AUTOMOC ON ).
Дмитро Сазонов

Я використав ваше рішення, але, на жаль, час компіляції отримав від 2'10 "до 2'40", для ~ 130 файлів. Чи потрібно переконатися, що myprecompiledheader.cppце скомпільовано спочатку? З цього фрагмента виглядає, що він буде складений останнім, тому, можливо, саме це може спричинити затримку. myprecompiledheader.hмістить лише найпоширеніші заголовки STL, якими використовується мій код.
Грим Фанданго

13

Я в кінцевому рахунку використовував адаптовану версію макросу 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})

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)

2
Раніше я використовував це рішення, але воно працює лише в тому випадку, якщо проект містить лише файли c ++. Оскільки COMPILE_FLAGS застосовується до всіх вихідних файлів, він також застосовуватиметься до файлів c (наприклад, тих, що генеруються MIDL), що не сподобається PC + c ++. Під час використання рішення Дейва ви можете використовувати get_source_file_property (_language $ {src_file} LANGUAGE) та встановлювати лише прапорці компілятора, якщо це дійсно файл CXX.
Андреас Хафербург

Приємно мати гнучкість іншого рішення у моїй задній кишені, але це саме те, що я шукав, дякую!
kylewm

Гарна відповідь. Остерігайтеся відсутніх дужок для set_source_files_properties.
Арно

2
Його можна вибірково вимкнути для окремих файлів з / Y-, використовуючи set_source_files_properties
mlt

Що є abcу вашому прикладі?
Сандберг

7

якщо ви не хочете винаходити колесо, просто використовуйте або Cotire, як підказує головна відповідь, або більш простий - cmake-попередньо складений заголовок тут . Для його використання просто включіть модуль та зателефонуйте:

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


2
Ця відповідь не додає нічого нового до відповіді янізозавра, опублікованої на півроку раніше, за винятком посилання на остаточну офіційну документацію, яку, мабуть, слід краще додати як коментар до цієї відповіді.
Алекс Че

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

Цей підхід перевірений і працює чудово.


0

Добре, коли збірки займають 10+ хвилин на чотирьохядерній машині щоразу, коли ви змінюєте один рядок у будь-якому з файлів проекту, це повідомляє вам про час додавати попередньо складені заголовки для Windows. На * nux я б просто використовував ccache і не хвилювався з цього приводу.

Я реалізував у своєму головному додатку та декількох бібліотеках, які він використовує. Це чудово працює до цього моменту. Одне, що також потрібно - це створити файл pch-джерела та заголовок, а у вихідний файл включити всі заголовки, які ви хочете попередньо скласти. Я робив це протягом 12 років з MFC, але мені знадобилося кілька хвилин, щоб згадати про це.


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.


-15

Навіть не туди. Попередньо складені заголовки означають, що кожного разу, коли один із заголовків змінюється, вам доведеться все перебудовувати . Вам пощастило, якщо у вас є система побудови, яка це усвідомлює. Частіше, ніж ніколи, ваша збірка буде просто невдалою, поки ви не зрозумієте, що ви змінили те, що попередньо складено, і тому вам потрібно зробити повне відновлення. Цього можна уникнути здебільшого, попередньо склавши заголовки, які ви абсолютно позитивні, не зміниться, але тоді ви відмовитесь також від значної частини збільшення швидкості.

Інша проблема полягає в тому, що ваш простір імен забруднюється всілякими символами, які ви не знаєте і не цікавите в багатьох місцях, де ви б використовували попередньо складені заголовки.


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

3
Навіть якщо ви використовуєте PCH для заголовків вашого власного проекту, вся суть системи збирання , як CMake, щоб переконатися , що залежно будуть дотримані. Якщо я зміню свій .h-файл (або одну із його залежностей), я хочу, щоб .pch був регенерований. Якщо я не зміню свій .h файл, я не хочу регенерувати .pch. Я думаю, що питанням ОП було: Як я можу це зробити, використовуючи CMake?
Квомплусон

1
Попередньо складені заголовки - це найкращий інструмент для скорочення часу компіляції, поки модулі C ++ не підтримають усі основні компілятори. Вони вирішують проблему, яка тільки погіршилася завдяки постійно зростаючому використанню шаблонів і бібліотек, лише для заголовків. При правильному використанні замінника немає. Незважаючи на це, це не є відповіддю на поставлене питання, а просто озвучує думку. Проголосований "вниз" та "видалити".
Неочікуваний
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.