Как стать автором
Обновить

Сборка проектов Си и Си++: от простого к сложному. Часть I. Библиотеки

Уровень сложностиСредний
Время на прочтение12 мин
Количество просмотров21K
Всего голосов 57: ↑55 и ↓2+71
Комментарии37

Комментарии 37

Натолкнула меня написать этот пост следующая история. Я хотел собрать минимального Telegram-бота на Си++, и всё, что мне нужно было, — это подключить библиотеку tgbot-cpp

понимаю, меня это натолкнуло написать свою библиотеку для тг ботов, где эта проблема решена https://github.com/bot-motherlib/TGBM

Аналогичная ситуация. Когда захотел внедрить в свой проект «МедиаТекст» (не опубликован, но скриншот – http://scholium.webservis.ru/Pics/MediaText.png ) код опенсорсного видео FFPlay.c, то, даже просто скомпилировать этот видеопроигрыватель под «Форточки», со всеми зависимостями, оказалось затеей не для слабонервных. А нужно было вставить это видео в выделенную часть клиентской области приложения. Причем, все подобные решения на Гитхабе оказали нереализуемыми, «из коробки».

Задачу решил путем переделки Си-кода в С++-классы и компиляцией в своем проекте.

Вообще, это общая проблема, использование С++ кода из Линкуса в Виндоус.

Таким образом, динамические библиотеки экономят нам память, загружаясь лишь в единственном экземпляре.

Не упомянули один важный момент - при динамической линковке мы автоматически подхватим оптимизации и фиксы в новых версиях тех же libc или libssl при апдейте системы. И ещё нюанс с использованием LGPL библиотек в проприетарных продуктах.

Верно подмечено. Не просто так динамическая линковка везде является дефолтом если на смену подхода нет очень веских причин.

А вот по поводу LGPL меня эта информация обходила стороной и стала новостью:

LGPL позволяет использовать библиотеку в проприетарном продукте, если библиотека подключается динамически (например, через динамические библиотеки .dll, .so).

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

Приятная статья, спасибо большое!

Лично для меня ldd было спасением, когда я пеовый раз компилировал на серваке с кучей версий компиляторов и библиотек.

Ещё про переменную окружения LD_DEBUG стоит знать для расследования случаев посложнее (ldd показывает, что всё есть, но программа всё равно не грузится).

НЛО прилетело и опубликовало эту надпись здесь

Есть в Gentoo пакеты, которые позволяют установить несколько версий и выбрать через симлинк дефолтную версию.

java:

# eselect java-vm list
Available Java Virtual Machines:
  [1]   openjdk-bin-8 
  [2]   openjdk-bin-11 
  [3]   openjdk-bin-17 
  [4]   openjdk-bin-21  system-vm


gcc:

# eselect java-vm list
Available Java Virtual Machines:
  [1]   openjdk-bin-8 
  [2]   openjdk-bin-11 
  [3]   openjdk-bin-17 
  [4]   openjdk-bin-21  system-vm


Но должно быть нечто, позволяющее выбрать необходимую версию. В основном это модули для eselect, который позволяет контролировать используемую версию. Именно такие пакеты с модулями для eselect обычно имеют слоты (до двоеточия версия, после - номер слота):

sys-kernel/gentoo-sources-6.13.0:6.13.0


Ни boost ни cmake не относятся к подобным пакетам, у которых можно было бы установить одновременно несколько версий:

# equery list -p cmake
 * Searching for cmake ....
[-P-] [  ] dev-build/cmake-3.28.5:0
[-P-] [  ] dev-build/cmake-3.30.5:0
[-P-] [  ] dev-build/cmake-3.30.6:0
[-P-] [  ] dev-build/cmake-3.31.3:0
[-P-] [  ] dev-build/cmake-3.31.4-r1:0
[IP-] [  ] dev-build/cmake-3.31.5:0
[-P-] [ -] dev-build/cmake-9999:0


При непонимании темы лучше попросить объясненений, а не приправлять это непонимание хамством для пущей эффектности.

никто не рассказал, что через "make install" можно поставить в отдельный префикс и не трогать всё остальное.


Как поведет себя FindPackage() если cmake тупо ляжет в отдельном префиксе? Или же немного нужно потрогать и даже поплясать чтобы сборщик смог им нормально воспользоваться?

CMake можно тыкать в префикс, где лежат отдельно собранные как надо библиотеки.

Практически каждый модуль CMake дополнительно имеет свой аргумент <Package>_ROOT, чтобы ему дополнительно ткнуть пальцем в конкретный модуль.

Конкретно связка Boost+CMake прошла тяжелый путь от CMake-модуля до CMake-конфига, и какие-то сочетания друг с другом плохо стыкуются. Это да. Ничего не мешает в отдельный префикс положить целый CMake нужной версии.

Собственно о статье - начали с нестыковок между системный окружением и требованиями конкретного проекта, а потом перешли на базовые принципы линковки, заметя проблему под ковер Docker-а. А после сборки в Ubuntu 20, как бинарь в хост-Gentoo использовать?

Опишу собственный подход к такой проблеме:

Системное окружение остается системным окружением, согласно завету пакетного менеджера. Проект или навязывает необходимые зависимости и окружение, или мы сами выбираем под что будем собирать и запускать. В отдельный префикс вручную по инструкциям собираем все то, что или недоступно в системе, или нас не устраивает. На этом шаге больше всего проблем, ибо даже одну систему сборки разные проекты используют по разному, а еще есть meson, autotools, make, и прочий зоопарк, и никто не читает инструкции для инструмента. После страданий с указанием префикса для тех зависимостей, которые не могут с префиксом и out-of-source сборку, берем свой проект, тыкаем CMake в нужные префикс и полетели. И это для разработки. Для релиза/деплоя все пройденные этапы упаковываем в скрипты сборки под целевую платформу, и объединяем со сборкой нашего проекта, кладя в тот же префикс. Итого, имеет почти самодостаточную директорию проекта со всеми зависимостями. Отсюда уже варианты - упаковка в контейнеры, в пакеты для целевой платформы, архивы и все такое.

Описанный подход прекрасно работает для Linux семейства, позволяя деволопить в одной системе, а деплоить в другие, где зависимости нужных версий не доступны. Чуть менее комфортно поддерживать или девелопить в Windows, Mingw сглаживает трудности, и если оставаться в пределах Mingw окружения - то все то же самое. А при намерении нативно собираться MSVC и следовать заветам Windows окружения, приходится собирать зависимости 'под Windows', уговаривать CMake дополнительными флагами и опциями, и страдать над GNU библиотеками, которым для MSVC сборки надо через Cygwin присовывать в autotools черте-что, и не косячить с путями и слэшами.

НЛО прилетело и опубликовало эту надпись здесь

про cmake ничего не понял

Значит нужно собрать старый boost отдельно от системы, подключив к проекту через cmake. Опа! Но cmake тоже новый, и на старую версию boost он ругается ошибками.

что мешает скачать старый cmake с cmake.org или github (gitlab)? Распаковать в какую-нибудь папку, добавить в PATH и вуаля, я так делал.

Но не тут-то было. Оказалось, что в новых версиях такого мощного и любимого всеми сборщика проектов Cmake изменились политики поиска библиотек через функцию FindPackage, что он категорически отказался находить старые версии, как я ни плясал над ним

это видимо про политику CMP0167, но что мешает вернуть старое поведение через cmake_policy? Я посмотрел в версии 3.31, FindBoost.cmake пока на месте.

Такой cmake будет использовать модули поиска пакетов из системной директории потому что понятия не будет иметь как найти эти скрипты "в какой-нибудь папке". Вот у меня он найдет и использует вот этот потому что PATH касается только основного бинаря:

/usr/share/cmake/Modules/FindBoost.cmake

Самый простой и надежный вариант изолировать сборщик и зависимости от системы это Docker.

А какие версии boost и libcurl в итоге нужны? В conancenter около десятка разных имеется под разные ОС и архитектуры. Cкачиваются и ставятся одной командой, сonan install —requires=...

Эта команда также создает все нужные файлы, чтобы CMake нормально все собрала

За вторую часть статьи по библиотекам спасибо.

Но что касается первой части - все это давно решено пакетными менеджерами cpp, вот например: https://vcpkg.link/ports/tgbot-cpp/v/1.7.3/0

Соберет все что нужно со всеми зависимостями, хоть статик хоть динамик.

Также, насчет статической линковки, поправьте меня, но насколько я помню, в статье неправильно написано - из .a файла в бинарь слинкуются не все объектники, а только те, что реально используются.

хм... "давно решено"?
Ну вот я сижу на маке, проект собираю под винду, нужно собрать openssl.
vcpkg install openssl:x64-windows внезапно не работает!
Даже если просунуть тулчейн (которым прекрасно собирается весь проект под винду) - тоже не работает!

А че за агрессия? 😁

Потому что во первых, мой комментарий в контексте статьи, а значит в рамках одной платформы.

Во вторых, Ваш кейс явно очень специфический и у мейнтейнеров есть куда более перспективные задачи, а Ваша стопудов решается установкой винды в Parallels и сборкой нативным MSVC.

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

Тем не менее для Вашего случая вот пример для кросс сборки под линукс, может на его основе как-то можно сделать триплет кастомный для кросс сборки мак-винда: https://stackoverflow.com/questions/58777810/how-to-integrate-vcpkg-in-linux-with-cross-build-toolchain-as-well-as-sysroot

Вы если попробуете - напишите потом мне ответ или даже статью плиз, было бы интересно почитать, зачем такое нужно, почему не решается другими путями и получилось ли что с триплетом. Для сообщества будет открыт еще один кейс.

Нет, тут дело в том, что vcpkg - далеко не "серебряная пуля". В код многих портов лучше вообще не заглядывать, настолько там всё костыльно. А openssl - тот ещё подарок для сборки. У него собственная система конфига на perl, а vcpkg лишь добавляет пару своих "слоёв абстракции".

Однако независимо от openssl - он сам довольно кривой. В режиме кросс-сборки у него компилятор хоста "просачивается" в цель, игнорируя заданный в тулчейне.

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

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

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

А вы в реальности сталкивались с этой "проблемой"?

Я вот динамические разделяемые библиотеки использую, но совсем для другого: как средства расширения функциональности (плагины и т.п.), а также в случае, когда статическая линковка невозможна (С - библиотека и какой-нибудь Бэйсик-интерпретатор, например), или когда реально нужно сократить объем передаваемых по сети данных при обновлении софта (до сих пор есть весьма медленные каналы связи), для разделения логики, или по лицензионным причинам. С нехваткой памяти из-за объема кода ни разу не сталкивался.

Не сталкивался потому что никому в голову не приходило собрать статически бинарь от какого-то браузера или аналогичной софтины. На практике часть библиотек даже невозможно слинковать статически. Вот берем chromium-browser:

ls -l /usr/lib64/chromium-browser/chrome
-rwxr-xr-x 1 root root 272288544 Jan 20 18:45 /usr/lib64/chromium-browser/chrome


272 мегабайта с динамической линковкой, Карл!
А теперь смотрим список сколько библиотек он тянет из системы:

 ldd  /usr/lib64/chromium-browser/chrome
        linux-vdso.so.1 (0x00007ffeebaff000)
        libgobject-2.0.so.0 => /usr/lib64/libgobject-2.0.so.0 (0x00007f6b05efa000)
        libglib-2.0.so.0 => /usr/lib64/libglib-2.0.so.0 (0x00007f6b05dac000)
        libsmime3.so => /usr/lib64/libsmime3.so (0x00007f6b05d7d000)
        libnss3.so => /usr/lib64/libnss3.so (0x00007f6b05c3d000)
        libnssutil3.so => /usr/lib64/libnssutil3.so (0x00007f6b05c0e000)
        libnspr4.so => /usr/lib64/libnspr4.so (0x00007f6b05bcd000)
        libdbus-1.so.3 => /usr/lib64/libdbus-1.so.3 (0x00007f6b05b7a000)
        libatk-bridge-2.0.so.0 => /usr/lib64/libatk-bridge-2.0.so.0 (0x00007f6b05b3b000)
        libatk-1.0.so.0 => /usr/lib64/libatk-1.0.so.0 (0x00007f6b05b14000)
        libcups.so.2 => /usr/lib64/libcups.so.2 (0x00007f6b05a83000)
        libgio-2.0.so.0 => /usr/lib64/libgio-2.0.so.0 (0x00007f6b05894000)
        libfontconfig.so.1 => /usr/lib64/libfontconfig.so.1 (0x00007f6b05846000)
        libz.so.1 => /usr/lib64/libz.so.1 (0x00007f6b05829000)
        libzstd.so.1 => /usr/lib64/libzstd.so.1 (0x00007f6b0576c000)
        libexpat.so.1 => /usr/lib64/libexpat.so.1 (0x00007f6b05742000)
        libpng16.so.16 => /usr/lib64/libpng16.so.16 (0x00007f6b05708000)
        libwebpdemux.so.2 => /usr/lib64/libwebpdemux.so.2 (0x00007f6b05701000)
        libwebpmux.so.3 => /usr/lib64/libwebpmux.so.3 (0x00007f6b056f4000)
        libwebp.so.7 => /usr/lib64/libwebp.so.7 (0x00007f6b05681000)
        libfreetype.so.6 => /usr/lib64/libfreetype.so.6 (0x00007f6b055b6000)
        libjpeg.so.62 => /usr/lib64/libjpeg.so.62 (0x00007f6b054fc000)
        libharfbuzz-subset.so.0 => /usr/lib64/libharfbuzz-subset.so.0 (0x00007f6b0538e000)
        libharfbuzz.so.0 => /usr/lib64/libharfbuzz.so.0 (0x00007f6b0524e000)
        libopenh264.so.7 => /usr/lib64/libopenh264.so.7 (0x00007f6b0514e000)
        libm.so.6 => /usr/lib64/libm.so.6 (0x00007f6b0509c000)
        libX11.so.6 => /usr/lib64/libX11.so.6 (0x00007f6b04f54000)
        libXcomposite.so.1 => /usr/lib64/libXcomposite.so.1 (0x00007f6b04f4f000)
        libXdamage.so.1 => /usr/lib64/libXdamage.so.1 (0x00007f6b04f4a000)
        libXext.so.6 => /usr/lib64/libXext.so.6 (0x00007f6b04f35000)
        libXfixes.so.3 => /usr/lib64/libXfixes.so.3 (0x00007f6b04f2d000)
        libXrandr.so.2 => /usr/lib64/libXrandr.so.2 (0x00007f6b04f1e000)
        libXtst.so.6 => /usr/lib64/libXtst.so.6 (0x00007f6b04f16000)
        libgbm.so.1 => /usr/lib64/libgbm.so.1 (0x00007f6b04f0f000)
        libxcb.so.1 => /usr/lib64/libxcb.so.1 (0x00007f6b04ee3000)
        libxkbcommon.so.0 => /usr/lib64/libxkbcommon.so.0 (0x00007f6b04e9a000)
        libffi.so.8 => /usr/lib64/libffi.so.8 (0x00007f6b04e8d000)
        libpango-1.0.so.0 => /usr/lib64/libpango-1.0.so.0 (0x00007f6b04e1f000)
        libcairo.so.2 => /usr/lib64/libcairo.so.2 (0x00007f6b04cdb000)
        libudev.so.1 => /usr/lib64/libudev.so.1 (0x00007f6b04c85000)
        libasound.so.2 => /usr/lib64/libasound.so.2 (0x00007f6b04b97000)
        libpulse.so.0 => /usr/lib64/libpulse.so.0 (0x00007f6b04b41000)
        libFLAC.so.12 => /usr/lib64/libFLAC.so.12 (0x00007f6b04adf000)
        libxml2.so.2 => /usr/lib64/libxml2.so.2 (0x00007f6b04985000)
        libatspi.so.0 => /usr/lib64/libatspi.so.0 (0x00007f6b0494c000)
        libminizip.so.1 => /usr/lib64/libminizip.so.1 (0x00007f6b0493e000)
        libxslt.so.1 => /usr/lib64/libxslt.so.1 (0x00007f6b048fb000)
        libgcc_s.so.1 => /usr/lib/gcc/x86_64-pc-linux-gnu/14/libgcc_s.so.1 (0x00007f6b048cd000)
        libc.so.6 => /usr/lib64/libc.so.6 (0x00007f6b046f3000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f6b165fd000)
        libpcre2-8.so.0 => /usr/lib64/libpcre2-8.so.0 (0x00007f6b0464e000)
        libplc4.so => /usr/lib64/libplc4.so (0x00007f6b04647000)
        libplds4.so => /usr/lib64/libplds4.so (0x00007f6b04641000)
        libgnutls.so.30 => /usr/lib64/libgnutls.so.30 (0x00007f6b04443000)
        libgmodule-2.0.so.0 => /usr/lib64/libgmodule-2.0.so.0 (0x00007f6b0443c000)
        libmount.so.1 => /usr/lib64/libmount.so.1 (0x00007f6b043c7000)
        libsharpyuv.so.0 => /usr/lib64/libsharpyuv.so.0 (0x00007f6b043be000)
        libbz2.so.1 => /usr/lib64/libbz2.so.1 (0x00007f6b043a9000)
        libbrotlidec.so.1 => /usr/lib64/libbrotlidec.so.1 (0x00007f6b0439a000)
        libgraphite2.so.3 => /usr/lib64/libgraphite2.so.3 (0x00007f6b04375000)
        libstdc++.so.6 => /usr/lib/gcc/x86_64-pc-linux-gnu/14/libstdc++.so.6 (0x00007f6b040f7000)
        libXrender.so.1 => /usr/lib64/libXrender.so.1 (0x00007f6b040ea000)
        libdrm.so.2 => /usr/lib64/libdrm.so.2 (0x00007f6b040d3000)
        libXau.so.6 => /usr/lib64/libXau.so.6 (0x00007f6b040cd000)
        libXdmcp.so.6 => /usr/lib64/libXdmcp.so.6 (0x00007f6b040c5000)
        libfribidi.so.0 => /usr/lib64/libfribidi.so.0 (0x00007f6b040a3000)
        libxcb-render.so.0 => /usr/lib64/libxcb-render.so.0 (0x00007f6b04093000)
        libxcb-shm.so.0 => /usr/lib64/libxcb-shm.so.0 (0x00007f6b0408e000)
        libpixman-1.so.0 => /usr/lib64/libpixman-1.so.0 (0x00007f6b03fee000)
        libcap.so.2 => /usr/lib64/libcap.so.2 (0x00007f6b03fe1000)
        libpulsecommon-17.0.so => /usr/lib64/pulseaudio/libpulsecommon-17.0.so (0x00007f6b03f55000)
        libogg.so.0 => /usr/lib64/libogg.so.0 (0x00007f6b03f4b000)
        libicui18n.so.76 => /usr/lib64/libicui18n.so.76 (0x00007f6b03c15000)
        libicuuc.so.76 => /usr/lib64/libicuuc.so.76 (0x00007f6b03a10000)
        libXi.so.6 => /usr/lib64/libXi.so.6 (0x00007f6b039fc000)
        libidn2.so.0 => /usr/lib64/libidn2.so.0 (0x00007f6b039c7000)
        libunistring.so.5 => /usr/lib64/libunistring.so.5 (0x00007f6b037e1000)
        libtasn1.so.6 => /usr/lib64/libtasn1.so.6 (0x00007f6b037cc000)
        libhogweed.so.6 => /usr/lib64/libhogweed.so.6 (0x00007f6b0377f000)
        libnettle.so.8 => /usr/lib64/libnettle.so.8 (0x00007f6b0372d000)
        libgmp.so.10 => /usr/lib64/libgmp.so.10 (0x00007f6b03685000)
        libblkid.so.1 => /usr/lib64/libblkid.so.1 (0x00007f6b03625000)
        libbrotlicommon.so.1 => /usr/lib64/libbrotlicommon.so.1 (0x00007f6b03602000)
        libsndfile.so.1 => /usr/lib64/libsndfile.so.1 (0x00007f6b03576000)
        libasyncns.so.0 => /usr/lib64/libasyncns.so.0 (0x00007f6b03570000)
        libicudata.so.76 => /usr/lib64/libicudata.so.76 (0x00007f6b0170b000)
        libvorbis.so.0 => /usr/lib64/libvorbis.so.0 (0x00007f6b016db000)
        libvorbisenc.so.2 => /usr/lib64/libvorbisenc.so.2 (0x00007f6b01643000)
        libopus.so.0 => /usr/lib64/libopus.so.0 (0x00007f6b015e1000)
        libmpg123.so.0 => /usr/lib64/libmpg123.so.0 (0x00007f6b01598000)
        libmp3lame.so.0 => /usr/lib64/libmp3lame.so.0 (0x00007f6b01520000)
        libmvec.so.1 => /usr/lib64/libmvec.so.1 (0x00007f6b01425000)

Да это же половину всего дистрибутива нужно будет в память загрузить! И это только код не считая уже самого контента, для которого места уже совсем не останется.

При чем тут библиотеки, которые уже есть в системе. Явно же речь о причинах для создания собственных.

PS;

Ой, что это что такое хотя бы самая первая библиотека в вашем списке: linux-vdso.so.1? А где она находится ?

Ой, что это за циферки рядом с именем "файла" - 0x00007f6b05efa000? Неужели размер?

0x00007f6b05efa000 == 16 952 335 507 456 (dec) байт, серьезно? Почти 17 терабайт, какой кошмар!

Странный комментарий. Я вообще не понимаю в чем вопрос и какие должны быть причины для создания собственных библиотек в контексте динамической или статической линковки.

Рядом с SO файлами в выводе ldd пишутся не террабайты, а контрольная сумма.

И по поводу VDSO тоже немного ликбеза:

linux-vdso.so.1 — это виртуальный общий объект, предоставляемый ядром Linux. Это не физический файл, расположенный на диске, а механизм, предоставляемый ядром.

Цель vDSO (virtual dynamic shared object) — оптимизировать производительность определённых системных вызовов, позволяя им выполняться непосредственно в пользовательском пространстве, без необходимости переключения контекста в режим ядра.

Библиотека позволяет быстро выполнять определённые функции ядра, такие как функции времени, для доступа к которым не требуется какой-либо особый уровень привилегий. Вызов этих функций позволяет получить общедоступную системную информацию без фактического вызова системного вызова.

Имя vDSO отличается на разных архитектурах и часто его можно увидеть в выводе утилит, подобных ldd. 

Рядом с SO файлами в выводе ldd пишутся не террабайты, а контрольная сумма.

А вот man говорит что это `For each dependency, ldd displays the location of the matching object and the (hexadecimal) address at which it is loaded`

Если у вас серьёзный проект с кучкой бинарников, которые переиспользуют одни библиотеки и всё в целом занимает сотни мегабайт - проблемы те же. В какой нибудь SAP HANA отдельные soшки больше сотни мег весят.

докер-образы тоже с собой тащат всё нужное. И однако никто по этому поводу не возмущается, а, наоборот, довольны.

soname - специальное поле записываемое в динамическую библиотеку, по которому на самом деле динамический линковщик ищет библиотеку. Для старта можно почитать статью на вики

Извините, довольно далек от разработке на линуксе и сборкой на С\++ тоже не каждый день приходится заниматься.. А на одном из первых этапов нельзя было подложить телеграмм библиотеке бинарники нужных версий curl/boost? Или их просто не принято держать в таком виде, и только сорсы, только хардкор? Или у неё статическая линковка на них, и бинарники в этом случае не помогли бы?

Если понимать процесс, обеспечить гарантию что не подцепится другая версия из системы, а подключится именно из нужной директории, то так можно разок вручную собрать если никому не рассказывать. Но по уму нужно написать универсальный рецепт, которым легко можно будет воспользоваться независимо от ОС, с рассчетом на долгосрочную поддержку, с пониманием что может измениться в новых версиях зависимостей.

Видел, что в репозиторий добавляют бинарники зависимостей и не парятся с их обновлением вообще. Но это тупик. Тот проект, например, что я видел, можно было собрать только GCC не старше 5.5.

Бинарники в репозитории - это треш. Лучше всё таки свой бранч third-party библиотеки, с которым всё точно собирается (и который периодически обновляется с проверкой, что ничего не сломалось).

Да, это дичь. Простым это кажется только в первый день, когда добавил и быстро скомпилил. А дальше будет усугубляющийся аццкий ад.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий