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

发布于 2025-01-10 23:16:51 字数 755 浏览 0 评论 0原文

我见过很多这样的代码:

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) 的命令?

参考:

I have seen a lot of code like this:

include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)

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

However, that seems to be the wrong way about doing it, as it only uses the include directories and libraries, but ignores defines, library paths and other flags that might be returned by 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)?

ref:

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

咿呀咿呀哟 2025-01-17 23:16:51

首先,调用:

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_DIRSSDL2_LDFLAGS_OTHER 仍然被忽略,不知道这会成为一个问题的频率。

更多文档请参见:CMake 文档 — FindPkgConfig

First off, 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().

Secondly, manually calling pkg-config should be avoided when possible. CMake comes with a rich set of package definitions, found on Linux under /usr/share/cmake-3.0/Modules/Find*cmake. These provide more options and choice for the user than a raw call to pkg_search_module().

As for the mentioned hypothetical target_use() command, CMake already has that built-in in a way with PUBLIC|PRIVATE|INTERFACE. A call like target_include_directories(mytarget PUBLIC ...) will cause the include directories to be automatically used in every target that uses mytarget, e.g., target_link_libraries(myapp mytarget).

However, this mechanism seems to be only for libraries created within the CMakeLists.txt file and does not work for libraries acquired with pkg_search_module(). The call add_library(bar SHARED IMPORTED) might be used for that, but I haven't yet looked into that.

As for the main question, this here works in most cases:

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})

The SDL2_CFLAGS_OTHER contains defines and other flags necessary for a successful compile. The flags SDL2_LIBRARY_DIRS and SDL2_LDFLAGS_OTHER are however still ignored, no idea how often that would become a problem.

More documentation here: CMake Documentation — FindPkgConfig.

星星的軌跡 2025-01-17 23:16:51

现代 CMake 方法 - 使用 pkgconfig 结果导入目标

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

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

另一个问题可能是 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 GLOBAL any-package)
pkg_check_modules(YOUR_PKG REQUIRED IMPORTED_TARGET GLOBAL 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 属性,例如编译器标志、编译器定义、包含路径等,因此请谨慎使用 PUBLIC 关键字。
  • 使用 IMPORTED_TARGET 需要 CMake 3.6 或更高版本。
  • GLOBAL 扩展了 MY_PKGYOUR_PKG 目标的定义范围。

The modern-CMake approach - an imported target using pkgconfig results

If you're using cmake and pkg-config in a pretty normal way, this solution works.

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.

Another issue could be that the pkg-config files are not installed in the normal place, and the pkg-config paths for the project need to be added using the PKG_CONFIG_PATH environment variable while cmake is running (see other Stack Overflow questions regarding this). This solution also works well when you use the correct pkg-config path.

Using IMPORTED_TARGET is key to solving the issues above. This solution is an improvement on this earlier answer and boils down to this final version of a working 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 GLOBAL any-package)
pkg_check_modules(YOUR_PKG REQUIRED IMPORTED_TARGET GLOBAL ya-package)

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

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

Notes:

  • 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.
  • Using IMPORTED_TARGET requires CMake version 3.6 or later.
  • GLOBAL expands the scope of the definition of the MY_PKG and YOUR_PKG targets.
酒绊 2025-01-17 23:16:51

大多数可用的答案都无法配置 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>)

用你的目标代替 并用任何库代替

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

Most of the available answers fail to configure the headers for the pkg-config library. After meditating on the Documentation for FindPkgConfig I came up with a solution that provides those also:

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>)

(Substitute your target in place of <my-target> and whatever library in place of <some-lib>, accordingly.)

The IMPORTED_TARGET option seems to be key and makes everything then available under the PkgConfig:: namespace. This was all that was required and also all that should be required.

﹎☆浅夏丿初晴 2025-01-17 23:16:51

很少有人只需要与 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 的自我回答。

It's rare that one would only need to link with SDL2. The currently popular answer uses pkg_search_module() which checks for given modules and uses the first working one.

It is more likely that you want to link with SDL2 and SDL2_Mixer and SDL2_TTF, etc... pkg_check_modules() checks for all the given 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})

Disclaimer: I would have simply commented on Grumbel's self answer if I had enough street creds with stackoverflow.

树深时见影 2025-01-17 23:16:51
  1. 没有target_use这样的命令。但我知道有几个项目已经编写了这样的命令供其内部使用。但是每个项目都希望传递额外的标志或定义,因此在一般 CMake 中拥有它是没有意义的。不拥有它的另一个原因是像 Eigen 这样的 C++ 模板库,没有库,但你只有一堆包含文件。

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

  1. There is no such command as target_use. But I know several projects that have written such a command for their internal use. But every project want to pass additional flags or defines, thus it does not make sense to have it in general CMake. Another reason not to have it are C++ templated libraries like Eigen, there is no library but you only have a bunch of include files.

  2. The described way is often correct. It might differ for some libraries, then you'll have to add _LDFLAGS or _CFLAGS. One more reason for not having target_use. If it does not work for you, ask a new question specific about SDL2 or whatever library you want use.

暮年慕年 2025-01-17 23:16:51

如果您还想从库中添加定义,可以使用 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})

If you are looking to add definitions from the library as well, the add_definitions instruction is there for that. Documentation can be found here, along with more ways to add compiler flags.

The following code snippet uses this instruction to add GTKGL to the project:

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})
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文