你如何告诉 CMake 静态链接到使用 find_package 找到的包中的库?

Posted

技术标签:

【中文标题】你如何告诉 CMake 静态链接到使用 find_package 找到的包中的库?【英文标题】:How do you tell CMake to statically link to a library in a package found with find_package? 【发布时间】:2021-01-26 16:33:09 【问题描述】:

我正在从源代码构建一个库和一个依赖于该库的可执行文件。该库依赖于第三方库libjpeg,我想通过静态链接进行处理。 libjpeg 安装在我的系统上。我可以在/usr/lib/x86_64-linux-gnu/libjpeg.a 看到.a 文件(我在Ubuntu 上构建。)

在处理我的库的CMakeLists.txt 文件时,CMake 成功找到了libjpeg,但它的输出表明已找到 libjpeg 的动态版本,即 CMake 的输出是

-- Found JPEG: /usr/lib/x86_64-linux-gnu/libjpeg.so (found version "80")

然后在CMakeLists.txt 中,我通过链接将我的库链接到libjpeg

target_link_libraries( my_library
    PRIVATE JPEG
)

构建成功。但是,当我从 my_executable 链接到 my_library 时,我收到以下错误:

[100%] Linking CXX executable my_executable
/usr/bin/ld: cannot find -lJPEG
collect2: error: ld returned 1 exit status

我认为问题在于,由于my_library 动态链接到libjpeg,我需要从可执行文件而不是库中将target_link_libraries 链接到libjpeg。无论如何,我想做的就是完全隐藏my_librarylibjpeg的依赖。

有什么方法可以告诉find_package 更喜欢静态库吗?

【问题讨论】:

target_link_libraries(my_library PRIVATE JPEG::JPEG) — 这不能回答问题,但这将使动态链接工作。所以至少你不会从破碎的状态开始解决问题。 【参考方案1】:

首先,您应该修复您的脚本:

target_link_libraries(my_library PRIVATE JPEG::JPEG)

您必须将导入的目标名称提供给target_link_libraries

话虽如此,不,不是直接的。 find_package 本身什么都不做,它只是寻找正确的脚本并调用它。因此,实际行为是特定于库的。对于 libjpeg,它可能会找到 this script 的副本并运行它。

特别是对于 libjpeg,FindJPEG.cmake 无法告诉它更喜欢静态版本还是动态版本。

但是,您可以在调用 find_package 之前定义 JPEG_LIBRARY_RELEASE,以强制它查找特定版本。例如,如果你这样做:

cmake -DJPEG_LIBRARY_RELEASE=/usr/lib/x86_64-linux-gnu/libjpeg.a .....

然后你应该得到一个静态构建(替换为JPEG_LIBRARY_DEBUG 以定义调试版本)。您几乎可以对任何要强制检测其他内容的检测到的内容执行相同操作,因为大多数脚本不会覆盖在命令行上定义的值。

如果这适合您的工作流程,那就完美了。否则,您将无法单独使用 CMake 获得干净的解决方案,您应该在其之上添加一个依赖管理器(vcpkg、conan.io)。

【讨论】:

我怎么知道导入的目标名称是什么? find_package 找到的包如何获得名称? find_package 找到并运行的脚本将创建导入的目标(如果足够现代且设计良好)。所以它也是特定于包的。对于 libjpeg,它在那里记录:cmake.org/cmake/help/latest/module/… 当然,如果您不熟悉 cmake,那么有用的位不是很明显导入目标部分。【参考方案2】:

你可以注明名字:

find_library(JPEGLIB REQUIRED NAMES libjpeg.a)
add_executable(myapp myapp.c)
target_link_libraries(myapp PRIVATE $JPEGLIB)

赋予 NAMES 选项的每个库名称首先被视为库文件名,然后与特定于平台的前缀(例如 lib)和后缀(例如 .so)一起考虑。因此,可以直接指定库文件名,例如 libfoo.a。这可用于在类 UNIX 系统上定位静态库。

【讨论】:

请注意,如果您取消 find_package,您还必须找到标头和target_include_directories 它们。这仅适用于库实际命名为libjpeg.a 的系统,因此如果您想支持其他系统/发行版,您需要添加所有可能的名称。您还应该以某种方式将 IMPORTED_LINK_INTERFACE_LANGUAGES_<CONFIG> 设置为 C,以确保正确完成 C++ 应用程序的链接。或者简而言之,你必须重新实现 FindJPEG.cmake。

以上是关于你如何告诉 CMake 静态链接到使用 find_package 找到的包中的库?的主要内容,如果未能解决你的问题,请参考以下文章

CMAKE 如何告诉 makefile 链接库?

如何在cmake中链接winsock?

在CMake中静态链接OpenSSL加密库

如何使用cmake生成基于静态库的动态链接库

CMake--模块的使用和自定义模块

如何“告诉”CMake 3.9+ 我想使用 NVIDIA 的 OpenCL 库?