Как мы исправили один баг в CMake


В августе 2019 года в CMake появилась долгожданная поддержка предкомпилированных заголовков. До этого приходилось использовать разные плагины, например, Cotire. Сразу после выпуска CMake с новым функционалом было ещё несколько доработок. Но уже осенью мы посчитали, что можно начать использовать эту фичу, и переписали наши скрипты. Позже мы нашли баг, который формировал неправильные параметры компилятора Clang и мешал запуску анализатора PVS-Studio. Баг пришлось исправить самим.

https://import.viva64.com/docx/blog/0719_CMakePchFix_ru/image1.png

Что не так?

Проблема была в том, что исходный файл, в котором использовался предкомпилированный заголовок, был некорректно препроцессирован, если использовался компилятор Clang. На месте header-файла было пустое место. Но в GCC и MSVC все работало нормально.

Что делает CMake?

Стоит рассказать о том, как CMake формирует предкомпилированный заголовок. Для каждого target'а (а начиная с CMake 3.17 и для каждого типа сборки – Debug, Release, ...) создается 2 файла: cmake_pch.hxx и cmake_pch.cxx. hxx-файл – это header-файл, из которого уже будет сформирован предкомпилированный заголовок, а cxx – пустой файл с содержимым:

/* Generated by CMake */

Нужен он для генерации предкомпилированного заголовка – он используется как исходник при запуске компилятора.

Во время сборки проекта заголовочный файл включается через передачу флагов компилятора. Для Clang это:

-Xclang -include-pch -Xclang <PCH_FILE>

Т.е. в команду компиляции передается уже созданный предкомпилированный заголовок. В итоге команды запуска сохраняются в файле compile_commands.json, который анализатор использует для проверки проекта.

Проблема

Однако на этом моменте имя оригинального header-файла потеряно. При запуске препроцессора Clang пытается достать имя заголовочного файла из созданного предкомпилированного заголовка и в итоге использует пустой файл cmake_pch.cxx. Почему? Если взглянуть в документацию Clang, то можно увидеть их способ создания предкомпилированных заголовков:

To generate PCH files using clang -cc1, use the option -emit-pch:
$ clang -cc1 test.h -emit-pch -o test.h.pch

Заголовочный файл test.h используется в качестве исходного. А вот как делает CMake:

-Xclang -emit-pch -Xclang -include -Xclang <PCH_HEADER>

Он включает его через специальный флаг, а затем передает cmake_pch.cxx как исходник. Информация о включенных файлах, переданных через командную строку, к сожалению, теряется, а информация об исходном файле остается. Это можно узнать, посмотрев документацию Clang:

Original file name
    The full path of the header that was used to generate the AST file.

Но как в этом случае работал Cotire?

Cotire по своей структуре похож на CMake, однако cxx-файл выглядел примерно так:

#ifdef __cplusplus
#include "/path/to/header.hxx"
#endif

В исходнике у него была директива include, поэтому препроцессор работал нормально. Это первое, что мы хотели предложить в качестве исправления.

Однако при таком подходе появились проблемы - нарушалась кодировка файлов (компилятор может преобразовывать исходный файл в свою кодировку), могла сломаться работа других компиляторов, а в CMake 3.17 файл cmake_pch.cxx создавался только один для каждого типа сборки, когда hxx-файлы могли отличаться.

Решение

В итоге было решено передавать в строку компиляции еще и оригинальный исходный файл:

-Xclang -include-pch -Xclang <PCH_FILE> -Xclang -include -Xclang <PCH_HEADER>

Благодаря опции -Xclang следующий флаг передается напрямую в препроцессор, минуя драйвер, который выполняет поиск соответствующего заголовку .pch/.gch файла. Ссылка на патч: PCH: Clang: Update PCH usage flags to include original header.

Заключение

Наш небольшой патч вошел в CMake 3.17, исправив проблему. Надеюсь, было интересно, и вы узнали чуть больше о работе CMake.

Мы можем рекомендовать использовать эту фичу в реальных проектах.



Найдите ошибки в своем C, C++, C# и Java коде

Предлагаем попробовать проверить код вашего проекта с помощью анализатора кода PVS-Studio. Одна найденная в нём ошибка скажет вам о пользе методологии статического анализа кода больше, чем десяток статей.

goto PVS-Studio;



Найденные ошибки

Проверено проектов
411
Собрано ошибок
14 123

А ты совершаешь ошибки в коде?

Проверь с помощью
PVS-Studio

Статический анализ
кода для C, C++, C#
и Java

goto PVS-Studio;
Этот сайт использует куки и другие технологии, чтобы предоставить вам более персонализированный опыт. Продолжая просмотр страниц нашего веб-сайта, вы принимаете условия использования этих файлов. Если вы не хотите, чтобы ваши данные обрабатывались, пожалуйста, покиньте данный сайт. Подробнее →
Принять