对于CMake系列知识点来说,第三方库的使用是一个绕不开的知识点,废话不多说,直接开讲。
在此之前还是必须先简单了解一下基本知识点:库可以分为静态库与动态库。
- 静态库:静态库在程序编译链接时,将库中用到的代码直接链接(或者说复制)到最终的可执行文件中。这意味着,一旦你的程序链接了静态库,那么即使在没有库文件的系统上,你的程序也能正常运行,因为它已经包含了所有需要的代码。然而,这也会导致你的可执行文件比链接动态库的版本大,因为它包含了所有的库代码。
通常的文件后缀:
WindowsLinux和AndroidmacOS和iOS| *.lib | *.a | *.a或*.framework |
- 动态库:与静态库不同,动态库在程序编译链接时,并不会被复制到最终的可执行文件中。相反,当程序运行时,它会从系统中加载动态库。这意味着,如果你的程序链接了动态库,那么在运行程序的系统上,需要有一个相应的动态库文件。动态库的优点是它可以被多个程序共享,这可以减少磁盘空间和内存的使用。此外,如果动态库更新了,程序可以在不重新编译的情况下使用新版本的库。
通常的文件后缀:
WindowsLinux和AndroidmacOS和iOS| *.dll | *.so | *.dylib或*.framework |
libc++_shared.so
这个库就是C++在Android上包含了C++ 标准库的实现(包括IO操作、字符串处理、数学计算等功能),以及一些底层的服务,如内存管理和线程处理。此外,C++运行时库还包含了运行C++程序所必需的启动和退出代码。例如,在程序启动时,C++运行时库会负责初始化全局和静态变量,在程序退出时,C++运行时库会负责清理资源。。在Android里,它是由NDK提供的,具体位置如下图:
当然,它还有另一种形式---静态库,位置也来张图:
至于用哪个可以根据需求来调整。
可能看完前面的介绍现在还是有一些人不能理解这个运行时库到底是什么?那我可以提一下Java Runtime Environment(JRE),你现在大概知道它是什么了吧?你没看错,C++也是需要运行时库的,只是说这个运行时库不会很大,有的是直接静态导入到了exe里,所以你可能找不到它。
所以,在链接第三方库时(在这里系统库也算是第三方库),可以用静态或动态的方式来链接这个C++运行时库。比如:你在Windows上用MSVC来编译链接你的库时,你在cmake里可以设置它是用静态还是动态的方式来链接:
#设置为静态链接运行时库
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
或者
#设置为动态链接运行时库
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL$<$<CONFIG:Debug>:Debug>")
/MT/MTd/MD/MDd
MultiThreaded/MTMultiThreadedDebug/MTdMultiThreadedDLL/MDMultiThreadedDebugDLL/MDd
/MD/MDdMultiThreadedDLLMultiThreadedDebugDLL
额。。。如果是其他编译器呢?
- 我们先来看静态链接运行时库:
GCC(包括MinGW):
target_link_options(myprogram PRIVATE -static-libstdc++ -static-libgcc)
#或者要想全部使用静态链接的话(但并不推荐,甚至可能有些操作系统会报错):
target_link_options(myprogram PRIVATE -static)
Clang:
target_link_options(myprogram PRIVATE -static-libc++ -static-libc++abi)
那如果是Android平台呢?
#可以,但不推荐,因为是全局的设置,甚至可能报错。
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libc++ -static-libc++abi")
#或者用android平台更简单的方式
set(ANDROID_STL c++_static)
如果是JNI的话也可以直接在gradle里这样配置:
android {
defaultConfig {
externalNativeBuild {
cmake {
arguments "-DANDROID_STL=c++_static"
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
- 再说动态链接运行时库:
因为默认情况下,所有平台都是默认的动态链接运行时库,所以只要你不明确的指定是静态链接运行时库,那么它就一定是动态链接运行时库。
那如果我是一个“铁脑壳”,非要指定呢?
MSVC:
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL$<$<CONFIG:Debug>:DebugDLL>")
GCC和Clang:
target_link_options(your_target PRIVATE -shared-libgcc -shared-libstdc++)
Android:
#不推荐:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -shared-libgcc -shared-libstdc++")
#或
#推荐:
set(ANDROID_STL c++_shared)
如果是JNI的话也可以直接在gradle里这样配置:
android {
defaultConfig {
externalNativeBuild {
cmake {
arguments "-DANDROID_STL=c++_shared"
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
既然是这样,那这个C++运行时库在哪?分发的时候需要我手动添加吗?
libc++_shared.somsvcp140.dllMicrosoft Visual C++ Redistributable
好,现在到这里解释了动态库/静态库与运行时库,我们现在可以自由配置链接它们了,那CMake里有哪些链接方式呢?
find_packageFind.cmakeConfig.cmakefind_package
#如果你想静态链接就加上这句代码,否则就是动态链接。每个库的变量不一样,请自己查找。
set(OPENSSL_USE_STATIC_LIBS TRUE)
find_package(OpenSSL REQUIRED)
target_link_libraries(MyExecutable PRIVATE OpenSSL::SSL)
OpenSSL::SSL
需要注意的是,并不是所有库都提供了CMake的查找模块,也不是所有查找模块都提供了选择静态链接或动态链接的选项。
target_link_libraries
#静态
target_link_libraries(MyExecutable PRIVATE "/path/to/mylibrary.lib")
#动态,它们之前的区别就是文件本身。
target_link_libraries(MyExecutable PRIVATE "/path/to/mylibrary.so")
"/path/to/mylibrary.lib"
- 创建导入目标:你可以手动创建一个导入目标,然后在这个导入目标上设置库文件的路径以及其他属性。例如:
# 动态链接
add_library(MyLibrary SHARED IMPORTED)
set_target_properties(MyLibrary PROPERTIES
IMPORTED_LOCATION "/path/to/mylibrary.dll" # Windows
# 或者
# IMPORTED_LOCATION "/path/to/mylibrary.so" # Linux
INTERFACE_INCLUDE_DIRECTORIES "/path/to/mylibrary/headers"
)
# 静态链接
add_library(MyLibrary STATIC IMPORTED)
set_target_properties(MyLibrary PROPERTIES
IMPORTED_LOCATION "/path/to/mylibrary.lib" # Windows
# 或者
# IMPORTED_LOCATION "/path/to/mylibrary.a" # Linux
INTERFACE_INCLUDE_DIRECTORIES "/path/to/mylibrary/headers"
)
target_link_libraries(MyExecutable PRIVATE MyLibrary)
MyLibrary"/path/to/mylibrary.*""/path/to/mylibrary/headers"add_library(MyLibrary SHARED IMPORTED)SHAREDSTATIC
find_pathfind_libraryfind_package_handle_standard_args
好像讲到这里还没说,静态库和动态库在链接时,为什么需要跟C++运行时库发生点关系?因为如果你链接的库是使用静态运行时库编译的,那么你自己的库用动态运行时的话就会报:error LNK2038错误(MSVC里是这个错,其他的自己试)。所以,自己的库要与链接的库的C++运行时库要相同。
暂时就这么多,本来还想多写点,发现有其他事要做了。。。