Как правильно использовать pkg-config из cmake?

Оглядываясь в сети, я видел много такого кода:

include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)

target_include_directories(app SYSTEM PUBLIC ${SDL2_INCLUDE_DIRS}
target_link_libraries(app ${SDL2_LIBRARIES})

Однако это кажется неправильным, поскольку он использует только подключаемые каталоги и библиотеки, но игнорирует определения, пути к библиотекам и другие флаги, которые могут быть возвращены pkg-config.

Каким будет правильный способ сделать это и убедиться, что все флаги компиляции и компоновки, возвращаемые pkg-config, используются скомпилированным app? И есть ли одна команда для этого, например, target_use(app SDL2)?

ссылка:


person Grumbel    schedule 22.03.2015    source источник


Ответы (6)


Если вы обычно используете cmake и pkg-config, это решение работает.

Если, однако, у вас есть библиотека, которая существует в каком-либо каталоге разработки (например, / home / me / hack / lib), то использование других методов, представленных здесь, не может настроить пути компоновщика. Библиотеки, которые не находятся в типичных местах установки, могут привести к ошибкам компоновщика, например /usr/bin/ld: cannot find -lmy-hacking-library-1.0. Это решение исправляет ошибку компоновщика в этом случае.

Другая проблема может заключаться в том, что файлы pkg-config не установлены в обычном месте, и пути pkg-config для проекта должны быть добавлены с использованием переменной среды PKG_CONFIG_PATH во время работы cmake (см. Другие вопросы о переполнении стека по этому поводу). Если у вас настроен правильный путь pkg-config, это решение также устраняет эту проблему.

Использование IMPORTED_TARGETs решает эту проблему. Это решение является улучшением этого более раннего ответа и сводится к этой окончательной версии рабочего файла CMakeLists.txt:

cmake_minimum_required(VERSION 3.14)
project(ya-project C)

# the `pkg_check_modules` function is created with this call
find_package(PkgConfig REQUIRED) 

# these calls create special `PkgConfig::<MODULE>` variables
pkg_check_modules(MY_PKG REQUIRED IMPORTED_TARGET any-package)
pkg_check_modules(YOUR_PKG REQUIRED IMPORTED_TARGET ya-package)

add_executable(program-name file.c ya.c)

target_link_libraries(program-name PUBLIC
        PkgConfig::MY_PKG
        PkgConfig::YOUR_PKG)

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

person activedecay    schedule 26.07.2019
comment
IMPORTED_TARGET требуется CMake 3.6 или новее. - person Cris Luengo; 24.01.2020
comment
Если вы проголосовали против, обязательно прокомментируйте, почему вы проголосовали против, чтобы мы могли улучшить ответ. - person activedecay; 29.10.2020
comment
Я думаю, это не удалось из-за gitlab.kitware.com/cmake/cmake/ - / issues / 19387. - person Florian Zwoch; 18.12.2020

Во-первых, звонок:

include(FindPkgConfig)

следует заменить на:

find_package(PkgConfig)

Вызов find_package() более гибкий и позволяет использовать такие параметры, как REQUIRED, которые автоматически выполняют действия, которые можно было бы делать вручную с помощью include().

Во-вторых, по возможности следует избегать ручного вызова pkg-config. CMake поставляется с богатым набором определений пакетов, которые можно найти в Linux в разделе /usr/share/cmake-3.0/Modules/Find*cmake. Они предоставляют пользователю больше возможностей и выбора, чем простой вызов pkg_search_module().

Что касается упомянутой гипотетической команды target_use(), в CMake уже есть встроенная функция PUBLIC | PRIVATE | INTERFACE. Вызов типа target_include_directories(mytarget PUBLIC ...) вызовет автоматическое использование подключаемых каталогов в каждой цели, которая использует mytarget, например target_link_libraries(myapp mytarget). Однако этот механизм, похоже, предназначен только для библиотек, созданных в файле CMakeLists.txt, и не работает для библиотек, полученных с помощью pkg_search_module(). Для этого можно использовать вызов add_library(bar SHARED IMPORTED), но я еще не рассматривал это.

Что касается главного вопроса, то здесь это работает в большинстве случаев:

find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
...
target_link_libraries(testapp ${SDL2_LIBRARIES})
target_include_directories(testapp PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(testapp PUBLIC ${SDL2_CFLAGS_OTHER})

SDL2_CFLAGS_OTHER содержит определения и другие флаги, необходимые для успешной компиляции. Флаги SDL2_LIBRARY_DIRS и SDL2_LDFLAGS_OTHER по-прежнему игнорируются, не знаю, как часто это может стать проблемой.

Дополнительная документация здесь http://www.cmake.org/cmake/help/v3.0/module/FindPkgConfig.html

person Grumbel    schedule 28.03.2015
comment
Я согласен с тем, что следует избегать pkg-config IF существует Find * .cmake, но это все еще не относится к последней версии cmake в 2016 году. - person Cubic; 21.08.2016
comment
Это не работает, если библиотеки не находятся в каталогах по умолчанию. link_directories () может быть обходным путем, но он глобальный. - person Henry Hu; 22.05.2018
comment
Этот подход не работает для vcpkg. Могу ли я найти SDL2_image без путей жесткого кодирования !? - person user2023370; 24.08.2018
comment
@HenryHu, не могли бы вы показать, как это было бы сделано, если бы такая ситуация была в конфигурации в этом ответе? - person Tim Visée; 30.08.2018
comment
Требовать такого инструмента сборки, как CMake, для связывания эвристики для сниффинга каждой библиотеки в мире, не имеет смысла, это не его роль. Pkg-config разработан таким образом, что ответственность за его доступность для пользователей лежит на создателе библиотеки или сопровождающем pkg / distro. И если следовать этой схеме, правильный способ использования библиотеки всегда - это вызов pkg-config. - person Johan Boulé; 16.03.2019
comment
поскольку FindPkgConfig - это встроенный модуль cmake (например, CTest), использование include(FindPkgConfig) - это путь, IMHO (поскольку сначала включите поиск в каталог встроенного модуля cmake). см. страницу документации CTest: cmake.org/cmake/help/latest/module/CTest .html, которые используют include() - person Mizux; 22.03.2019
comment
Кстати, include() также есть OPTIONAL параметры .... src: cmake.org/cmake /help/latest/command/include.html - person Mizux; 22.03.2019
comment
Что касается моего предыдущего комментария, я показываю, что кое-что исправляю: все эти файлы приносит не CMake, а авторы библиотеки. Однако pkg-config по-прежнему является концепцией, поскольку автору библиотеки не нужно знать или требовать, какую систему сборки будут использовать их пользователи. - person Johan Boulé; 07.04.2019
comment
Хотя я бы действительно рекомендовал использовать подход IMPORTED_TARGET, используемый в stackoverflow.com/a/57224542/211520, если вам действительно нужно to target_link_libraries(), тогда используйте <XXX>_LINK_LIBRARIES вместо просто <XXX>_LIBRARIES: первый имеет полные, абсолютные пути и, таким образом, работает и для нестандартных каталогов; например при кросс-компиляции. - person iliis; 11.11.2019

Редко бывает нужно связать только с SDL2. В популярном в настоящее время ответе используется pkg_search_module(), который проверяет заданные модули и использует первый рабочий.

Более вероятно, что вы захотите связать с SDL2 и SDL2_Mixer, SDL2_TTF и т. Д. pkg_check_modules() проверяет наличие всех данных модулей.

# sdl2 linking variables
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2 SDL2_ttf SDL2_mixer SDL2_image)

# your app
file(GLOB SRC "my_app/*.c")
add_executable(my_app ${SRC})
target_link_libraries(my_app ${SDL2_LIBRARIES})
target_include_directories(my_app PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(my_app PUBLIC ${SDL2_CFLAGS_OTHER})

Отказ от ответственности: я бы просто прокомментировал собственный ответ Грумбеля, если бы у меня было достаточно уличных кредитов с stackoverflow.

person fluxrider    schedule 28.12.2017
comment
Копирование исходных файлов - плохая практика и не рекомендуется. - person liberforce; 23.02.2018
comment
Для меня target_link_libraries(my_app ${SDL2_LINK_LIBRARIES}) работал лучше. - person stefan; 19.02.2019
comment
Подбор исходных файлов @liberforce - это хорошая практика, это вина CMake, если он содержит ошибки. - person Johan Boulé; 16.03.2019
comment
@ JohanBoulé: Нет, это не так. Вы можете попросить разработчика добавить кучу файлов локально и заставить их работать на своем компьютере, а не фиксировать все необходимые файлы. Затем они продвигают свои изменения, и это ломается для других. Конечно, это можно уловить с помощью некоторой непрерывной интеграции, но это только самая очевидная проблема. Система сборки решила не реализовывать подстановка файлов и разработчики CMake явно не рекомендуют использовать подстановку файлов. Явное лучше, чем неявное. - person liberforce; 18.03.2019
comment
@liberforce Я видел этот аргумент во много раз больше, чем реальную проблему, которую он теоретизирует. Мезон против, build2 за. Ни у кого его не будет, как табуляция против пробела. - person Johan Boulé; 18.03.2019
comment
@liberforce Этот мой друг - это то, что мы называем проблемой XY. Решение - это ловушка CI, которая строится на каждом push и отклоняет запросы на вытягивание, которые не создаются. - person jfh; 16.07.2020
comment
@jfh, пожалуйста, прочтите предоставленные мной ссылки. Разработчики Meson прямо заявляют: This can not be made both reliable and fast. By reliable we mean that if the user adds a new source file to the subdirectory, Meson should detect that and make it part of the build automatically. One of the main requirements of Meson is that it must be fast. This means that a no-op build in a tree of 10 000 source files must take no more than a fraction of a second. This is only possible because Meson knows the exact list of files to check. - person liberforce; 17.07.2020

В большинстве доступных ответов не удается настроить заголовки для библиотеки pkg-config. После размышлений над документацией для FindPkgConfig я пришел к решению, которое предоставляет также:

include(FindPkgConfig)
if(NOT PKG_CONFIG_FOUND)
  message(FATAL_ERROR "pkg-config not found!" )
endif()
 
pkg_check_modules(<some-lib> REQUIRED IMPORTED_TARGET <some-lib>)
 
target_link_libraries(<my-target> PkgConfig::<some-lib>)

(Замените свою цель вместо <my-target> и любую библиотеку вместо <some-lib> соответственно.)

Параметр IMPORTED_TARGET кажется ключевым и делает все доступным в пространстве имен PkgConfig::. Это было все, что требовалось, а также все, что должно требоваться.

person Eero Aaltonen    schedule 04.06.2019
comment
СОВЕТ: распечатайте cmake var после запуска pkg_check_modules, чтобы увидеть доступные переменные stackoverflow.com/a/9328525/1211174 - person oak; 02.12.2019

  1. Нет такой команды, как target_use. Но я знаю несколько проектов, которые написали такую ​​команду для внутреннего использования. Но каждый проект хочет передать дополнительные флаги или определения, поэтому нет смысла иметь их в CMake. Еще одна причина не использовать его - это шаблонные библиотеки C ++, такие как Eigen, библиотеки нет, но у вас есть только набор включаемых файлов.

  2. Описанный способ часто бывает правильным. Для некоторых библиотек он может отличаться, тогда вам придется добавить _LDFLAGS или _CFLAGS. Еще одна причина отсутствия target_use. Если это не сработает, задайте новый вопрос, касающийся SDL2 или любой другой библиотеки, которую вы хотите использовать.

person usr1234567    schedule 22.03.2015

Если вы хотите добавить определения из библиотеки, для этого есть инструкция add_definitions. Документацию можно найти здесь, а также другие способы добавления флагов компилятора. .

В следующем фрагменте кода эта инструкция используется для добавления GTKGL в проект:

pkg_check_modules(GTKGL REQUIRED gtkglext-1.0)
include_directories(${GTKGL_INCLUDE_DIRS})
link_directories(${GTKGL_LIBRARY_DIRS})
add_definitions(${GTKGL_CFLAGS_OTHER})
set(LIBS ${LIBS} ${GTKGL_LIBRARIES})

target_link_libraries([insert name of program] ${LIBS})
person jeanluc    schedule 21.06.2017
comment
Не используйте include_directories и т. Д., Это заразит глобальную область видимости! Используйте target_include_directories и т. Д. - person Dawid Drozd; 21.09.2017