好的,我终于有时间回到这个了。
问题
所以基本上问题在于对 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 您可以将一个特定的接口库设置为所有具有循环依赖关系的库的持有者..