- PVSM.RU - https://www.pvsm.ru -
Задавшись этим вопросом я, в первую очередь, сформулировал требования: жесткие и опциональные (но желательные) для системы сборки и графической среды разработки.
Сразу хочу отметить что речь идет о написании C++ кода не под какую-то специфичную платформу типа Android или фреймворка, например Qt, — где все уже готово, как с построением так и с редактированием кода, а об generic коде не привязанному к конкретной платформе или фреймворку.
В свете этих «хотелок» мной были рассмотрены несколько систем сборки и графических сред разработки. Этот небольшой обзор ни в коей мере не претендует на полноту и содержит мои субъективные оценки, но, возможно, кому-то это покажется полезным в качестве начальной ступени.
Make — [античность] мастодонт и заслуженный ветеран систем сборки, которого все никак не хотят отпустить на пенсию, а заставляют везти на себе все новые и новые проекты. Это очень низкоуровневая тулза со своим специфичном языком, где за пробел вместо таба вам сразу же грозит расстрел на месте. С помощью make можно сделать все что угодно — билд любой сложности, но за это придется заплатить усилиями для написания скрипта, а так же его поддержки в актуальном состоянии. Переносить логику билда из проекта в проект так же будет накладно. Существуют некие современные «заменители» make-а: типа ninja и jam, но сути они не меняют — это очень низкоуровневые инструменты. Так же как и на ассемблере можно написать все что угодно, только стоит ли?
CMake — [средневековье] первая попытка уйти от низкоуровневых деталей make-а. Но, к сожалению, далеко уйти не удалось — движком здесь служит все тот же make для которого CMake генерирует огромные make-файлы на основе другого текстового файла с более выскоуровневым описанием билда. По схожему принципу работает и qmake. Такой подход напоминает мне красивый фасад старого деревянного дома, который заботливо обшили свежим пластиком. CMake стабильная и хорошо зарекомендовавшая себя система, есть даже встроенная интеграция с Eclipse, но, к сожалению, мне не подошла потому что противоречит части требований изложенных в начале статьи. Под Linux все вроде бы хорошо, но если нужно построить тот же проект под Windows с помощью MSVC — а я предпочитаю нативный компилятор MinGW-шному, будут сгенерированы файлы для NMake. Т.е. зависимости на еще одну тулзу и разные команды на сборку для другой платформы. И все это следствия чуток кривоватой архитектуры, когда основная часть работы выполняется другими «помощниками».
Ant — [эпоха возрождения] своеобразный клон make для Java. Скажу честно, я потратил совсем немного времени для проверки Ant (а так же Maven) в качестве билд системы для C++. И у меня сразу же появилось ощущение что поддержка С++ здесь чисто «для галочки» и недостаточно развита. К тому же даже в Java проектах Ant уже используется достаточно редко. В качестве языка сценария (так же как и для Maven) здесь выбран XML — этот гнусный птичий язык :). Этот факт оптимизма мне совсем не прибавил для дальнейшего погружения в тему.
SCons — [новые времена] самодостаточная, кросплатформенная билд система, написанная на Python. SCons одинаково хорошо справляется как с Java так и с C++ билдами. Зависимости хидеров для инкрементальной сборки отрабатываются корректно (насколько я понял создается некая база данных с метаданными билда), а на Windows «без бубна» работает MSVC. Язык сценария сборки — Python. Весьма достойная система, и я даже хотел закончить свои изыскания на ней, но как известно, нет пределу совершенства, и при более детальном осмотре выявились некоторые минусы в свете вышеизложенных требований.
Нет никаких абстрактных настроек для компилятора, поэтому если, например, возникнет необходимость сменить тулчейн, возможно, понадобиться искать места в билд скрипте для внесения изменений. Те же макросы придется прописывать с вложенными условиями — если это Виндовс то сделай так, если это GCC сделай так и т.д.
Нет поддержки удаленных артефакториев и высокоуровневой зависимости одного билда на другой.
Общая архитектура построена так, что так называемые user defined builders существуют практически изолированно и нет возможности заиспользовать уже существующую логику билда, чтобы дополнить ее своей через несложный плагин. Но в целом это достойный выбор для небольших проектов.
Gradle [современность] — у меня уже был позитивный опыт использования Gradle для Java и Kotlin проектов и я возлагал на него большие надежды.
Для JVM языков в Gradle очень удобная концепция работы с библиотеками, необходимыми для построения проекта (билд зависимостями):
Сначала я заценил «старый» плагин для поддержки C++ — `cpp` — и был разочарован — структура скрипта не интуитивная: model, component, nativespec — и какая-то мешанина из различных типов бинарей: и выполнимые и библиотеки все в одном скрипте. Непонятно где размещать юнит тесты. Такая структура сильно отличалась от того что я использовал для Java.
Но, оказалось, что есть и «новые» плагины для поддержки C++: `cpp-application` — для приложений, `cpp-library` для библиотек: статических и динамических и наконец `cpp-unit-test` для юнит тестирования. И это было то что я искал! :)
Структура папок проекта по умолчанию похожа на проект для Java:
Такая структура не жесткая — ее всегда можно поменять в скрипте, но все же не стоит этого делать без особой необходимости, она вполне разумна.
Кстати, билд скрипт — обычно build.gradle, это DSL языка Groovy или Kotlin (build.gradle.kts) на выбор. Внутри скрипта всегда доступен Gradle API и API добавленных в скрипт плагинов.
Для библиотек можно выбрать тип: статическая или динамическая (или собрать оба варианта).
По умолчанию сконфигурированы два варианта построения: Debug (gradle assemble) и Release (gradle assembleRelease).
Принцип запуска юнит тестирования такой же как в Java: gradle test выполнит простройку основного компонента, потом тестов, если они есть в папке src/test/cpp, а затем выполнит тестовое приложение.
Пресловутые дефайны можно задавать абстрактно — Gradle сам сгенерирует необходимы параметры компилятора. Есть еще несколько абстрактных настроек типа оптимизации, отладочной информации и т.д.
Из коробки поддерживаются GCC, Microsoft Visual C++, CLang.
Система плагинов очень развита, а архитектура расширений устроена удобно — можно взять готовую логику и задекорировать/расширить ее. Плагины бывают двух видов: динамические, которые пишутся прямо на Groovy и встраиваются в скрипт или написанные на Java (или на другом языке с JVM) и скомпилированные в бинарные артефакты. Для плагинов существует бесплатный Gradle-артифакторий, в котором любой желающий может разместить свой плагин, который будет доступен всем. Что успешно и проделал автор этой статьи :) но об этом чуть позже.
Хотелось бы подробнее остановиться теперь на системе работы с бинарными компонентами в Gradle для C++: она почти такая же как в Java! Билд зависимости работают практически так же как я описал выше.
Возьмем для примера композитный билд:
В файле build.gradle из папки app достаточно прописать такую зависимость:
dependencies {
implementation project(':utils')
}
А все остальное проделает Gradle! Добавит в компилятор путь для поиска заголовочных файлов utils и прилинкует бинарь библиотеки.
И все это одинаково хорошо сработает как под Linux GCC, так и под Windows MSVC.
Инкрементальная сборка, естественно, тоже замечательно работает и при изменении хидеров в utils будет перестроен app.
Как оказалось, в Gradle пошли дальше и реализовали возможность выкладывать C++ артефакты в Maven Repository! Для этого используется стандартный `maven-publish` плагин.
В скрипте необходимо указать репозиторий куда вы хотите выложить свой артефакт и сделать gradle publish (или gradle publishToMavenLocal для локальной публикации). Gradle сбилдит проект и
выложит в специальном формате — с учетом версии, платформы, архитектуры и варианта билда.
Выкладываются сами бинарные файлы библиотек и публичные заголовочные файлы — из папки src/main/public.
Понятно что выложить С++ артефакт на Maven Cental нельзя — он не пройдет обязательные проверки системы. Но поднять Maven репозиторий в сети совсем нетрудно, а для локального репозитория вообще ничего делать не нужно — это просто папка на диске.
Теперь если вы хотите использовать в своем проекте чью-то библиотеку вы можете написать в билд скрипте что-то вроде:
repositories {
maven {
url = 'https://akornilov.bitbucket.io/maven'
}
}
unitTest {
dependencies {
implementation 'org.bitbucket.akornilov.tools:gtest:1.8.1'
}
}
Здесь говориться что для юнит тестирования нужно использовать артефакт gtest версии 1.8.1 из Maven репозитория [2].
Это, кстати, вполне реальный репозиторий в котором выложен мой билд Google Test v1.8.1, простроенный с помощью Gradle для Windows и Linux x86_64.
Естественно, что всю низкоуровневую работу по конфигурированию компилятора и линковщика для работы с внешним компонентом Gradle берет на себя. Вам достаточно заявить о своих намерениях использовать такую-то библиотеку с такой-то версией из такого-то репозитория.
Для интеграции с IDE в Gradle есть два встроенных плагина для Visual Studio и XCode. Они хорошо работают, за исключением того что Visual Studio плагин игнорирует код юнит тестов из папки src/test/cpp и генерирует проект только для основного кода.
Eclipse CDT (2018-12R) — зрелый и качественный продукт. Если ему удалось успешно пропарсить Ваш проект, значит Вам повезло — редактировать будет комфортно. Скорее всего он даже «поймет» самые замороченные типы auto. Но если нет… Тогда он будет яростно подчеркивать красным пунктиром все подряд и ругаться нехорошими словами. Например, он не переваривает стандартные заголовочные файлы MSVC и Windows SDK. Даже вполне безобидный printf подчеркивается красным пунктиром и не воспринимается как нечто осмысленное. Там же оказался и std::string. Под Linux с родным ему gcc все замечательно. Но даже при попытке заставить его индексировать проект из родственного Android Native начались проблемы. В заголовках bionic он в упор отказывался видеть определение size_t, а заодно и всех функций которые его использовали. Наверное, под Windows можно исправить ситуацию если вместо заголовочных файлов Microsoft подсунуть ему, например, Cygwin или MinGW SDK, но мне такие фокусы не очень интересны, мне бы все же хотелось чтобы софт такого уровня «кушал то что дают», а не только то что он «любит».
Возможности по навигации, рефакторингу и генерации шаблонного кода замечательные, но вот к помощнику при наборе букв есть вопросы: допустим набираем несколько символов из какого-то длиннющего имени, почему бы не предложить варианты завершения? Нет, помощник терпеливо дожидается пока пользователь доберется до. или -> или ::. Приходится постоянно нажимать Ctrl + Space — раздражает. В Java эту досадную недоделку можно было исправить выбрав в качестве триггера весь алфавит в CDT же я не нашел простого решения.
NetBeans 8.1/10.0 — доводилось пользоваться эти IDE для Java, запомнился как неплохой и легковесный софт со всем необходимым функционалом. Для C++ у него есть плагин разработанный не сообществом, а непосредственно NetBeans. Для C++ проектов существует довольная жесткая зависимость на make и gcc. Редактор кода неторопливый. В генераторе шаблонного кода не нашел очень простую вещь: добавляем новый метод в заголовочном файле класса — нужно сгенерировать тело метода в cpp файле — не умеет. Степень «понимания» кода средняя, вроде бы что-то парсит, а что-то нет. Например, итерирование по мапе с автоиетратором для него уже сложновато. На макросы из Google Test ругается. Закастомизировть билд команды проблематично — на Linux при доступном gcc и make (это при том что используется уже другая билд система) сработает, на Windows потребует MinGW, но даже при его наличии откажется построить. В целом работа в NetBeans с C++ возможна, но комфортной я бы ее не назвал, наверное, надо очень любить эту среду чтобы не замечать разные ее болячки.
KDevelop 5.3.1 — когда-то задумывался как инструмент разработчика для KDE (Linux), но сейчас есть версия и под Windows. Имеет быстрый и приятный редактор кода с красивой подсветкой синтаксиса (основан на Kate). Закостомизировать левую билд систему не получится — для него основная система сборки CMake. Толерантно относится к MSVC и Windows SDK заголовкам, во всяком случае printf и std::string точно не приводят его в ступор как Eclipse CDT. Очень шустрый помощник по написанию кода — хорошие варианты завершения предлагает почти сразу во время набора текста. Имеет интересную возможность по генерации шаблонного кода: можно написать свой шаблон и выложить его онлайн. При создании по шаблону можно подключиться к базе данных готовых шаблонов и загрузить понравившийся. Единственное что расстроило: встроенный шаблон по созданию нового класса криво работает как под Windows так и под Linux. Wizrd-а по созданию класса имеет несколько окон в которым можно много чего настроить: какие конструкторы нужны, какие члены класса и т.д. Но на финальной стадии под Windows выскакивает какая-то ошибка успеть разглядеть текст которой невозможно и создаются два файла h и cpp размером 1 байт. В Linux почему-то нельзя выбрать конструкторы — вкладка пустая, а на выходе корректно генериться только заголовочный файл. В общем-то, детские болезни для такого зрелого продукта смотрятся как-то несерьезно.
QtCreator 4.8.1 (open source edition) — наверное услышав это название Вы недоумеваете как сюда затесался этот монстр заточенный под Qt с дистрибутивом в гигабайт с гаком. Но речь идет о «легкой» версии среды для generic проектов. Его дистрибутив весит всего около 150 Мб и не тащит с собой вещи специфичные для Qt: download.qt.io/official_releases/qtcreator/4.8 [3].
Собственно он умеет делать почти все о чем я написал в своих требованиях быстро и корректно. Он парсит стандартные заголовки как Windows так и Linux, кастумезируется под любую билд систему, подсказывает варианты завершения, удобно генерит новый классы, тела методов, позволяет выполнять рефакторинг и навигацию по коду. Если хочется просто комфортно работать, не думая постоянно о том, как побороть ту или иную проблему есть смысл присмотреться к QtCreator-у.
Собственно осталось рассказать о том чего мне не хватило в Gradle для полноценной работы: интеграция с IDE. Чтобы билд система сама сгенерировала бы проектные файлы для IDE, в которых уже были бы прописаны команды для построения проекта, перечислены все исходные файлы, необходимы пути для поиска заголовочных файлов и определения.
Для этой цели мной был написан плагин для Gradle `cpp-ide-generator` [4] и опубликован на Gradle Plugin Portal.
Плагин может использоваться только совместно с `cpp-application`, `cpp-library` и `cpp-unit-test`.
Вот пример его использования в build.gradle:
plugins {
id ‘cpp-library’
id ‘maven-publish’
id ‘cpp-unit-test’
id ‘org.bitbucket.akornilov.cpp-ide-generator’ version ‘0.3’
}
library {
// Library specific parameters
}
// Configuration block of plugin:
ide {
autoGenerate = false
eclipse = true
qtCreator = true
netBeans = true
kdevelop = true
}
Плагин поддерживает интеграцию со всеми вышеперечисленными графическими средами разработки, но в конфигурационном блоке плагина — ide можно отключить поддержку ненужных IDE:
kdevelop = false
Если параметр autoGenerate выставлен в true проектные файлы для всех разрешенных IDE будут автоматически генерироваться прямо во время билда. Так же в режиме автоматической генерации проектные файлы будут удаляться при очистке билда: gradle clean.
Поддерживается инкрементальная генерация, т.е. обновляться будут только те файлы, которые требуют реального обновления.
Вот список целей которые добавляет плагин:
Во время генерации плагин «обнюхивает» билд и извлекает из него всю необходимую информацию для создания проектных файлов. После открытия проекта в IDE должны быть видны все исходные файлы, прописаны пути ко всем хидерам, а так же настроены базовые билд комманды — построить/очистить.
Второй плагин, который мне пришлось сделать, называется `cpp-build-tuner` [5] и он также работает в паре с cpp-application`, `cpp-library` и `cpp-unit-test`.
У плагина нет никаких настроек, его достаточно просто зааплаить:
plugins {
id ‘cpp-library’
id ‘maven-publish’
id ‘cpp-unit-test’
id ‘org.bitbucket.akornilov.cpp-build-tuner’ version ‘0.5’
}
Плагин выполняет небольшие манипуляции с настройками тулчейнов (компилятора и линковщика) для разных вариантов билда — Debug и Release. Поддерживаются MSVC, gcc, CLang.
Особенно это актуально для MSVC потому что по умолчанию в результате релизного билда Вы получите «жирный», не эстетичный бинарь с дебажной информацией и статически прилинкованной стандартной библиотекой. Часть настроек для MSVC я «подсмотрел» в самой Visual Studio, которые по дефолту он добавляет в свои C++ проекты. Как для gcc/CLang так и для MSVC в профиле Release включаются link time optmizations.
Заметка: Плагины проверялись с последней версией Gradle v5.2.1 и не тестировались на совместимость с предыдущими версиями.
Исходные коды плагинов, а так же простенькие примеры использования Gradle для библиотек: статических и динамических, а так же приложения, которое их использует можно посмотреть: bitbucket.org/akornilov/tools [6] дальше gradle/cpp.
Так же в примерах показано, как пользоваться Google Test для юнит тестирования библиотек.
Maven Repository с простроенной в Gradle Google Test v1.8.1 (без mock). [2]
Автор: akornilov
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/make/310706
Ссылки в тексте:
[1] хостинг: https://www.reg.ru/?rlink=reflink-717
[2] Maven репозитория: https://akornilov.bitbucket.io/maven
[3] download.qt.io/official_releases/qtcreator/4.8: https://download.qt.io/official_releases/qtcreator/4.8/
[4] плагин для Gradle `cpp-ide-generator`: https://plugins.gradle.org/plugin/org.bitbucket.akornilov.cpp-ide-generator
[5] `cpp-build-tuner`: https://plugins.gradle.org/plugin/org.bitbucket.akornilov.cpp-build-tuner
[6] bitbucket.org/akornilov/tools: https://bitbucket.org/akornilov/tools
[7] Источник: https://habr.com/ru/post/442682/?utm_campaign=442682
Нажмите здесь для печати.