Який правильний спосіб використовувати `pkg-config` з` cmake`?


86

Оглядаючи мережу, я побачив багато такого коду:

include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)

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

Однак, здається, це неправильний спосіб, оскільки він використовує лише каталоги та бібліотеки include, але ігнорує визначення, шляхи бібліотеки та інші прапори, які можуть бути повернені pkg-config.

Яким був би правильний спосіб зробити це та переконатись, що всі прапори компіляції та посилання, що повертаються pkg-config, використовуються скомпільованим app? І чи існує одна команда для цього, тобто щось на зразок target_use(app SDL2)?

посилання:

Відповіді:


32

Якщо ви використовуєте 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, це рішення також вирішує цю проблему.

Рішення зводиться до цієї остаточної версії робочого 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 властивості вказаних цілей, такі як: прапори компілятора, визначення компілятора, включення шляхів тощо


5
IMPORTED_TARGETвимагає CMake 3.6 або новішої версії.
Cris Luengo

якщо ви проголосували за це, будь ласка, переконайтесь і прокоментуйте, чому ви проголосували, щоб ми могли покращити відповідь.
activedecay

Я думаю, це не вдалося для мене через gitlab.kitware.com/cmake/cmake/-/issues/19387 .
Флоріан Звох

63

По-перше, дзвінок:

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


4
Я згоден , що PKG-конфігурації слід уникати ЯКЩО знахідку * .cmake існує, але до сих пір не відноситься до останньої версії CMake в 2016 році
Cubic

3
Це не працює, якщо бібліотеки відсутні в каталогах за замовчуванням. link_directories () може бути обхідним шляхом, але він є глобальним.
Генрі Ху

Цей підхід не працює для vcpkg . Чи можу я знайти SDL2_image без жорстких шляхів кодування !?
user2023370

@HenryHu не могли б ви показати, як це було б зроблено, якби така ситуація була в конфігурації у цій відповіді?
Тім Візе

2
Потребувати інструмента збірки, такого як CMake, щоб поєднати евристику для вишукування кожної бібліотеки у світі, немає сенсу, це не її роль. Pkg-config розроблений таким чином, що відповідальність за надання доступу користувачам до відповідальності несе автор бібліотеки або супровідник pkg / distro. І якщо дотримуватися цієї схеми, правильний спосіб використання lib - це завжди за допомогою виклику pkg-config.
Йохан Буле,

10

Рідко коли комусь потрібно буде лише зв’язати 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.


2
Глобування вихідних файлів є поганою практикою і не рекомендується.
liberforce

1
Для мене це target_link_libraries(my_app ${SDL2_LINK_LIBRARIES})працювало краще.
stefan

2
@liberforce глобування вихідних файлів є гарною практикою, це помилка CMake, якщо вона глючить.
Йохан Буле,

2
@ ЙоханБуле: Ні, це не так. Можливо, розробник може додати купу файлів локально, а матеріали працюватимуть на своєму комп’ютері, а не фіксуватимуть усі необхідні файли. Потім вони просувають свої зміни, і це порушує для інших людей. Звичайно, це можна зловити за допомогою безперервної інтеграції, але це лише найбільш очевидна проблема. Система збірки Meson вирішила не реалізовувати глобалізацію файлів , і розробники CMake явно не рекомендують глобалізувати . Явне краще, ніж неявне.
liberforce

1
@liberforce Я вже бачив цей аргумент у багато разів більше, ніж реальна проблема, яку він теоретизує. Мезон проти, build2 для. Ні в кого не буде, як tab проти пробілу.
Йохан Буле

6

Більшість доступних відповідей не вдається налаштувати заголовки для 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::простором імен. Це було все , що було потрібно , а також всі , що повинно бути обов'язково.


РАДА: друк CMake вар після запуску , pkg_check_modulesщоб подивитися список доступних вари stackoverflow.com/a/9328525/1211174
дуб

0
  1. Не існує такої команди як target_use. Але я знаю кілька проектів, які написали таку команду для їх внутрішнього використання. Але кожен проект хоче передавати додаткові прапори або визначення, тому немає сенсу мати його взагалі CMake. Ще однією причиною, щоб цього не мати, є бібліотеки з шаблоном C ++, такі як Eigen, бібліотеки немає, але у вас є лише купа файлів включення.

  2. Описаний спосіб часто є правильним. Це може відрізнятися для деяких бібліотек, тоді вам доведеться додати _LDFLAGSабо _CFLAGS. Ще одна причина відсутності target_use. Якщо це не працює для вас, задайте нове запитання про SDL2 або будь-яку бібліотеку, яку ви хочете використовувати.


-2

Якщо ви хочете також додати визначення з бібліотеки, для цього 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})

3
Не використовуйте include_directoriesі т. Д. Це заразить глобальний масштаб! Використовуйте target_include_directoriesтощо
Давид Дрозд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.