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


81

在网上环顾四周,我看到了很多这样的代码:

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)

参考:

Answers:


28

如果您以非常普通的方式使用cmake和pkg-config,则此解决方案有效。

但是,如果您在某个开发目录(例如/ home / me / hack / lib)中存在一个库,则使用此处显示的其他方法无法配置链接器路径。在典型安装位置下找不到的库将导致链接器错误,例如/usr/bin/ld: cannot find -lmy-hacking-library-1.0。此解决方案解决了这种情况下的链接器错误。

另一个问题可能是pkg-config文件未正常安装,并且在cmake运行时需要使用PKG_CONFIG_PATH环境变量添加项目的pkg-config路径(请参阅与此有关的其他Stack Overflow问题)。假设您设置了正确的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

62

首先,调用:

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 ...)将使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/v3.0/module/FindPkgConfig.html


4
我同意,如果存在Find * .cmake,则应避免使用pkg-config ,但对于2016
Cubic

3
如果库不在默认目录中,则此方法不起作用。link_directories()可以解决,但它是全局的。
胡锦涛

这种方法不适用于vcpkg。是否可以在没有硬编码路径的情况下找到SDL2_image!
user2023370

@HenryHu您可以显示如果此答案中的配置情况如何执行此操作吗?
蒂姆·维西

2
需要像CMake这样的构建工具来将启发式方法捆绑在一起以使世界上的每个图书馆都变得毫无意义,这不是它的作用。Pkg-config的设计目的是由lib作者或pkg / distro维护者负责将其提供给用户。如果遵循此方案,则使用lib的正确方法始终是通过调用pkg-config。
JohanBoulé19年

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,我会简单评论Grumbel的自我回答。


2
混淆源文件是一种不好的做法,因此不建议使用。
liberforce

1
对我来说,target_link_libraries(my_app ${SDL2_LINK_LIBRARIES})效果更好。
stefan

2
@liberforce遍历源文件是一个习惯,如果有错误,这是CMake的错。
JohanBoulé19年

1
@JohanBoulé:不,不是。您可能要让开发人员在本地添加一堆文件,并在他们的计算机上正常工作,而不提交所有需要的文件。然后,他们推动自己的改变,而这种改变对其他人来说是无效的。当然,可以通过一些持续的集成来解决这个问题,但这只是最明显的问题。该介子构建系统选择不实现文件globingCMake的开发者明确劝阻通配符。显式胜于隐式。
liberforce

1
@liberforce我已经看到该参数比它提出的实际问题多了很多倍。介子反对,build2反对。没有人会拥有它,例如tab vs space。
JohanBoulé19年

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 var,pkg_check_modules以查看可用的var stackoverflow.com/a/9328525/1211174
橡木

0
  1. 没有这样的命令target_use。但是我知道有几个项目已经为内部使用编写了这样的命令。但是,每个项目都希望传递其他标志或定义,因此在一般的CMake中使用它是没有意义的。没有它的另一个原因是像Eigen这样的C ++模板库,没有库,但是只有一堆include文件。

  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_directoriesetc会影响全球范围!使用target_include_directories
大卫·德罗兹
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.