Управление пакетами C/C++ может быть запутанным, но его можно значительно улучшить.
В этой статье я расскажу о проблемах управления пакетами в C/C++ и о том, как Conan может помочь вам решить эти проблемы.
Начните с простого
Давайте начнем с тривиальной задачи, беря значения из аргументов командной строки и выводя их на стандартный вывод:
+root - mainapp.c - CMakeLists.txt
Мы используем функцию getops из стандартной библиотеки, чтобы получить аргументы -a
и -b
и связанные с ними значения, а затем выводим их.
Затем мы используем CMake для компиляции проекта в исполняемый файл с именем myproject
.
Настроим этот проект:
cmake --build ./build --config Debug --target MyProject -j 10 --
А теперь запустим приложение:
./build/MyProject -a foo -b bar
Результат:
a: foo b: bar
Это работает, но «работает» не всегда достаточно.
Функция Getops использует глобальные переменные, такие как optarg
для хранения связанных значений аргументов, optopt
для имени переменной и optind
для индекса.
Это означает, что функция не является потокобезопасной, и вы можете анализировать только один входной вектор в одном потоке.
Если у вас есть несколько входных векторов, вам нужно анализировать эти векторы последовательно, и, что еще хуже, вы должны каждый раз сбрасывать состояние анализатора.
Еще одна известная проблема getops — это сообщение об ошибке, которое скрыто внутри, что затрудняет перенаправление.
Итак, чтобы решить эту проблему, я решил использовать parg, библиотеку C с открытым исходным кодом для разбора аргументов с безопасностью потоков путем выделения структуры и использования ее для мониторинга состояния анализатора вместо глобальных переменных.
Но как я буду использовать этот код?
Вариант 1. Использовать исходный код
Мы можем использовать gits submodule tool, чтобы клонировать parg в каталог нашего проекта, а затем использовать cmake для его компиляции и связывания с нашим исполняемым файлом.
git submodules add https://github.com/jibsen/parg.git
Это создаст файл .gitsubmodules
со следующим содержимым:
[submodule "parg"] path = parg url = https://github.com/jibsen/parg.git
Теперь мы можем получить репозиторий с его исходным кодом:
git submodules init
У нас есть весь исходный код parg
в каталоге parg
.
Теперь нам нужно добавить это в наши CMakeLists.txt
конфигурации:
Мы добавили каталог и связали библиотеку с нашим исполняемым файлом.
Теперь мы можем изменить наш код, чтобы использовать библиотеку parg
:
Теперь наш код является потокобезопасным, и мы можем легко анализировать несколько векторов аргументов, если это необходимо.
Давайте скомпилируем:
cmake --build ./build --config Debug --target MyProject -j 10 --
Бегать:
./build/MyProject -a foo -b bar
Результат:
a: foo b: bar
Основное ограничение добавления исходного кода заключается в том, что нам нужно компилировать его каждый раз, когда мы компилируем исполняемый файл, и это может привести к увеличению времени компиляции.
Вариант 2 . Использовать скомпилированную библиотеку
В этом варианте мы скомпилируем parg
один раз, сохраним библиотеку, а затем свяжем библиотеку напрямую без повторной компиляции.
После компиляции нашего проекта parg
будет создан файл libaprg.a
.
Теперь мы можем внести некоторые изменения в CMakeLists и код C, чтобы заставить его работать:
Смейк:
А теперь код C:
Единственное изменение в коде C состоит в том, что теперь заголовочный файл помещается в связанную библиотеку.
В файле CMakeLists мы удалили add_subdirectories
и вместо этого связали скомпилированную библиотеку с ее путем, чтобы мы не собирались компилировать ее каждый раз при компиляции приложения.
Теперь давайте снова скомпилируем и запустим.
cmake --build ./build --config Debug --target MyProject -j 10 --
Бегать:
./build/MyProject -a foo -b bar
Результат:
a: foo b: bar
Оно работает! это лучше, но может быть и лучше!
Проблема с этим подходом в том, что он загрязняет рабочую среду, а когда у вас много зависимостей, а у ваших зависимостей есть зависимости второго или даже третьего уровня, управлять ими становится чрезвычайно громоздко.
Вариант 3. Используйте Конана, чтобы поплакать вслух!
Conan — это менеджер пакетов C\C++ с открытым исходным кодом, разработанный JFrog.
Он использует артефакт JFrog для управления зависимостями.
В основном он используется платформой Программное обеспечение как услуга (SaaS), но также может использоваться как локальный репозиторий для внутренних нужд компании.
В качестве предварительного условия у вас должен быть установлен Python.
Во-первых, нам нужно установить Conan из pip с помощью следующей команды:
pip install conan
Теперь нам нужно создать профиль для наших конфигураций:
conan profile new myprofile
Вывод будет:
Empty profile created: C:\Users\user\.conan\profiles\myprofile
Теперь мы можем перейти к файлу профиля и выставить нужные нам конфигурации.
В моем случае я использовал следующее:
[env] CC=C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin/gcc.exe CXX=C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin/g++.exe [settings] os_build=Windows arch_build=x86 os=Windows arch=x86 compiler=gcc compiler.version=8 compiler.libcxx=libstdc++11 build_type=Release
Это устанавливает все пути и переменные, необходимые для компиляции проекта.
Установив профиль, я создам conanfile.txt
в каталоге проекта.
Глядя на репозиторий parg в Conan center, я могу найти все необходимые мне детали.
[requires] parg/1.0.2 [generators] cmake
Теперь мой проект зависит от parg
, и Конан знает об этом, поэтому он должен быть подготовлен для cmake.
Теперь я могу запустить команду установки Conan:
conan install . -pr=myprofile
Выход:
Configuration: [settings] arch=x86 arch_build=x86 build_type=Release compiler=gcc compiler.libcxx=libstdc++11 compiler.version=8 os=Windows os_build=Windows [options] [build_requires] [env] CC=C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin/gcc.exe CXX=C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin/g++.exe conanfile.txt: Installing package Requirements parg/1.0.2 from 'conancenter' - Cache Packages parg/1.0.2:a955db98e980a5ab86ae50d6df8bfee361185c27 - Cache Installing (downloading, building) binaries... parg/1.0.2: Already installed! conanfile.txt: Generator txt created conanbuildinfo.txt conanfile.txt: Generator cmake created conanbuildinfo.cmake conanfile.txt: Aggregating env generators conanfile.txt: Generated conaninfo.txt conanfile.txt: Generated graphinfo
Прохладный!
Теперь давайте добавим некоторые изменения в файл CMakeLists.txt
, чтобы он взял библиотеку со следующими зависимостями:
Итак, мы включили информацию о сборке из папки с исходным кодом (она была создана при установке Conan) и связали библиотеку.
Теперь мы можем использовать тот же код C, что и в первом примере, как показано ниже:
Разница в том, что теперь мы не засоряем наш код подмодулями git и нам не нужно каждый раз компилировать код.
Точно так же нам больше не нужно самостоятельно управлять зависимостями и артефактами.
Теперь давайте снова скомпилируем и запустим этот код:
cmake --build ./build --config Debug --target MyProject -j 10 --
Бегать:
./build/MyProject -a foo -b bar
Результат:
a: foo b: bar
Сладкий!