从‘ cmake’中使用‘ pkg-config’的正确方法是什么?

我在网上看到过很多这样的代码:

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

What would be the correct way to do this and ensure that all compile and link flags returned by pkg-config are used by the compiled app? And is there a single command to accomplish this, i.e. something like target_use(app SDL2)?

参考文献:

94231 次浏览
  1. 没有像 target_use这样的命令。但是我知道有几个项目已经编写了这样的命令供内部使用。但是每个项目都希望传递额外的标志或定义,因此在一般 CMake 中使用它是没有意义的。另一个不使用它的原因是像 Eigen 这样的 C + + 模板库,没有库,但是你只有一堆包含文件。

  2. 所描述的方法往往是正确的。对于某些库,它可能有所不同,那么您必须添加 _LDFLAGS_CFLAGS。又一个没有 target_use的原因。如果它不适合你,问一个新的问题,特定于 SDL2或任何你想使用的库。

First of, the call:

include(FindPkgConfig)

should be replaced with:

find_package(PkgConfig)

The find_package() call is more flexible and allows options such as REQUIRED, that do things automatically that one would have to do manually with include().

其次,尽可能避免手动调用 pkg-config。CMake 提供了一组丰富的包定义,可以在 /usr/share/cmake-3.0/Modules/Find*cmake下的 Linux 中找到。与对 pkg_search_module()的原始调用相比,它们为用户提供了更多的选项和选择。

至于上面提到的假设的 target_use()命令,CMake 已经内置了 PUBLIC | PRIVATE | INTERFACE。像 target_include_directories(mytarget PUBLIC ...)这样的调用将导致 include 目录自动用于每个使用 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_DIRSSDL2_LDFLAGS_OTHER仍然被忽略,不知道多久会成为一个问题。

这里有更多的文档 http://www.cmake.org/cmake/help/latest/module/FindPkgConfig.html

如果您也希望从库中添加定义,那么 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})

只需要与 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})

免责声明: 如果我有足够的堆栈溢出的街头信用证,我会简单地评论 Grumbel 的自我回答。

大多数可用的答案都无法配置 pkg-config库的标题。在沉思了 Documentation for 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>)

(相应地,将您的目标替换为 ABC0,并将任何库替换为 <some-lib>)

IMPORTED_TARGET选项似乎是关键,它使得 PkgConfig::名称空间下的所有内容都可用。这是所有需要的,也是所有 应该所需要的。

如果您以一种非常正常的方式使用 cmake 和 pkg-config,那么这个解决方案是可行的。

If, however, you have a library that exists in some development directory (such as /home/me/hack/lib), then using other methods seen here fail to configure the linker paths. Libraries that are not found under the typical install locations would result in linker errors, like /usr/bin/ld: cannot find -lmy-hacking-library-1.0. This solution fixes the linker error for that case.

另一个问题可能是 pkg-config 文件没有安装在正常位置,而且在 cmake 运行时,需要使用 PKG_CONFIG_PATH环境变量添加项目的 pkg-config 路径(请参阅其他有关堆栈溢出的问题)。当您使用正确的 pkg-config 路径时,此解决方案也能很好地工作。

使用 IMPORTED_TARGET是解决上述问题的关键。这个解决方案是对 这个早期的答案的一个改进,归结为工作 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)

Note that target_link_libraries does more than change the linker commands. It also propagates other PUBLIC properties of specified targets like compiler flags, compiler defines, include paths, etc., so, use the PUBLIC keyword with caution.