【问题标题】:Linking mingw libs with cmake将 mingw 库与 cmake 链接
【发布时间】:2021-03-23 18:54:17
【问题描述】:

长话短说,我将 Godot 构建系统重写为 cmake(仅 windows 部分),主要是因为我想学习它,但是我在使用 mingw 编译 Godot 时遇到了麻烦。当我试图编译它时,起初一切都很好,直到链接最终 exe 时,我得到了很多“未定义的引用”错误。看起来主库(核心/场景/编辑器/..)无法看到彼此的功能。 MSVC 构建工作正常,scons 版本也在 mingw 下编译,所以我显然错过了我的 cmake 版本中的一些内容。我试图在我的 cmake 脚本中删除一些编译/链接选项作为测试,但没有任何改变。我真的不知道如何调试这个问题,所以如果有人能把我踢向正确的方向,我会很高兴的。

【问题讨论】:

  • 你确定你没有混合使用 mingw 和 msvc 构建的二进制文件吗?
  • 是的,我很确定。我什至为一些“未定义”的函数转储了符号,只是为了检查函数名称的修改,它们是正确的(而且我此时清除了几次构建文件夹,问题绝对不在那个)

标签: cmake mingw godot


【解决方案1】:

好的,我终于有时间回到这个了。

问题

所以基本上问题在于对 godot 库的循环依赖。我不认为这是问题,因为我被 MSVC 宠坏了(它不依赖于链接顺序)。此外,我尝试在一个规模小得多的测试项目中复制 mingw 中的循环依赖。该项目是一个 30 个库,每个库中有两个函数,第一个函数打印字符串,另一个调用所有 30 个库中的所有第一个函数,因此有 30 个循环依赖库。奇怪的是,该项目链接没有问题,并打印了 30^2 个字符串..

解决办法

解决方案是在所有库周围使用 -Wl,--start-group/-Wl,--end-group 链接标志。有两种方法可以做到。

第一种方法,是将所有库添加到某个排序列表中。这可能是全局属性,也可能是某个目标上的属性(不仅仅是一个简单的变量),因此可以从其他子目录访问它。形成库列表后,您只需将其链接到可执行文件,如下所示

# getting all your libs from the global property..
get_property(__LIBS_LIST GLOBAL PROPERTY EXE_LIBS_LIST)
# linking all libraries to the exe..
target_link_libraries(my-exe PRIVATE -Wl,--start-group ${__LIBS_LIST} -Wl,--end-group)

这是最简单的解决方案,但要谨慎对待链接到 exe 的库的依赖关系,因为似乎当 CMake 为你的 exe 创建链接行时,它首先列出了所有直接链接到你的 exe 的库,并且只有在它放置来自直接链接的库的依赖项的库之后。基本上,如果你的目标依赖树看起来像这样:

exe // your main exe file
    - lib_A // lib A linked directly to the main exe
        - lib_AA // lib AA linked to the lib_A
            - lib_AAA // lib AAA linked to the lib_AA
    - lib_B // lib B linked directly to the main exe
        - lib_BB // lib BB linked to the lib_B
            - lib_BBB // lib BBB linked to the lib_BB

您的 exe 链接顺序如下所示:

// first libs linked directly to the exe 
lib_A
lib_B
// only after recursively initial libs dependencies 
lib_AA
lib_AAA
lib_BB
lib_BBB

不过,如果您要链接 target_link_libraries(my-exe PRIVATE -Wl,--start-group ${__LIBS_LIST} -Wl,--end-group) 之类的库,--start-group 和 --end-group 将只保护直接链接到您的 exe 的库。我没有发现文档中对此进行了描述,但我发现了一个关于相同行为的问题(CMake library linking order)。另外,当我在 mingw 上对此进行测试时,lib_AA/lib_AAA/lib_BB/lib_BBB 的 lib_AA/lib_AAA/lib_BB/lib_BBB 究竟是通过 PRIVATE 还是通过 INTERFACE 链接的并不重要,结果都是一样的。

第二种方式,是利用链接扩展的递归性来获取直接链接到exe的库的依赖关系。从我的示例中您可以看到,lib_A (lib_AA/lib_AAA) 的依赖项没有与 lib_B (lib_BB/lib_BBB) 的依赖项混合。所以基本上我们可以做的是创建 INTERFACE 库并在之后立即连接到它 -Wl,--start-group。然后将任意数量的库添加到它的界面并将 global-libs 链接到您的 exe(顺序无关紧要)。最后,关闭全局库中的组

add_library(global-libs INTERFACE)
target_link_libraries(global-libs INTERFACE -Wl,--start-group)
# ...
# linking another libs, and linking global-libs to exe
# ...
target_link_libraries(global-libs INTERFACE -Wl,--end-group)

这将确保连接到全局库的所有库都被 -Wl,--start-group/-Wl,--end-group 包围。

现在,理论上,CMake 应该自己处理循环依赖,通过将库多次放置在链接行中(多少次由 LINK_INTERFACE_MULTIPLICITY 控制)。但是这种方法对我不起作用(我只是错过了一些东西..)。另外,您需要声明 cmake 目标之间的依赖关系,并且使用 -Wl,--start-group/-Wl,--end-group 您可以将一个特定的接口库设置为所有具有循环依赖关系的库的持有者..

【讨论】:

    猜你喜欢
    • 2018-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-31
    • 2021-11-30
    相关资源
    最近更新 更多