|
Professor Seleznov
|
Акт 1. Экспозиция

ГАМТ России, «Всегда зовите Долли!» Исторически сложилось, что одна из главных проблем C++ - это тулчейны, системы сборки, управление зависимостями и всё вокруг. Ничего из этого не является частью стандарта, поэтому кто во что горазд. Любой бигтех просто обязан написать свой инструмент, который наконец-то станет лучшим. Другой же лагерь, наоборот стоит за использование максимально распространенных, общеизвестных и поддерживаемых решений, и готов мириться с их недостатками. В системах сборки явным лидером и де-факто стандартом стал CMake. В пакетных менеджерах нет такого единства, здесь у нас можно обозначить двух лидеров в лице vcpkg и Conan. Дабы разводить холивар в комментариях, а не в этом небольшом историческом очерке, мы будем говорить только про последнего из двух) Статья посвящена больше тем, кто не любит писать велосипеды, а использует готовые инструменты. Да, есть некоторый каламбур, учитывая что речь вообще-то про userver. Нужен ли вам пакетный менеджер - каждый решает самостоятельно. Приведу лишь несколько аргументов за
- Отслеживание уязвимостей в сторонних пакетах;
- Прозрачный процесс обновления зависимостей;
- Упрощение различных процессов сертификации итоговых продуктов. Речь не только про ФСТЭК, больше про внутренние регламенты в организациях;
- Ускорение развертывания рабочего окружения - быстрая адаптация новых сотрудников.
За несколько лет я прочитал много публикаций на русском и английском как же хороши пакетные менеджеры, как же с ними легко и просто решаются все ваши проблемы. Вот прямо серебренная пуля, то чего всегда так не хватало в плюсах! Если кратко - это так. Тем не менее у них хватает своих недостатков. Судя по этим статьям казалось, что использовать пакетный менеджер в разработке очень просто... Вот только про внедрения в крупные проекты почти никто не пишет, и скоро мы поймем почему. Пришло время исправить это упущение, и на примере внедрения Conan в userver также рассмотреть реалии open-source разработки. Нам быстро придется покинуть мир розовых пони, погрузиться в самые дебри и понять, что иногда новые модные проекты держаться на людях, отрицающих все современные методы разработки. Conan Небольшое введение в Conan. Первый доступный релиз Conan вышел 1 декабря 2015 года. Совсем недавно был юбилей, по этому поводу можно открыть шампанское) В 2017 @ZaMaZaN4iKнаписал инструкцию на habr’e, которая стала путеводной звездой в Рунете для любого желающего изучить новый пакетный менеджер. С тех пор многое изменилось, публикация давно утратила актуальность. Сам Conan из модного-молодежного инструмента вырос в зрелый продукт и теперь используется в кровавом энтерпрайзе. Сейчас пакетный менеджер уже предоставляет полноценные инструкции для самых разных сценариев, с ними лучше ознакомиться в оригинале. Акт 2. Завязка Поехали. За точку отсчета берем выход статьи, и соответственно публичный релиз самого userver'a. После недолгого открытого обсуждения c сопровождающими было решено добавить поддержку conan (выбор был между vcpkg и conan). И как говорится - кто предложил, тому и флаг в руки.

pikabu.ru В голове созрел план как осуществить задуманное:
- Большой проект. Слона надо есть по частям. Делаем минимальный рабочий вариант.
- Conan v1/v2.
- Conan-center-index не самоцель, туда рецепт отправляем позднее больше для ревью, нежели ожидая попадание в репозиторий.
Акт 3. Кульминация Пункт 1. Как сделать минимальный вариант. Тут надо понимать, что проект достаточно хорошо структурирован, но всё же не заточен на какие-то нюансы пакетников. Получился вот такой достаточно несложный рецепт Что заработало из коробки:
self.requires('boost/1.79.0') self.requires('libev/4.33') self.requires('spdlog/1.9.0') self.requires('fmt/8.1.1') self.requires('c-ares/1.18.1') self.requires('libcurl/7.68.0') self.requires('cryptopp/8.6.0') self.requires('yaml-cpp/0.7.0') self.requires('cctz/2.3') self.requires('http_parser/2.9.4') self.requires('openssl/1.1.1q') self.requires('rapidjson/cci.20220822') self.requires('concurrentqueue/1.0.3') if self.options.with_postgresql: self.requires('libpq/14.2') if self.options.with_mongodb: self.requires('mongo-c-driver/1.22.0') self.options['mongo-c-driver'].with_sasl = 'cyrus' # да, это не по канону if self.options.with_redis: self.requires('hiredis/1.0.2') if self.options.with_rabbitmq: self.requires('amqp-cpp/4.3.16') if self.options.with_utest: self.requires('gtest/1.12.1') self.requires('benchmark/1.6.2')
Из коробки не заработали зависимости для
- grpc
- jemalloc
- clickhouse
- gssapi (kerberos)
Случился успешный успех. Userver собирался и работал с совсем другими версиями библиотек.

ГАМТ России, «Мертвые души» Казалось бы, ну всё, работа почти закончена. Можно праздновать. Всё это было сделано за полгода после выхода в open-source. Возможно, многим знакомо правило 80/20. Так вот Как можно догадаться, это было 20% всей работы. Минимальный рабочий вариант есть, идем к следующему пункту. Пункт 2. Какую же версию выбрать. В этот момент уже появился Conan v2 beta, новый функционал добавлялся каждый месяц, а документация не обновлялась или вообще отсутствовала. Поэтому пошел самым простым путем. Писать рецепт под v1, поддерживая полную совместимость с v2. Это было сложно, но возможно (иногда). За полгода именно такой рецепт у нас и получилось, и оно работало. Пункт 3. Conan-center-index. Боль К этому пункту пришел только спустя год после п.1 и п.2. Open-source дело неблагодарное, вам не скажут спасибо, ваш pr не посмотрят, а иногда и просто отменят без объяснений. Стоит готовиться к такому.

ГАМТ России, «Женитьба» Растущая популярность сыграла злую шутку с сопровождающими cci. Сюда же попал процесс миграции всех рецептов на v2. Именно в это время я решил туда принести один из самых больших рецептов. Угадайте было ли у них свободное время посмотреть мой pr. Правильно, нет. А дальше самое интересное. Conan работает с деревом зависимостей и все они должны быть друг с другом согласованы (сейчас подход сделали более мягким). Что это значит и как работает.

Один рецепт не может содержать несколько версий одной библиотеки Итоговый рецепт может содержать только зависимости одной версии. Вариант как на картинке не сработает. Т.е. наш рецепт должен иметь такие зависимости:
- Рецепт X/3.0
- Рецепт Y/0.1 <- Рецепт X/3.0
- Рецепт Z/0.1 <- Рецепт X/3.0
Conan-center-index публичное пространство, новые версии библиотек выходят достаточно часто (минорная версия условной zlib/curl/openssl раз в месяц принесет вам адскую боль). А теперь попробуйте успеть оформить успешный pull request, да так, чтобы все версии совпали. Кстати, все опции сборки тоже должны совпадать.

Один рецепт не может содержать несколько версий одной библиотеки с разными опциями Такое тоже не сработает. Оформление PR в conan‑center‑index Как же выглядел процесс оформления pull request в conan-center-index в 2022-2024 годах:
- Оформляем pr с зелеными билдами;
- Сопровождающий пишет замечание;
- Исправляем замечание. Падает сборка из-за конфликтов версий библиотек, потому что в каком-то одном пакете обновили версию, а в другом нет;
- Обновляем версии, снова зеленый билд;
- Ждем от 2 недель до месяца;
- Сопровождающий перезапускает сборку, и она опять падает т.к. в v2 что-то поменялось за это время;
Снова пишет замечание. Поздравляю, вы вернулись в П.2.

ГАМТ России, «Перечитывая Чехова» Небольшое лирическое отступление. Команда JFrog решала проблему. Что они сделали:
- Автоматический апрув pr, которые только поднимали версии зависимостей
- Разрешили ранжирование версий зависимостей, т.е. вот такое стало работать
self.requires("zlib/[>=1.2.11 <2]") self.requires("openssl/[>=1.1 <4]")
Меня хватило на год работы в таком режиме. Извините, но всё-таки нет, это невозможно. Рабочий рецепт был много раз, но принимать мы его не хотели) Дело кончилось здесь Как вы поняли, этот пункт оказался самым сложным и, так и не был выполнен за годы. Но об этом позже Акт 4. Спад А что дальше? Внимательный читатель заметил, что рецепт то у нас минимальный, мы не поддержали много модулей. Далее grpc и jemalloc были включены уже командой Яндекса в лице @Anton3 Для clickhouse потребовалось намного больше работы. Здесь используется clickhouse-cpp, а она содержала достаточно простой CMakelists, который не поддерживал install. В качестве дополнительного упражнения clickhouse-cpp содержала ровно один заголовок abseil. Это уже значило, что для большого проекта нам нужно линковать именно библиотеку, а не использовать этот заголовок, либо придумать что-то с пространством имен. Да, решили что лучше тащить целиком весь abseil в таком случае. Поэтому пришлось сначала сделать опции компиляции, а потом разделить shared и static сборки (тут спасибо @enmk). Теперь мы возвращаемся к conan-center-index. Поскольку мы опираемся в своем рецепте на библиотеки оттуда, то и здесь пришлось clickhouse-cpp добавить туда. Маленький и аккуратный рецепт, всё самое интересное реализовали в cmake, а здесь просто обертка снаружи. Но это заняло 3 месяца. Вишенкой на торте стала поддержка kerberos или же krb5. Продвинутые читатели возможно догадались в чем дело. krb5 тоже не было в conan-center-index - значит наша работа его добавить… Но не тут то было,
- Собирается оно Autotools;
- make install работает не так как нам нужно;
- Зависимости прибиты гвоздями, например будет использоваться системный openssl, а не тот что мы несем через conan, и в итоге получаем две версии openssl в конечном бинарнике;
- Репозиторий на гитхабе это зеркало, а патчи то они принимают по почте.
Была, не была. Попробуем малой кровью, conan-center-index позволяет нести патчи с собой. С одной стороны conan удобный инструмент, с другой стороны conan-center-index обычно требует от вас рецепт библиотеки, которая поддерживается максимальным количеством систем. Т.е. userver мы собираем только под linux и только статически с динамическим рантаймом. Теперь же нам предстоит написать рецепт, который поддерживает:
- macOS, Linux, Windows
- clang, gcc, msvc, mingw-gcc, clang-cl
- динамическую и статическую библиотеки
- динамический и статический рантайм
Таким нехитрым образом вместо отладки одной сборки, мы получили больше дюжины, а учитывая что под каждую систему несколько версий компиляторов, то там 20+ вариантов. Так сказать, упростили себе задачу. Путем нехитрых изысканий находим
From ccd48457ae137a32c627afe56f3d6dcfaf6ec1bd Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <<a href="mailto:anonymous.maarten@gmail.com" class="postLink" rel="noopener noreferrer nofollow">anonymous.maarten@gmail.com>
Date: Thu, 3 Sep 2020 20:55:07 +0200
Subject: [PATCH] Use PKG _CHECK_MODULES to system library com_err
To: krbdev@mit.edu
Да, нас сильно выручил товарищ Anonymous Maarten, который еще в 2020 написал заготовки рецепта и нужные патчи… которые за 2 года так и не были приняты в krb5. Делаем первый подход. Спустя несколько итераций всё заработало, да одна беда… На Linux krb5 make install написан, что подразумевается только установка в систему, чего мы делать не собираемся… Ушел думать как это дело починить. Снова пришла помощь откуда не ждали. Параллельно появился другой pull request, который в общем-то поддерживал намного меньше вариантов, но исправлял ту самую проблему make install. Вооружившись это патчем делаем второй подход. За полгода удалось дотянуть до рабочего решения, пускай и жертвой пал Windows, а также статическая линковка openssl. Рецепт не кажется сложным с высоты пройденного пути, но на тот момент отсутствовала любая документация по AutotoolsToolchain и AutotoolsDeps, можно было только смотреть в чужие рецепты. Ситуация с NMakeToolchain, NMakeDeps аналогичная. Спустя 2 года после старта работ krb5 заехал в conan-center-index, но к сожалению, из-за невозможности статической линковки openssl его нельзя было использовать. Спустя еще год, уже в 2025 в результате обновления версий, принятия части патчей в upstream krb5 рецепт наконец-то стал полностью рабочим. Можно попробовать включать поддержку в userver, но это будет уже совсем другая история)

ГАМТ России, «Ревизор» Уже в прошлом году @antoshkkaрешил повторно попробовать затащить userver в conan-center-index. Далее случилось ожидаемое:
I see this recipe is huge and complex to maintain, so I would like to know if it would be possible to start with something really simple
Комментировать только портить. Акт 5. Развязка Стартовали работы над Conan в userver в середине 2022 года. Минимальный рабочий рецепт был готов к декабрю этого же года. Весь 2023 и 2024 ушли на доводку рецепта, обновление Conan’a и работу с оставшимися зависимостями На текущий момент рецепт userver использует 54 зависимости:
abseil/20240116.2 abseil/20250127.0 amqp-cpp/4.3.27 autoconf/2.71 automake/1.16.5 b2/5.4.2 benchmark/1.9.4 bison/3.8.2 boost/1.86.0 bzip2/1.0.8 c-ares/1.34.6 cctz/2.4 cityhash/1.0.1 clickhouse-cpp/2.5.1 cmake/3.31.11 cmake/4.2.3 concurrentqueue/1.0.3 cryptopp/8.9.0 cyrus-sasl/2.1.28 flex/2.6.4 fmt/11.0.2 gnu-config/cci.20210814 googleapis/cci.20230501 grpc/1.72.0 gtest/1.17.0 hiredis/1.3.0 icu/76.1 jemalloc/5.3.0 libbacktrace/cci.20210118 libcurl/7.86.0 libev/4.33 libiconv/1.18 libnghttp2/1.61.0 libpq/17.7 librdkafka/2.13.0 libtool/2.4.7 lz4/1.9.4 m4/1.4.19 meson/1.10.0 mongo-c-driver/1.30.6 ninja/1.13.2 openssl/3.3.2 opentelemetry-proto/1.9.0 pkgconf/2.1.0 pkgconf/2.5.1 protobuf/5.29.3 pugixml/1.15 rapidjson/cci.20230929 re2/20230301 snappy/1.1.10 sqlite3/3.51.0 yaml-cpp/0.8.0 zlib/1.3.1 zstd/1.5.7
Внимательный читатель, наверное, заметил, что по какой-то причине некоторые зависимости содержатся несколько раз с разными версиями. Это уже возможности Conan v2. 10 лет назад вряд ли было возможно представить себе инструмент, который позволит собрать вместе зоопарк из Autotools, make, b2, cmake, m4, meson и ninja, чтобы получить итоговую библиотеку и всё это на разных окружениях с автоматической настройкой оного. Безусловно, это огромный шаг вперед для сообщества C и C++. Легко ли принести что-то большое в open-source? Нет. Помогут ли вам? Да, но часто это будут совсем не те люди, от которых ожидаешь помощи.

ГАМТ России, ««Виндзорские жёнушки» Спасибо всем причастным, их список намного больше, чем указанный в статье. Всё это невозможно было бы без каждого из них. Естественно, отдельно нужно отметить @ProCxx и @probuildsystems.-Источник
|