【问题标题】:Linking SDL2/SD2_image statically with CMake将 SDL2/SD2_image 与 CMake 静态链接
【发布时间】:2020-04-17 02:43:02
【问题描述】:

我正在尝试将 SDL2 作为库添加到我的项目中。我想静态链接它。我是 C++ 新手。

1 - 为什么 SDL 网站建议尽可能动态链接?

我了解动态库的好处。但是,假设用户已经安装了您需要的所有库并准备好进入他们的系统,这是 IMO 的一个很大的假设。 对我来说,动态链接听起来是个好主意的唯一情况是,您使用的是操作系统/平台附带的众所周知的库。 https://wiki.libsdl.org/Installation

2 - 动态链接似乎会自动找到(SDL2 和 SDL2_image)的内在依赖关系。静态链接不会。为什么会这样?这是我的FindSDL2_image.cmake文件


find_path(SDL2_IMAGE_INCLUDE_DIR SDL_image.h)
include_directories(${SDL2_IMAGE_INCLUDE_DIR})

# PREFER STATIC LIBRARIES ########
# cmake respects the order of extensions when looking for libraries
SET(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
# ------------------- ########

find_library(SDL2_IMAGE_LIBRARY NAMES SDL2_image PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX})
set(SDL2_IMAGE ${SDL2_IMAGE_LIBRARY})

这会静态链接 sdl2_image。链接不正确是因为Undefined symbols:

  "_png_set_strip_16", referenced from:
      _IMG_LoadPNG_RW in libSDL2_image.a(IMG_png.o)
  "_png_set_write_fn", referenced from:
      _IMG_SavePNG_RW_libpng in libSDL2_image.a(IMG_png.o)
  "_png_write_png", referenced from:
      _IMG_SavePNG_RW_libpng in libSDL2_image.a(IMG_png.o)

如果我删除 cmake 文件中的 ### PREFER STATIC LIBRARIES ## 部分。它动态链接,一切都按预期工作。为什么动态链接时内在依赖关系得到解决,而静态链接时却没有?

----更新----

我能够通过显式包含其依赖项来静态链接 sdl2_image

find_library(PNGLIB png)
find_library(JPEG jpeg)
find_library(TIFF tiff)
find_library(WEBP webp)
find_library(LZ z)

target_link_libraries(smb ${SDL2} ${PNGLIB} ${JPEG} ${TIFF} ${WEBP} ${SDL2_IMAGE} ${LZ})

但是,这对我来说不会很好地扩展。 弄清楚这些依赖关系需要一些猜测和谷歌搜索。理想情况下,我希望 CMAKE 自动将它们拉入。

【问题讨论】:

  • 一切都按预期工作?我认为这可能有点为时过早.. 最好说一切似乎都按预期工作。 :) 'ldd' 对你的动态可执行文件有什么看法?动态链接器是否正在寻找并找到 libpng.so.x ?

标签: c++ cmake sdl-2 static-linking sdl-image


【解决方案1】:

好像有几个问题,我会尽量一步一步回答问题:

为什么 SDL 网站建议随时动态链接 可能

将应用程序与库动态链接的原因之一是将应用程序与库分离(在本例中称为共享库/.so)。您可以更新您的库,而无需重新编译您的应用程序代码。例如。如果您完成了项目,并且您的客户正在运行您的应用程序,我想,一旦您使用的底层库修复了错误,您就不太可能想要重新编译您的应用程序代码。

另一方面,通过链接您的应用程序statically,您将您的应用程序与该库绑定(.lib 或.a 表单)。意味着库中的每次更改都会导致您重新编译代码。有时这是希望的,例如您已经为您的客户提供了应用程序的保证,通常您希望确保您的库将来不会出现问题导致您的应用程序崩溃。

我有一个简短的示例代码可以更好地理解这一点: CMakeLists.txt:

cmake_minimum_required (VERSION 3.0)
project(linkageLibrary)

set(STATIC_LIB lib_static)
set(SHARE_LIB lib_share)

set(STATIC_OTHER_LIB lib_otherstatic)
set(SHARE_OTHER_LIB lib_othershare)


add_library(${STATIC_LIB} STATIC ${STATIC_LIB}.cpp)
add_library(${SHARE_LIB} SHARED ${SHARE_LIB}.cpp)

# not yet in usage...
add_library(${STATIC_OTHER_LIB} STATIC ${STATIC_OTHER_LIB}.cpp)
add_library(${SHARE_OTHER_LIB} SHARED ${SHARE_OTHER_LIB}.cpp)

add_executable(${CMAKE_PROJECT_NAME} main.cpp)
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ${${CMAKE_PROJECT_NAME}_SOURCE_DIR})
target_link_libraries(${CMAKE_PROJECT_NAME} ${STATIC_LIB} ${SHARE_LIB})

file(WRITE ${CMAKE_BINARY_DIR}/exchangeShareLibrary.sh "
 echo \"before exchange the static library\"
 ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME} &&
 mv ${CMAKE_BINARY_DIR}/lib${SHARE_LIB}.so ${CMAKE_BINARY_DIR}/lib${SHARE_LIB}.so.bk &&
 cp ${CMAKE_BINARY_DIR}/lib${SHARE_OTHER_LIB}.so ${CMAKE_BINARY_DIR}/lib${SHARE_LIB}.so &&
 echo \"after the shared library has been changed\" &&
 ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}")

file(WRITE ${CMAKE_BINARY_DIR}/exchangeStaticLibrary.sh "
 echo \"before exchange the static library\"
 ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME} &&
 mv ${CMAKE_BINARY_DIR}/lib${STATIC_LIB}.a ${CMAKE_BINARY_DIR}/lib${STATIC_LIB}a.bk &&
 cp ${CMAKE_BINARY_DIR}/lib${STATIC_OTHER_LIB}.a ${CMAKE_BINARY_DIR}/lib${STATIC_LIB}.a &&
 echo \"after the static library has been changed\" &&
 ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}")

main.cpp:

#include <iostream>
#include "lib.hpp"
using namespace std;

int main() {
    printStaticLib();
    printShareLib();
    return 0;
}

lib.hpp:

#pragma ONCE

void printStaticLib();
void printShareLib();

lib_static.cpp:

#include <iostream>
#include "lib.hpp"
using namespace std;

void printStaticLib() {
    cout << "linkage of lib_static" << endl;
}

lib_share.cpp:

#include <iostream>
#include "lib.hpp"
using namespace std;

void printShareLib() {
    cout << "linkage of lib_share" << endl;
}

lib_otherstatic.cpp:

#include <iostream>
#include "lib.hpp"
using namespace std;

void printStaticLib() {
    cout << "linkage of the other lib_static with other text" << endl;
}

lib_othershare.cpp:

#include <iostream>
#include "lib.hpp"
using namespace std;

void printShareLib() {
    cout << "linkage of the other lib_share with other text" << endl;
}

如果您运行生成的脚本,您会注意到printShareLib() 在.so 获取交换后会输出不同,但printStaticLib() 不会。 (在不清理的情况下再次尝试make,如果您现在执行./linkageLibrary,这次您还将获得printStaticLib() 的其他输出。你能猜到原因吗?)

到你的第二期:

2 - 动态链接似乎会自动找到(SDL2 和 SDL2_image)的内在依赖关系。静态链接不会。为什么会这样?

在不知道您的系统设置的情况下,我猜您的构建系统找不到 .a 静态库。试着找到它在哪里,最后放上find_library(SDL2_IMAGE_LIBRARY NAMES SDL2_image HINTS ${_YOUR_SDL2_INSTALLATION_PATH})link

现在回到你的问题,为什么 SDL2 建议动态链接,这是库开发者的决定,你可以在他的README中读到

也请参考这个blog,了解如何将 SDL2 与 CMake 结合使用

【讨论】:

  • 感谢您抽出宝贵时间回答!我了解动态库的好处。不明白的是为什么有人想要动态链接 SDL,以及为什么 SDL 甚至建议这样做。您的软件的最终用户很有可能没有在他们的系统中安装它,并且应用程序甚至不会启动。假设该库存在于您的用户计算机上是一个巨大的风险,只是为了有可能在未来更新到新版本的 SDL。动态链接对平台库(已随操作系统提供的库)有意义
  • 好吧,至少对于 linux 你有它,例如Debian 软件包。优点:SDL 的安装可以由系统管理员而不是开发人员完成(它们通常更便宜)。其他优点:您可以保持代码整洁,并且如果您使用昂贵的库,您的客户必须购买它而不是开发人员。这就是人们想要动态链接他们的应用程序的原因。通常开发者只会链接他们的应用程序,如果他们真的需要的话。 (如果您要链接大型库,您的应用程序将会增长)所以最后是偏好问题,为什么他们建议动态链接。
  • 我猜,你读过一些关于它们的文献,也许this 也很有趣。但是有一点我可能忘了提,cpp(库)开发(C没有共享库)也随着UNIX/Linux系统的发展而发展,所以依赖操作系统的库处理被认为是轻量级的,和错误修剪。不要忘记,您只需 apt install libSDL2-dev 即可将其安装到您的发行版中。
  • 我知道动态链接有它的优势,我在这里专门讨论 SDL。 SDL 主要用于制作游戏。我希望能够将文件发送给我的朋友或上传到 Internet 的链接,让人们玩游戏,而我不必担心他们拥有的 Linux 发行版或制作有关如何下载 SDL 的复杂教程。一些 Linux 发行版可能预装了 SDL,但我不会自动假设所有发行版都安装了它。
  • 好的,现在我了解您的用例了。无论如何,我想他们为什么建议动态链接的答案只能由开发人员回答(除了我试图为您提供的明显原因)请找到他们的 README ,特别是您的问题,我会看看我能做什么.
【解决方案2】:

静态库只是编译后的目标文件的集合。它没有关于其依赖库的任何附加信息。您可以使用ar -t &amp;ltstatic_lib&amp;gt 命令查看静态库的内容。因此,如果您需要将可执行文件链接到静态库,则必须提供其所有依赖库的名称和路径。

例如:考虑两个静态库,A 和 B,并假设 A 依赖于 B。然后如果希望你的 exe C 链接到 A,那么你的链接语句应该包含 A 和 B。那么只有所有的符号会正确定义。
链接器:C ===> A 和 B

动态库与静态库不同且更智能。它是 ELF 格式,它的标题包含有关其依赖共享库的信息。这可以通过在其上发出ldd &amp;ltdynamic_lib&amp;gt 命令来查看。动态加载器也知道在运行时从标准位置选择依赖库。如果找不到,就会报错。在您的情况下,您将在 ldd 输出中找到有关动态库的依赖库的信息,并且大概所有这些库都将位于标准位置。这就是您在尝试动态链接时没有发现错误的原因。

这里有一些有用的链接可以了解更多信息,
https://renenyffenegger.ch/notes/development/dynamic-loader
https://amir.rachum.com/blog/2016/09/17/shared-libraries/

【讨论】:

    【解决方案3】:

    当您在 CMake 中执行 find_package 时,将在某些 CMAKE 定义的路径中搜索 Findxxx.cmake 文件。

    该命令有两种搜索包的模式:“模块”模式和“配置”模式。当使用上述简化签名调用命令时,模块模式可用。 CMake 在 CMAKE_MODULE_PATH 中搜索名为 Find.cmake 的文件,然后是 CMake 安装。

    (https://cmake.org/cmake/help/latest/command/find_package.html)

    所以你必须定义你自己的FindSDL2.cmake,它会告诉你图书馆在哪里。 (https://cmake.org/cmake/help/v3.17/manual/cmake-developer.7.html)

    你需要告诉find_package 来搜索你自己的FindSDL2.cmake。您可以将路径传递给find_package 来执行此操作。

    如果您让 CMake 使用您的文件,变量 ${SDL2_INCLUDE_DIRS} 和 ${SDL2_LIBRARIES} 您将是您在文件中定义的变量。

    【讨论】:

    • 感谢您的回复!我现在更了解 find_package 了。我很肯定它使用/usr/local/Cellar/sdl2/2.0.12_1/lib/cmake/sdl2-config.cmake 将自己配置为一个库。看起来它将自己定义为 STATIC 和 SHARED 库,我现在更加困惑。这是该文件的内容,gist.github.com/feresr/0c1c29fb2887a1d75bc4db7785e8231d
    • 通过“将自己定义为静态和共享”我的意思是->这两行存在 add_library(SDL2::SDL2 SHARED IMPORTED) 和 add_library(SDL2::SDL2-static STATIC IMPORTED) 定义.
    • 你确定你的 CMakeLists 它正在使用这个 .cmake 吗?因为它不是 FindSDL.cmake
    • 我确定,作为快速测试,我在 sdl2-config.cmake 文件中添加了message("this is being run"),并将其打印到控制台。
    【解决方案4】:

    这里有几种可能性..

    最简单的解释是,您在动态链接器可以找到的某个位置(/usr/lib、/usr/local/lib 等)安装了动态 libpng.so.x,但没有静态 libpng 。一种。在这种情况下,答案是安装一个静态 libpng.a 并查看 cmake 的魔法是否可以解决它。

    下一个最简单的解释是您的系统上没有 libpng.* 并且正在进行一些动态后期绑定。例如,如果您正在构建的软件也在构建 libpng.*,则可能会发生这种情况,在这种情况下,后期绑定将允许您成功地将应用程序动态链接到它,即使 libpng 实际上尚未安装在 /usr 中/lib 或 /usr/local/lib 或您正在构建的软件最终将其放置的任何位置。这是一种先有鸡还是先有蛋的情况。您可以通过发出“ldd foo”来解决这个问题,其中“foo”是您的应用程序的名称。输出将是动态链接器需要为您的可执行文件获取的所有东西的杂货清单,libpng 应该在列表中,并且应该列出一个已解析的路径,如果不是,您的应用程序可能会启动但随后会崩溃例如,如果在某些时候调用了 png_write_png()。解决此问题的方法是在构建之前安装您的应用程序所期望的 libpng.* 库。

    希望这会有所帮助:)

    【讨论】:

    • 谢谢!我检查了libpng.a 已经存在于/usr/local/lib/ 中。请查看我上面更新的问题(如果我明确添加所有依赖项,我的程序运行正常)
    猜你喜欢
    • 2015-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-11
    • 2020-02-09
    • 1970-01-01
    • 1970-01-01
    • 2013-10-09
    相关资源
    最近更新 更多