CMake教程(二)- 添加静态库文件和动态库文件
什么是库文件
一般来说,一个程序,通常都会包含目标文件
和若干个库文件
。经过汇编得到的目标文件再经过和库文件的链接,就能构成可执行文件
。库文件像是一个代码仓库或代码组件的集合,为目标文件提供可直接使用的变量、函数、类等。
库文件包含了静态链接库
和动态链接库
两种。两者最根本的区别在与在程序编译的过程中,如何处理库文件和目标文件的链接关系。
静态链接库
静态链接库在Linux
系统中以.a文件
的形式存在。
在上图程序编译的链接阶段,静态库会完全复制到可执行文件中,一旦可执行文件构建完成,就不再需要静态库的存在,可执行文件在后续的使用中,也不再依赖这个静态库。
动态链接库
虽然静态库非常容易理解且不会引入依赖问题,但是试想一下,如果你在统筹构建一个无比庞大的工程,这时,其中一个开发者升级了他所开发的库。这时,你就需要花费很长的时间来重新构建这个如此庞大的工程。这时,你就可以使用动态库来避免这个问题。
动态库在程序编译的链接阶段,仅将一些重要的信息,如重定位和符号表信息复制到可执行文件中,可执行文件在后面执行的过程中,如果需要引用这个库文件,就会根据这些信息从系统中寻找对应的库文件以实现对应的功能。
静态库和动态库的区别
区别 | 静态库 | 动态库 |
---|---|---|
可执行文件大小 | 较大 (因为动态库的内容会被完全复制到可执行文件中) | 较小 |
占用磁盘大小 | 较大 (如果有多个可执行文件都用到同一个静态库,这个静态库会被多次复制到不同的可执行文件中) | 较小 (即使多个可执行文件都需要用到同一个动态库,他们也只是共用同一个动态库文件) |
拓展性与兼容性 | 全量更新 (库文件的更新会引起整个可执行文件的重新编译及发布) | 增量更新 (不需要重新编译可执行文件,只需发布动态库文件) |
依赖问题 | 无依赖问题 (已构建的可执行文件不依赖其他静态库文件) | 有依赖问题 (可执行文件的执行需要系统存在依赖的动态库文件) |
复杂程度 | 简单 | 复杂 (会引起很多问题,例如如何在运行时确定地址,库文件版本管理等) |
加载速度 | 快 | 慢 |
如何在CMake中添加库文件
下面是一个C++工程,包含了一个主程序文件和两个准备用于生成库文件的源文件。
这里有一个简单的主程序,其中调用了两个来自库文件的函数:print_char()
和print_int()
,其中print_char()
来自静态库文件,print_int()
来自动态库文件。
#include <iostream>
#include "printer_char.hpp"
#include "printer_int.hpp"
using namespace std;
int main(int argc, char* argv)
{
// 静态库 + 动态库
print_char('A'); // print_char() 来自静态库
print_int(5); // print_int() 来自动态库
return 0;
}
使用CMake来构建MakeFile,并将指定的源文件制作为静态库和动态库。
cmake_minimum_required(VERSION 3.10)
# 设置项目名
project(CMakeTutorial VERSION 1.2)
# 添加头文件搜索路径
include_directories(
${PROJECT_SOURCE_DIR}/inc
)
# 生成库
add_library(PrinterChar STATIC src/printer_char.cpp) # 静态库
add_library(PrinterInt SHARED src/printer_int.cpp) # 动态库
# 添加需要构建的可执行文件
#------方法对比 1.1 - 不用库的写法
# add_executable(LinkLibraryDemo link_library_demo.cpp src/printer_char.cpp src/printer_int.cpp)
#------方法对比 1.2 - 用库的写法
add_executable(LinkLibraryDemo demo/link_library_demo.cpp)
target_link_libraries(LinkLibraryDemo PRIVATE PrinterChar) # 为目标添加静态库
target_link_libraries(LinkLibraryDemo PRIVATE PrinterInt) # 为目标添加动态库
然后执行下面的命令来生成库文件和可执行文件
本人习惯在工程的根目录下创建一个cmake目录用于构建工程,以避免污染根目录。是在cmake目录下执行的cmake指令,所以指定的CMakeLists.txt文件是在当前目录的上一级目录。请根据你的工程目录结构选择正确的cmake指令的目录。
cmake ..
make
可见编译生成了两个库文件,静态库libPrinterChar.a
和动态库libPrinterInt.so
。此时可执行文件能够成功运行。
尝试一下删除静态库libPrinterChar.a
,并再次运行程序,发现程序依然能成功运行。这说明可执行文件的运行并不依赖静态库文件存在。
再尝试把动态库文件删掉,再运行可执行文件,运行失败,提示找不到对应的.so动态库文件
。说明可执行文件的运行依赖对应的动态库文件的存在。
CMake 中 target_link_libraries 的 PRIVATE,PUBLIC 和 INTERFACE 的区别
情况 | 使用参数 |
---|---|
只有源文件(.cpp)中包含了库文件 | PRIVATE |
只有头文件(.hpp)中包含了库文件 | INTERFACE |
源文件和头文件都包含了库文件 | PUBLIC |
关于这个问题可以参考这里