使用cmake构建静态库的静态库

Posted

技术标签:

【中文标题】使用cmake构建静态库的静态库【英文标题】:Using cmake to build a static library of static libraries 【发布时间】:2018-10-05 22:23:50 【问题描述】:

我正在尝试创建一个静态库的静态库。这是我的CMakeLists.txt

cmake_minimum_required(VERSION 2.8)

project(myRtspClient)

add_subdirectory(../third_party/Base64_live555 base64_live555)
add_subdirectory(../third_party/md5 md5)
add_subdirectory(../third_party/JRTPLIB jrtplib)

include_directories(include)
include_directories(../third_party/Base64_live555/include)
include_directories(../third_party/md5/include)
include_directories(jrtplib/src)
include_directories(../third_party/JRTPLIB/src)

file(GLOB SOURCES "*.cpp")

add_library(myRtspClient STATIC $SOURCES)

add_library(libmd5 STATIC IMPORTED)
SET_PROPERTY(TARGET libmd5 PROPERTY IMPORTED_LOCATION ./md5/libmd5.a)

add_library(libbase64_live555 STATIC IMPORTED)
SET_PROPERTY(TARGET libbase64_live555 PROPERTY IMPORTED_LOCATION ./base64_live555/libbase64_live555.a)

add_library(libjrtp STATIC IMPORTED)
SET_PROPERTY(TARGET libjrtp PROPERTY IMPORTED_LOCATION ./jrtplib/src/librtp.a)

target_link_libraries(myRtspClient libmd5 libbase64_live555 libjrtp)
#install(TARGETS myRtspClient DESTINATION /usr/lib)

如果想看全图:https://github.com/lucaszanella/myRtspClient/blob/8658dbcb8ed071b8d2649a471455f57f268932f4/myRtspClient/CMakeLists.txt

如您所见,我正在尝试通过将目标 myRtspClientlibmd5 libbase64_live555 libjrtp 链接来创建它。因为cmake 没有错误,即使我这样做了

target_link_libraries(myRtspClient eewgg dsgsg dgsgsdgsg)

我不能确定是什么错误。图书馆在我指出的位置。但是,我不知道它们是否在第一次编译中。我尝试了第二个,但谁知道......

所以,继续...我尝试了很多SET_PROPERTY,例如:

SET_PROPERTY(TARGET libbase64_live555 PROPERTY IMPORTED_LOCATION ./base64_live555/libbase64_live555.a)

SET_PROPERTY(TARGET libbase64_live555 PROPERTY IMPORTED_LOCATION ./base64_live555)

SET_PROPERTY(TARGET libbase64_live555 PROPERTY IMPORTED_LOCATION base64_live555/libbase64_live555.a)

当我转到examples 并尝试构建common_example.cpp(如有必要,请参阅上面链接中的源代码树)我会这样做:

g++ common_example.cpp -I ../myRtspClient/include ../myRtspClient/libmyRtspClient.a

但我收到如下链接错误:

utils.cpp:(.text+0x2f4): undefined reference to `MD5Init(MD5_CTX*)'
utils.cpp:(.text+0x316): undefined reference to `MD5Update(MD5_CTX*, unsigned char*, unsigned int)'
utils.cpp:(.text+0x32c): undefined reference to `MD5Final(MD5_CTX*, unsigned char*)'
../myRtspClient/libmyRtspClient.a(MediaSession.cpp.o): In function `MyRTPSession::MyRTPSession()':
MediaSession.cpp:(.text._ZN12MyRTPSessionC2Ev[_ZN12MyRTPSessionC5Ev]+0x1e): undefined reference to `jrtplib::RTPSession::RTPSession(jrtplib::RTPRandom*, jrtplib::RTPMemoryManager*)'
../myRtspClient/libmyRtspClient.a(myRtpSession.cpp.o): In function `MyRTPSession::IsError(int)':
myRtpSession.cpp:(.text+0x48): undefined reference to `jrtplib::RTPGetErrorString[abi:cxx11](int)'
../myRtspClient/libmyRtspClient.a(myRtpSession.cpp.o): In function `MyRTPSession::MyRTP_SetUp(MediaSession*)':
myRtpSession.cpp:(.text+0x1b5): undefined reference to `jrtplib::RTPSessionParams::RTPSessionParams()'
myRtpSession.cpp:(.text+0x25c): undefined reference to `jrtplib::RTPSession::Create(jrtplib::RTPSessionParams const&, jrtplib::RTPTransmissionParams const*, jrtplib::RTPTransmitter::TransmissionProtocol)'

我在链接过程中做错了什么? libMyRtspClient 应该将所有这些库链接到它。

更新:

似乎我无法将静态库链接在一起,也无法从静态库创建共享库。我应该如何将所有代码打包到一个共享库和一个静态库中?

【问题讨论】:

静态库不链接到其他静态库。 @tkausl 好的,我改成add_library(myRtspClient SHARED $SOURCES),不是我得到/usr/bin/ld: md5/libmd5.a(md5.cpp.o): relocation R_X86_64_PC32 against symbol _Z12MD5TransformPjPh'不能在制作共享对象时使用;使用 -fPIC` 重新编译。好像我不能使用静态库来制作共享库?这里的解决方案是什么? Seems like I cannot use static libraries to make a shared one? - 你可以。只需按照错误消息:使用 -fPIC 重新编译静态库或在没有它的情况下编译共享库。 这可能会有所帮助:Answer to "CMake: include library dependencies in a static library". 【参考方案1】:

首先要知道:一个不链接静态库 - 一个使用存档器(Linux 上的ar),它只是将所有对象文件放入一个存档 - libXXX.a

从其他静态库构建静态库不是很常见,但并非不可能(虽然我不知道如何用 cmake 来做,但如果一切都失败了 - 你仍然有add_custom_command)。

假设您有两个静态库libA.alibB.a,并希望将它们合并到一个组合库libALL.a。最直接的方法是解压两个档案(记住静态库毕竟只是档案),并将所有解压的目标文件打包到一个新的档案/静态库libALL.a(请参阅man pages of ar以获取更多信息使用的选项):

ar -x libA.a
ar -x libB.a
ar -crs libALL.a *.o

另一种可能性是使用mri-script 代替ar,使用它我们可以避免所有未打包的目标文件(而且它更健壮,因为并非所有目标文件都有*.o-扩展名):

ar -M <<EOM
    CREATE libALL.a
    ADDLIB libA.a
    ADDLIB libB.a
    SAVE
    END
EOM

有些人还跑了

ar -s libALL.a 

或同等的

ranlib libALL.a 

以确保创建存档索引(这是区分静态库与简单存档的唯一区别),但无论如何都会默认构建存档索引(至少ar-versions 是这种情况到目前为止我都用过)。

另一个注意事项:直观(更类似于 VisualS tudio 命令lib.exe /OUT:libALL.lib libA.lib libB.lib

ar -crs libALL.a libA.a libB.a

不会生成可供链接器使用的存档 - ar 需要目标文件并且不够聪明,无法查看子存档以找到它们。


共享库已链接。此外,共享库需要位置独立代码,这意味着所有目标文件都必须使用选项-fPIC 编译。

通常一个库提供两个版本:

    静态,编译时没有-fPIC-fPIC共享、编译

在没有-fPIC 的情况下编译,静态版本的效率略高。它还确保不会将静态库用作共享库中的依赖项,这可能导致违反One Definition Rule。

根据经验,共享库应该依赖于其他共享库而不是静态库。

显然,您可以使用 -fPIC 重新编译所有静态库并将它们链接到一个共享库 - 但我不推荐这样做。

【讨论】:

以上是关于使用cmake构建静态库的静态库的主要内容,如果未能解决你的问题,请参考以下文章

CMake 学习四:CMake 构建静态库和动态库

CMake-构建静态库的困难

在 CMake 中安装静态库的调试和发布配置

使用 CMake 将几个静态库合并为一个

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

CMake:检查静态 C++ 库的可用性