CMakeLists配置疑问:为何log库需重复链接到多共享库?
为什么Android NDK中链接器不会通过libplayer传递log库给libcodec?
这个问题的核心在于链接器的工作逻辑和不同构建系统处理依赖传递的方式差异,我们一步步拆解:
1. Android NDK链接器(GCC/Clang ld)的独立链接行为
当你构建共享库(.so)时,链接器会要求这个库自身所有用到的符号都能被解析——也就是说,libcodec里直接调用了__android_log_print,这个函数的符号定义在liblog.so中,那么libcodec在链接阶段必须明确告诉链接器要去liblog.so里找这个符号。
libplayer和libcodec是两个完全独立的共享库:
- 你给
libplayer链接liblog,只是让libplayer自己能解决它用到的log相关符号,这只会把liblog加入libplayer的依赖列表(在libplayer.so的动态节里记录)。 - 但
libcodec的链接过程是单独进行的,链接器不会因为libplayer依赖liblog,就自动把liblog加到libcodec的链接路径里——毕竟libcodec是独立的模块,链接器没有理由“猜测”它需要继承libplayer的依赖。
哪怕libplayer和libcodec之间存在互相依赖的调用关系,这种关系也只是模块间的逻辑关联,不会自动传递链接库——链接器只关心当前正在构建的库有没有解决自己的所有未定义符号。
2. Visual Studio项目依赖的差异
Visual Studio的项目系统(MSBuild)在处理**项目引用(Project Reference)**时,默认会做一些构建层面的依赖传递:
- 如果项目A引用了项目B,而项目B链接了某个库,VS会自动把这个库添加到项目A的链接输入中(这是MSBuild的规则,不是链接器的行为)。
- 但注意:这也只适用于项目引用的场景,而且如果是动态库的话,要是项目A自己直接调用了第三方库的符号,仍然需要自己显式链接——只不过VS的项目系统帮你做了一部分自动传递的工作。
而你这里用到的CMake(target_link_libraries是CMake命令)默认不会自动传递链接依赖,除非你明确指定链接的范围:
- 用
PRIVATE:链接库仅当前目标使用,不会传递给依赖它的目标; - 用
PUBLIC/INTERFACE:链接库会传递给所有依赖当前目标的目标。
但回到你的问题:libcodec是直接调用了liblog的函数,所以哪怕你给libplayer用PUBLIC链接liblog,也帮不了libcodec——因为libcodec自己的符号未定义问题,必须自己解决。
3. 总结
你遇到的情况是正常的链接器行为:
- 每个共享库在构建时必须独立解决自己的所有未定义符号,依赖的库不会自动从其他模块传递过来;
- Visual Studio的自动传递是构建系统层面的便利,不是链接器的通用规则;
- 解决方法就是你已经做的:给
libcodec单独添加target_link_libraries(libcodec ${log-lib}),让链接器知道去liblog.so里找__android_log_print的符号。
内容的提问来源于stack exchange,提问作者Sirop4ik
相关产品推荐
相关产品推荐

