--- category: ru type: paper hastr: true layout: paper tags: разработка, c++, cmake title: Добавляем cppcheck и clang-format для проекта на cmake short: cppcheck-and-clang-format --- Небольшое How-To посвященное прикручиванию автоматической проверки стиля, а также статического анализатора к проекту на `C++`, который использует в качестве системы сборки `cmake`. ## Проект Наш проект имеет следующую структуру: ```bash sources/ |- CMakeLists.txt |- 3rdparty/ |- first_component/ |- second_component/ ``` **3rdparty** - директория с различными дополнительными библиотеками, которую надо исключить из проверок (в дальнейшем соответствует переменной cmake `PROJECT_TRDPARTY_DIR`). Дополнительно допустим, что у нас, помимо обычных файлов исходного кода (`*.cpp`, `*.h`) есть еще какие-либо (например, `*.qml`). Дополнительно используемые ниже команды можно вставить в pre-commit hook и невозбранно тролить коллег по ынтырпрайзу, не давая им закоммитить ничего, пока они не научатся читать `CONTRIBUTING.md`. ## cppcheck Коль скоро нормальных (из коробки) статических анализаторов не завезли в open source будем использовать то, что имеется. Знатоки говорят, что [cppcheck](//cppcheck.sourceforge.net/ "Сайт cppcheck") при должной конфигурации будет лучше, чем любой аналог, но конфигурация его для достаточно большого проекта похожа больше на написание нового проекта. Суть добавления cppheck к проекту сводится к указанию очевидных недоработок в коде и тыканью в лужу них. ### Общий пример запуска Тут все, казалось бы, очень просто: ```bash cppcheck --enable=warning,performance,portability,information,missingInclude --std=c++11 --library=qt.cfg --template="[{severity}][{id}] {message} {callstack} (On {file}:{line})" --verbose --quiet path/to/source/files/or/directory ``` * `--enable` говорит о том, какие уведомления надо включить. Я выключил `style` (для этого ниже мы заведем `clang-format`), `unusedFunction` - выдает false-positive для некоторых мест. * `--std` говорит об используемом стандарте. * `--library=qt.cfg` некий файл настроек, который говорит о том, что и как надо обрабатывать. Добрые разработчики предлагаю почитать на эту тему [мануал](//cppcheck.sourceforge.net/manual.pdf "cppcheck мануал"). В данном случае я использовал шаблон из `/usr/share/cppcheck/cfg/`. * `--template` - шаблон строки уведомления. * `---verbose --quiet` две противоречащие друг другу опции. Первая включает более информативные сообщения, вторая выключает отчет о прогрессе. ### Интеграция с cmake Файл `cppcheck.cmake` в корне проекта: ```cmake # additional target to perform cppcheck run, requires cppcheck # get all project files # HACK this workaround is required to avoid qml files checking ^_^ file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.h) foreach (SOURCE_FILE ${ALL_SOURCE_FILES}) string(FIND ${SOURCE_FILE} ${PROJECT_TRDPARTY_DIR} PROJECT_TRDPARTY_DIR_FOUND) if (NOT ${PROJECT_TRDPARTY_DIR_FOUND} EQUAL -1) list(REMOVE_ITEM ALL_SOURCE_FILES ${SOURCE_FILE}) endif () endforeach () add_custom_target( cppcheck COMMAND /usr/bin/cppcheck --enable=warning,performance,portability,information,missingInclude --std=c++11 --library=qt.cfg --template="[{severity}][{id}] {message} {callstack} \(On {file}:{line}\)" --verbose --quiet ${ALL_SOURCE_FILES} ) ``` `cppcheck` умеет рекурсивно директории проверять, однако, на моем примере, мне нужно было пропустить проверку qml-файлов, потому что open source проект, в лучших традициях, сегфолтился на некоторых из них - именно для этого используется поиск исходных файлов с дальнейшим выбрасыванием из них файлов, которые не должны проверяться. Включаем в проект (`CMakeLists.txt`)... ```cmake include(cppcheck.cmake) ``` ...и запускаем: ```bash cmake make cppcheck ``` Дальше руками вносим необходимые исправления. ### Дополнительно * Можно добавить свои директории для поиска хидеров, используя опцию `-I dir`. * Можно вычеркнуть файлы и/или директории из проверки, используя опцию `-i path/to/file/or/directory`. ## clang-format [clang-format](//clang.llvm.org/docs/ClangFormat.html "Сайт clang-format") предназначен для автоматического подгона стиля под желаемый или требуемый. Среди аналогов стоит выделить [astyle](//astyle.sourceforge.net/ "Сайт astyle"), который имеет очень скромные возможности, и [uncrustify](//uncrustify.sourceforge.net/ "Сайт uncrustify"), который, наоборот, имеет слишком много опций. ### Общий пример запуска ```bash clang-format -i -style=LLVM /path/to/source/files ``` (К сожалению, он **не умеет** в рекурсивный обход директории.) * `-i` включает автозамену файлов (в противном случае, результат будет печататься в stdout). * `-style` выбор определенного стиля либо из предустановленных, либо из файла (`file`), см. ниже. ### Интеграция с cmake Файл `clang-format.cmake` в корне проекта: ```cmake # additional target to perform clang-format run, requires clang-format # get all project files file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.h) foreach (SOURCE_FILE ${ALL_SOURCE_FILES}) string(FIND ${SOURCE_FILE} ${PROJECT_TRDPARTY_DIR} PROJECT_TRDPARTY_DIR_FOUND) if (NOT ${PROJECT_TRDPARTY_DIR_FOUND} EQUAL -1) list(REMOVE_ITEM ALL_SOURCE_FILES ${SOURCE_FILE}) endif () endforeach () add_custom_target( clangformat COMMAND /usr/bin/clang-format -style=LLVM -i ${ALL_SOURCE_FILES} ) ``` Аналогичных способ поиска исходных файлов, как и для `cppcheck`, поскольку `clang-format` не умеет в рекурсию. Включаем в проект (`CMakeLists.txt`)... ```cmake include(clang-format.cmake) ``` ...и запускаем: ```bash cmake make clangformat ``` Никаких дополнительных действий не требуется. ### Дополнительно * Настройка. Можно почитать опции на [официальном сайте](//clang.llvm.org/docs/ClangFormat.html "Сайт clang-format"). Также можно воспользоваться [интерактивной утилитой](//clangformat.com/ "Сайт") для просмотра опций. Для использования уже готового стиля за базу используем следующую команду: ```bash clang-format -style=LLVM -dump-config > .clang-format ``` Далее редактируется полученный файл `.clang-format`. Для включения его необходимо передать опцию `-style=file`, файл должен находиться в одной из родительских директории для каждого файла (например, в корне проекта). Также, можно передать нужные опции прямо в командной строке, например `-style="{BasedOnStyle: llvm, IndentWidth: 8}"`.