Подключение внешних библиотек
Рассмотрим пример подключения внешней библиотеки OpenCV и сборку программы в терминале, а также в IDE Visual Studio и CLion, используя разные системы сборки.
OpenCV
Популярная библиотека для работы с изображениями. Будем рассматривать пример подключения библиотеки в программу, написанную на C. Для этого используется OpenCV версии 3.4 (sources), т.к. начиная с версии 4.0 C API было удалено из исходников библиотеки.
Структура баблиотеки выглядит следующим образом:
Общая структура библиотеки может поставляться в виде исходники (sources) + собранная версия библиотеки (build). OpenCV как раз можно скачать в таком варианте.
Mode Name
---- ----
d----- build
d----- sources
-a---- LICENSE.txt
-a---- LICENSE_FFMPEG.txt
-a---- README.md.txt
Нас интересует собранная версия, поэтому рассмотрим подробнее струкутуру директории build:
Mode Name
---- ----
d----- bin
d----- etc
d----- include
d----- java
d----- python
d----- x64
-a---- LICENSE
-a---- OpenCVConfig-version.cmake
-a---- OpenCVConfig.cmake
-a---- setup_vars_opencv3.cmd
Чаще всего в библиотеках/фреймворках встречаются директории include
, bin
, lib
, x86
, x64
, docs
, 3rdparty
, samples
и прочие.
include
Заголовочные файлы, в которых описаны структуры/макросы/функции
bin
Исполняемые файлы и файлы динамических библиотек
lib
Файлы статических библиотек. Чаще всего внутри содержит директории x86
, x64
lib-32
/ x64
Чаще всего файлы статических библиотек, собранных под разные битности
docs
Документация
3rdparty
Сторонние библиотеки
samples
Примеры использования
Рассмотрим структуру поддиректорий include
, bin
, x64
Mode Name
---- ----
d----- opencv
d----- opencv2
Как можно заметить, заголовочные файлы не обязаны все лежать в одной директории, а могут быть расположенны по разным поддиректориям. Также в OpenCV версии 3 видно, что рядом могут лежать заголовочные с C и C++ API.
В OpenCV исполняемые файлы и файлы динамических библиотек самой OpenCV располагаются не в корневой bin
директории. Такое также встречается, когда bin
директорий в проекте несколько и в таком случае необходимые файлы динамический библиотек следует искать в директориях x86
и x64
. Последняя в сброке OpenCV имеется.
vc14
и vc15
– директории, содержащие сборки под разными версиями Visual Studio. vc15
– сборка под VS 2017 и позднее, vc14
– VS 2015. Структура в директориях идентичная, так что будем рассматривать vc15
.
Можно заметить, что и dll
, и lib
файлы представлены в двух вариациях, отличающихся суффиксом d
. В OpenCV файлы библиотек с суффиксом d
собраны с сохранением отладочной информации и их следует использовать при сборке программы в debug-конфигурации. Без суффикса – релизная сборка.
В качестве тестовой программы будем использовать код main.c
, который позволяет открыть изображение, поданной через аргументы командной строки, в отдельном окне.
#include <stdio.h>
#ifdef OPENCV
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <opencv2/imgcodecs/imgcodecs_c.h>
#define WINDOW_NAME "Example"
#endif
int main(int argc, char* argv[])
{
printf("sizeof(size_t) = %zu\n", sizeof(size_t));
if (argc != 2)
{
fprintf(stderr, "ERROR! Usage: %s image-file", argv[0]);
return -1;
}
#ifdef OPENCV
IplImage* img = cvLoadImage(argv[1], CV_LOAD_IMAGE_COLOR);
cvNamedWindow(WINDOW_NAME, CV_WINDOW_AUTOSIZE);
cvShowImage(WINDOW_NAME, img);
cvWaitKey(0);
cvReleaseImage(&img);
cvDestroyWindow(WINDOW_NAME);
#endif
return 0;
}
OpenCV (vcpkg)
Библиотеку можно установить при помощи пакетных менеджеров, например, vcpkg.
Для установки можно найти интсрукцию на сайте:
vcpkg install opencv3
После установки в vcpkg/packages
будут располагаться "установленные" (собранные из исходников под ваш таргет) библиотеки с их зависимостями. В названии также отображается битность прилагаемых библиотек.
Дополнительно в логах по окончании установки будет располагаться заклинание для CMake, например:
# opencv3 is compatible with built-in CMake variables. In case of multiple OpenCV version installed, set OpenCV_ROOT *before* the find_package call:
set(OpenCV_ROOT "${VCPKG_INSTALLED_DIR}/x64-windows/share/opencv3")
find_package(OpenCV REQUIRED)
Подключение внешних библиотек в терминале
Попробуем собрать программу без указания особых ключей компиляции:
> clang -O2 .\main.c -o main.exe
При запуске программы получим:
> .\main.exe
sizeof(size_t) = 8
ERROR! Usage: ..\cmd_test\main.exe image-file
> .\main.exe "..\chat.png"
sizeof(size_t) - 8
Попробуем указать сборку в другую битность (по умолчанию x64; установим x86):
> clang -O2 .\main.c -o main.exe -m32
> .\main.exe
sizeof(size_t) = 4
ERROR! Usage: ..\cmd_test\main.exe image-file
Действительно, программа собрана как 32-битная. Т.к. в нашем распоряжении имеются собранные файлы библиотек только в варианте x64
, то в дальнейшем будем собирать программу 64-битную.
Как можно заметить, в коде нигде явно не объявлен макрос OPENCV
и соотственно код, связанный с использованием библиотеки был отброшен на этапе препроцессинга.
Объявим макрос OPENCV
на этапе сборки программы:
> clang -O2 .\main.c -o main.exe -m64 -D OPENCV
.\main.c:4:10: fatal error: 'opencv/cv.h' file not found
4 | #include <opencv/cv.h>
| ^~~~~~~~~~~~~
1 error generated.
Ошибка гласит, что не удалось найти заголовочный файл. Мы не указали include directories, из-за чего на этапе препроцессинга поиск заголовочных файлов проводился только в директориях, заданных по умолчанию (директории компилятора и системные директории).
Исправим это. Примечание: в текущем примере директория opencv
лежит относительно main.c
по ..\library\opencv
.
> clang -O2 .\main.c -o main.exe -m64 -D OPENCV -I "..\library\opencv\build\include\"
main-f71563.o : error LNK2019: unresolved external symbol cvLoadImage referenced in function mainmain-f71563.o : error LNK2019: unresolved external symbol cvNamedWindow referenced in function main
main-f71563.o : error LNK2019: unresolved external symbol cvShowImage referenced in function main
main-f71563.o : error LNK2019: unresolved external symbol cvWaitKey referenced in function main
main-f71563.o : error LNK2019: unresolved external symbol cvReleaseImage referenced in function main
main-f71563.o : error LNK2019: unresolved external symbol cvDestroyWindow referenced in function main
main.exe : fatal error LNK1120: 6 unresolved externals
clang: error: linker command failed with exit code 1120 (use -v to see invocation)
Теперь можно заметить, что компиляция прошла успешно, а вот на этапе линковки возникли проблемы unresolved external symbol
. Линковщику необходимо указать пути до файлов и сами файлы статических библиотек:
> clang -O2 .\main.c -o main.exe -m64 -D OPENCV -I "..\library\opencv\build\include\" -L "..\library\opencv\build\x64\vc15\lib\" -lopencv_world3416.lib
clang: error: no such file or directory: '.lib'
Обратите внимание, если указать имя файла с расширением, то собрать программу не получится.
> clang -O2 .\main.c -o main.exe -m64 -D OPENCV -I "..\library\opencv\build\include\" -L "..\library\opencv\build\x64\vc15\lib\" -lopencv_world3416
Ура, никаких ошибок линковки не возникло. Попробуем запустить программу:
> .\main.exe ; "0x"+$LastExitCode.ToString("X")
0xC0000135
Теперь ошибка на старте программы. В таких случаях можно запустить программу через явное создание процесса или же из проводника:
> Start-Process .\main.exe ; "0x"+$LastExitCode.ToString("X")

В обоих случаях удаётся найти сообщение об ошибке – не найден файл динамической библиотеки.
В таком случае есть 2 варианта:
Скопировать
.dll
-файл рядом сexe
-файлом.Добавить директорию, в которой лежит необходимый
.dll
-файл в переменные окружения. В таком случае не нужно каждый раз копироватьdll
кexe
-файлу.
В этом примере подложим dll
файл к exe
:

При запуске видно, что всё открылось успешно и в консоли также видно данные, выводимые в программе.

Подключение внешних библиотек в системе сборки MSBuild (Visual Studio)
Система сборки MSBuild имеет консольный и пользовательский графический интерфейс. Рассмотрим настройку через графический интерфейс в среде разработки Visual Studio.
Добавим готовый файл main.c
из начального примера в проект.

Все параметры и данные для сборки настраиваются через свойства решений и проектов в IDE Vusial Studio. Если в решении несколько проектов, то системе сборки MSBuild можно задать правила и порядок сборки проектов, что может быть полезно, если имеется зависимость между проектами в решении.
В нашем случае будет 1 проект в 1 решении и далее мы будем рассматривать только настройки проекта (ПКМ по проекту – Properties).
В первую очередь настоятельно рекомендуется установить общие для всех конфигураций и платформ настройки, установив Configurations и Platforms в All. Затем уже устанавливать параметры, различные для конфигураций и платформ. Это сильно сэкономит времени и нервов :)
В первой вкладке General
можно настроить используемый toolset
, название выходного файла, стандарт языка и пр.

Toolset – набор инструментов платформы, состоящий из компилятора C++ (cl.exe) и компоновщика (link.exe), а также стандартных библиотек C/C++.

По умолчанию будет доступен 1 toolset
той VS, которая была установлена (например 2022). Через Visual Studio Installer можно поставить toolset
более ранних VS, а также поставить clang
(LLVM-Clang на скрине). Помимо этого компилятор Intel имеет интеграцию в VS и после установки также будет доступен в выпадающем списке.
Target name – имя выходного файла. Аналог -o
у clang-а.
Стандарт языка позволяет явно задать под какой стандарт будет собираться программа. аналог -std=
у clang-а.
Во вкладке Отладка можно настроить рабочую директорию и аргументы командной строки.

Рабочая директория – директория, относительно которой будет вестить поиск файлов. По умолчанию - каталог, содержащий файл проекта. Когда программа собирается из терминала, то рабочая директория – та, в которой вы находитесь в терминале.
На скриншотах выше вы могли заметить значения вида $(ProjectDir)
и пр. Это макросы VS, которые будут раскрываться при сборке программы. Во что раскрываются макросы можно посмотреть при редактированнии полей:

Попробуем собрать и запустить нашу программу в Release x64
и Debug x86
.


Переключение между конфигурациями происходит достаточно просто через опции на верхней панели среды разработки.
Однако как в случае со сборкой в терминале мы видим, что код OpenCV не был задействован, т.к. мы не указали полезных данных для среды разработки про OpenCV.
В первую очередь посмотрим на подсказки самой среды разработки:

Видно, что фрагмент кода в main()
показывается тусклым. Так среда разработки подсказывает, что этот код не будет исполняться – либо код недостижим, либо будет отброшен на этапе препроцессинга. Второй вариант как раз характеризует наш случай.
Если укажем preprocessor defines
, то картина изменится:

Теперь IDE явно подсказывает, что код будет исполняться, но про сами макросы и функции из OpenCV ничего не известно.
Укажем include directories:

В разделе C/C++/general
помимо include directories можно изменить ключ компиляции warning levels для отлавливания большего числа предупреждений и нужно отключить SDL checks для наступления счастья.
Теперь при попытке сборки ожидаемо будут возникать ошибки линковки:
Build started at 16:33...
1>------ Build started: Project: OPENCV_VSProject, Configuration: Release x64 ------
1>main.c
1>main.obj : error LNK2001: unresolved external symbol cvWaitKey
1>main.obj : error LNK2001: unresolved external symbol cvReleaseImage
1>main.obj : error LNK2001: unresolved external symbol cvLoadImage
1>main.obj : error LNK2001: unresolved external symbol cvDestroyWindow
1>main.obj : error LNK2001: unresolved external symbol cvShowImage
1>main.obj : error LNK2001: unresolved external symbol cvNamedWindow
1>..\OPENCV_VSProject\x64\Release\OPENCV_VSProject.exe : fatal error LNK1120: 6 unresolved externals
1>Done building project "OPENCV_VSProject.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
========== Build completed at 16:33 and took 00.281 seconds ==========
Которые исправим указанием файлов статических бибилотек и директорий, где они расположены. Обратите внимание, что здесь мы явно указываем платформу x64
, т.к. собрана библиотека у нас только в этом варианте; а при задании файла мы не забудем для сборки в debug указать debug-версию библиотеки, а для release – release-файл.



Теперь сборка будет проходить успешно:
Build started at 16:38...
1>------ Build started: Project: OPENCV_VSProject, Configuration: Release x64 ------
1>Generating code
1>1 of 5 functions (20.0%) were compiled, the rest were copied from previous compilation.
1> 0 functions were new in current compilation
1> 0 functions had inline decision re-evaluated but remain unchanged
1>Finished generating code
1>OPENCV_VSProject.vcxproj -> ..\OPENCV_VSProject\x64\Release\OPENCV_VSProject.exe
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
========== Build completed at 16:38 and took 00.311 seconds ==========
А для корректного старта необходимо в корень проекта или к exe-файлам подложить dll
файлы (подсказка: в корень проекта проще).
Подключение внешних библиотек в системе сборки CMake (Visual Studio)
Visual Studio 2022 поддерживает CMake и позволяет писать CMake-проекты.
Также помимо запуска под Windows есть возможность настроить запуск под WSL (есл установлен) или же настроить запуск с удаленного хоста.

По умолчанию создано несколько стандартных конфигураций сборки программы:

По умолчанию все проекты CMake в VS создаются как C++-проекты и при необходимости необходимо самостоятельно изменить в свойствах проекта язык с C++ (CXX) на C.
Свойства проекта представлены в виде CMakeLists.txt
файла:
# CMakeList.txt : CMake project for OPENCV_CmakeProject, include source and define
# project specific logic here.
#
cmake_minimum_required (VERSION 3.8)
# Enable Hot Reload for MSVC compilers if supported.
if (POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()
add_executable(OPENCV_CmakeProject OPENCV_CmakeProject.c)
Для дальнейшей работы рекомендуется удалить из проекта автосгенерированные исходники и прописать CMakeLists.txt самостоятельно. В случаи использования OpenCV также рекомендуется убрать весь if (POLICY CMP0141) ... endif()
.
После объявления минимально подходящей версии CMake следует явно указать переменную проекта (версия проекта опциональна):
project(OPENCV_CmakeProject VERSION 0.1)
Укажем явно, что мы хотим собирать проект под C17 и, например, что поддержка этого стандарта нам необходима для сборки (более ранние версии не подойдут):
set(CMAKE_C_STANDART 17)
set(CMAKE_C_STANDART_REQUIRED True)
Дополним его необходимыми сведениями про OpenCV.
Заведём для примера переменную, содержащую все файлы проекта, которые будут подаваться на компиляцию
set(SOURCES "OPENCV_CmakeProject.c")
Укажем макрос
add_definitions(-DOPENCV)
Дополним ключи компиляции по умолчанию ключом про уровень предупреждений (по умолчанию
/W3
, терминология компилятора MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4")
Укажем include directories (здесь и далее полагаем, что OpenCV лежит рядом с нашим проектом)
include_directories("../library/opencv/build/include")
Укажем при сборке в
x64
library directories (32-битной версии библиотеки у нас нет, поэтому не будем рассматривать данную ветку)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
link_directories("../library/opencv/build/x64/vc15/lib")
endif()
Модифицируем аргументы конструкции
add_executable
add_executable(OPENCV_CmakeProject ${SOURCES})
Дополним сведения о файлах статических библиотек
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
if (CMAKE_BUILD_TYPE EQUAL "Debug")
target_link_libraries(OPENCV_CmakeProject "opencv_world3415d.lib")
els()
target_link_libraries(OPENCV_CmakeProject "opencv_world3415.lib")
endif()
endif()
CMAKE_BUILD_TYPE
нам известен из файла CMakePresets.json
, в котором указаны компилтор и подсистема сборки (по умолчанию Ninja), а также переменные каждой конфигурации, которые можно смело использовать в CMakeLists.txt
.
Примечение: при изменении CMakeLists.txt
они автоматически сохраняются и сразу происходит регенерация кеша сборки:

Там содержаться логи регенерации с ошибками (если они есть). В противном случае будет CMake generation finished.
. Из полезного: Command line:
, в которой можно посмотреть параметры, с которым вас по итогу собирают, и Working directory:
– рабочая директория проекта (та, где лежит сформированный выходной файл).
Чтобы задать аргументы командной строки необходимо:
Переключить отображение в
Solution Explorer
наProject View

Перейти в
Add Debug Configurations
(ПКМ по проекту)

Прописать параметры
currentDir
(WorkingDirectory) иargs
(command-line arguments) в открывшемсяlaunch.json
:
{
"version": "0.2.1",
"defaults": {},
"configurations": [
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "OPENCV_CmakeProject.exe",
"name": "OPENCV_CmakeProject.exe",
+ "currentDir": ".",
+ "args": [
"../chat.png"
]
}
]
}
После проделанных манипуляций при указании на верхней панели конфигураций x64 Release
и x64 Debug
будет собираться программа, запускаться с переданным аргументом и показываться картинка на экране, а приx86 Release
и x86 Debug
– ошибка линковки.
Подключение внешних библиотек в системе сборки CMake (CLion)
Сам файл CMakeLists.txt
почти ничем не будет отличаться, за исключением:
cmake_minimum_required (VERSION 3.8)
вероятно будет более новой версии# Enable Hot Reload for MSVC compilers if supported.
и связанный с этим комментариемif
будет отсутствоватьфлаги компиляции следует указывать те, которые нужны для используемого
toolchain
– еслиtoolchain
не VS, то/W4
можно заменить на-Wall -Wextra -Wpedantic
Подробнее про toolchain
в CLion: clang-clion
Last updated