Акт 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
pikabu.ru

В голове созрел план как осуществить задуманное:

  1. Большой проект. Слона надо есть по частям. Делаем минимальный рабочий вариант.

  2. Conan v1/v2. 

  3. 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 годах:

  1. Оформляем pr с зелеными билдами;

  2. Сопровождающий пишет замечание;

  3. Исправляем замечание. Падает сборка из‑за конфликтов версий библиотек, потому что в каком‑то одном пакете обновили версию, а в другом нет;

  4. Обновляем версии, снова зеленый билд;

  5. Ждем от 2 недель до месяца;

  6. Сопровождающий перезапускает сборку, и она опять падает так как в v2 что‑то поменялось за это время;

    Снова пишет замечание. Поздравляю, вы вернулись в П.2.

ГАМТ России, «Перечитывая Чехова»
ГАМТ России, «Перечитывая Чехова»

Небольшое лирическое отступление. Команда JFrog решала проблему. Что они сделали:

  1. Автоматический апрув pr, которые только поднимали версии зависимостей

  2. Разрешили ранжирование версий зависимостей, то есть вот такое стало работать

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 <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.

Комментарии (16)


  1. eao197
    05.05.2026 09:36

    Статья затрагивает сразу несколько важных аспектов и OpenSource здесь только один из. Возможно даже не самый важный.

    Позволю себе заострить внимание вот на каком моменте: имхо, системы управления зависимостями для C++ не должны быть централизованными (как это происходит с Conan и vcpkg). Вместо того, чтобы сделать рецепт для условной библиотеки X (которая зависит от Y и Z) и отправить PR на включение этого рецепта в Conan/vcpkg, было бы лучше опубликовать этот рецепт где-то отдельно. Например, в репозитории библиотеки X.

    В рецепте для X были бы ссылки на аналогичные рецепты для Y и Z, что-то вроде (синтаксис условный):

    "depends": [
      {"name": "Y", "receipe": "https://github.com/y-lib-org/receipes/v1.2.3"},
      {"name": "Z", "receipe": "https://github.com/z-lib-org/receipes/v3.2.1"}
    ],
    ...
    

    В проекте, где потребовался бы X, было бы что-то вроде:

    "depends": [
      {"name": "X", "receipe": "https://github.com/x-lib-org/receipes/v0.1.1"},
    ],
    ...
    

    При загрузке зависимостей сперва бы сходили в репозиторий X, достали бы оттуда рецепт. Потом сходили бы по репозиториям, которые описаны в рецепте X, достали бы оттуда информацию и т.д.

    При такой распределенной структуре можно было бы не зависеть от того, что в условный conan-center PR могут принимать неделями. Да и для каких-то библиотек можно собственные рецепты писать и поддерживать (даже если авторы библиотек не хотят потом эти рецепты к себе в апстрим забирать).


    1. ReadOnlySadUser
      05.05.2026 09:36

      Но... Conan так умеет. А если чего-то не умеет, он довольно легко расширяется (ибо питончик).

      Рецепты хранятся вместе с кодом, registry можно зеркалировать и поднимать свои


      1. eao197
        05.05.2026 09:36

        Рецепты хранятся вместе с кодом, registry можно зеркалировать и поднимать свои

        Это хорошо для внутрикорпоративных разработок. Сделал зеркало и бодаешься с ним сам потом.

        А если делаешь какую-то программку для себя или собственную библиотеку, то это уже оверкилл, как по мне. Между тем, каких-то зависимостей в Conan-е может не быть (или там какие-то старые версии).


        1. ReadOnlySadUser
          05.05.2026 09:36

          Выбор-то небольшой) Мы или пилим централизованно и одинаково, или оставляем всё как было)

          Вариант сделать свою систему управления зависимостями никуда не делся. Если не прибивать это дело гвоздями централизованно, то разницы между "я тут свой configure написал" и Conan будет никакой)


          1. eao197
            05.05.2026 09:36

            Мы или пилим централизованно и одинаково

            О том, к чему это ведет, видно из статьи.

            Сам я на Conan давно забил, делаю обновления только на vcpkg. Но и там были случаи, когда принятия PR нужно было ждать неделями.

            или оставляем всё как было

            Не понимаю такой двоичной логики. Есть еще вариант – комитет таки перестает вредить (шутка) и рожает наконец стандартное описание зависимостей. Тогда все будут описывать зависимости проектов в стандартом виде. Просто не обязательно собирать готовые рецепты в одном месте (как это сейчас происходит с Conan и vcpkg).

            Помнится, какой-то подкомитет посвященный тулингу в комитете по стандартизации C++ есть. Результатов пока не видно. Но он есть.


            1. ReadOnlySadUser
              05.05.2026 09:36

              Если их не собирать в одном месте, то и гарантий собираемости не обеспечить. А если не обеспечить, то и зачем оно тогда нужно, просто банально проще идти старым путём


              1. eao197
                05.05.2026 09:36

                Если их не собирать в одном месте, то и гарантий собираемости не обеспечить.

                Это слишком сильное и, полагаю, бездоказательное утверждение.


                1. ReadOnlySadUser
                  05.05.2026 09:36

                  Что бездоказательного в том, что доверяя владение другому лицу, ты больше ничего не можешь гарантировать? Внешние ссылки склонны умирать, устаревать и просто бесследно исчезать


                  1. eao197
                    05.05.2026 09:36

                    Что бездоказательного

                    Чуть меньше чем все.

                    что доверяя владение другому лицу, ты больше ничего не можешь гарантировать?

                    Как будто отправляя рецепт в Conan или в vcpkg вы поступаете как-то иначе.

                    Внешние ссылки склонны умирать, устаревать и просто бесследно исчезать

                    Что в рецепте Conan-а, что в порте vcpkg лежат точно такие же внешние ссылки, которые склонны умирать, устаревать и просто бесследно исчезать ©

                    PS. Предлагаю воздержаться от продолжения этого бесполезного обсуждения ваших глупостей.


          1. Xadok Автор
            05.05.2026 09:36

            Вариант сделать свою систему управления зависимостями никуда не делся. Если не прибивать это дело гвоздями централизованно, то разницы между "я тут свой configure написал" и Conan будет никакой)

            Это тема следующей статьи, и почему так делать не нужно. Пока аргументы готовлю


            1. ReadOnlySadUser
              05.05.2026 09:36

              Я знаю, почему так делать не надо) Так уж вышло, что я и сам немало времени провел в работе над этими бедами с зависимостями, я бы может и написал даже об этом статью, если бы она не сводилась к тому, что собственные велосипеды неизбежны, а чужие велосипеды вам не подойдут и зря вы читали эту статью)

              Комментарием я просто хотел сказать, что выбор небольшой. Хранить рецепты централизованно больно, а распределенно нельзя, ибо результат будет эквивалентен отсутствию пакетного менеджера.


  1. DungeonLords
    05.05.2026 09:36

    Жаль The Qt Group прекратили официальную поддержку Conan… Чем больше организаций пользуются технологией, тем лучше для всех.


    1. Verona90210
      05.05.2026 09:36

      Исход логичный, Qt это вещь в себе со своей экосистемой, пытаться запихнуть ее в общую гребенку пакетника было странной идеей


      1. Xadok Автор
        05.05.2026 09:36

        Вы либо используете пакетник и тянете все зависимости через него. Либо страдаете. Qt срезали косты и переложили ответственность на пользователей, чтобы это была их проблема.


  1. Verona90210
    05.05.2026 09:36

    Мазохистом надо быть чтобы тянуть Autotools в Конан... За упорство респект конечно, но обычно на таких задачах люди и выгорают в ноль


  1. Playa
    05.05.2026 09:36

    Как только userver выложили в опенсорс было желание написать под него порт для vcpkg, но на тот момент сложилось впечатление, что система сборки была написана под внутреннюю кухню - одна платформа, вроде бы некоторые зависимости нужно было выкорчёвывать, install step не описан (могу ошибаться). Надо как-нибудь на досуге попробовать совершить второй заход. С добавлением/обновлением портов в vcpkg опыт положительный.