使用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
如您所见,我正在尝试通过将目标 myRtspClient
与 libmd5 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.a
和libB.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构建静态库的静态库的主要内容,如果未能解决你的问题,请参考以下文章