文章目录
CMake 构建静态库和动态库
本章介绍 CMake 构建静态库和动态库的方法,先看看静态库和动态库的区别:
- 静态库的扩展名一般为 *.a 或 *.lib;动态库的扩展名一般为 *.so 或 *.dll ;
- 静态库在编译时会直接整合到目标文件中,编译成功的可执行文件可独立运行;
- 动态库在编译时不会整合到目标文件中,可执行程序无法单独运行,需要有动态库文件;
一般动态库比较常用。下面通过两个实例来分别讲解 CMake 构建静态库和动态库的方法。
CMake 构建静态库
任务实例:构建静态库 libadd.a ,提供 AddFunc 函数,函数内部做加法运算。代码结构如下
[mayw@localhost lib_a]$ tree ..
├── CMakeLists.txt
└── lib
├── add.cpp
├── add.h
└── CMakeLists.txt
头文件 lib/add.h 中的内容
#ifndefADD_H_#defineADD_H_intAddFunc(int m,int n);#endif// ADD_H_
源文件 lib/add.cpp 中的内容
#include"add.h"intAddFunc(int m,int n){return m + n;}
lib/CMakeLists.txt 中的内容
set(lib_src add.cpp)
add_library(add STATIC ${lib_src})
add_library 参数说明
- add :库文件名称,Linux 上生成库文件会自动加上前后缀,如当前的静态库文件名称为 libadd.a;
- STATIC :静态库,动态库为 SHARED;
- ${lib_src} :构造库文件所需的源码文件。
最外层 CMakeLists.txt ,生成的库文件放在 build/lib 目录中
cmake_minimum_required(VERSION 3.5)
project(libadd)
add_subdirectory(lib lib)
此时使用外部构建方法,在 build/lib 目录中会生成静态库 libadd.a 。
$ mkdir build
$ cd build
$ cmake ..
$ make
CMake 构建动态库
构建动态库也很简单,只需要讲 lib/CMakeLists.txt 中的内容改为
set(lib_src add.cpp)
add_library(add SHARED ${lib_src})
此时同样使用外部构建方法,在 build/lib 目录中会生成动态库 libadd.so 。
同时构建静态库和动态库
很多开源软件都同时提供了动态库和静态库,如果使用两条 add_library 指令是不行的。如下:
# 如果用这种方式,只会构建一个动态库,不会构建静态库,虽然静态库的后缀是 *.a
add_library(add SHARED ${lib_src})
add_library(add STATIC ${lib_src})
# 修改静态库的名字,可以同时构建动态库和静态库,但构建出的库文件名称不同(libadd.so,libadd_static.a)
add_library(add SHARED ${lib_src})
add_library(add_static STATIC ${lib_src})
此时,需要使用 set_target_properties 指令,设置库文件的输出的名称,对于动态库,还可以通过该指令设置动态库的版本号。如下指令可以同时构建动态库和静态库
set(lib_src add.cpp)
add_library(add_static STATIC ${lib_src})
# 将 add_static 重命名为 add
set_target_properties(add_static PROPERTIES OUTPUT_NAME "add")
# cmake 在构建一个新的 target 时,会尝试清理掉使用这个名称的库,
# 所以在构建 libadd.so 时,就会清理掉 libadd.a
set_target_properties(add_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
add_library(add SHARED ${lib_src})
set_target_properties(add PROPERTIES OUTPUT_NAME "add")
set_target_properties(add PROPERTIES CLEAN_DIRECT_OUTPUT 1)
使用外部构建方法,会在 build/lib 目录中同时生成静态库和动态库 libadd.a 和 libadd.so 。
设置动态库版本号
一般开源软件的动态库都有一个版本号,如 grpc 的动态库
libgrpc++.so -> libgrpc++.so.1.48
libgrpc++.so.1.48 -> libgrpc++.so.1.48.0
libgrpc++.so.1.48.0
可以在 lib/CMakeLists.txt 中进行如下设置
set_target_properties(add PROPERTIES VERSION 1.2 SOVERSION 1)
其中 VERSION 指代动态库版本,SOVERSION 指代 API 版本。编译后会产生如下链接
[mayw@localhost lib]$ ll lib*
-rw-rw-r--. 1 mayw mayw 1398 Dec 3117:49 libadd.a
lrwxrwxrwx. 1 mayw mayw 11 Dec 3117:50 libadd.so -> libadd.so.1
lrwxrwxrwx. 1 mayw mayw 13 Dec 3117:50 libadd.so.1 -> libadd.so.1.2
-rwxrwxr-x. 1 mayw mayw 7896 Dec 3117:50 libadd.so.1.2
CMake 调用库文件
安装库文件
在调用库文件之前,需要先安装头文件和库文件,当然,也可以直接将头文件和库文件拷贝给调用者(有点 low 了)。这里采用专业一点的方法:使用 CMake install 进行安装。在 lib/CMakeLists.txt 中添加如下内容
# 将头文件放到指定的 include 目录下
install(FILES add.h DESTINATION include)
# 将库文件安装到指定目录中
# TARGETS 指目标二进制文件,LIBRARY 指动态库,ARCHIVE 指静态库
install(TARGETS add add_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
使用外部构建进行编译安装,将工程安装到 /tmp/add 目录中
$ cmake -DCMAKE_INSTALL_PREFIX=/tmp/add ..
$ make
$ makeinstall[50%] Built target add[100%] Built target add_static
Install the project...
-- Install configuration: ""
-- Installing: /tmp/add/include/add.h
-- Installing: /tmp/add/lib/libadd.so.1.2
-- Installing: /tmp/add/lib/libadd.so.1
-- Installing: /tmp/add/lib/libadd.so
-- Installing: /tmp/add/lib/libadd.a
注意,CMake 构建时要通过
-DCMAKE_INSTALL_PREFIX
指定安装路径,否则会安装到默认的 /usr/local 目录中,这需要管理员权限。
调用库文件
重新新建一个工程用于测试库文件调用
[mayw@localhost add_test]$ tree ..
├── add
│ ├── include
│ │ └── add.h
│ └── lib
│ ├── libadd.a
│ ├── libadd.so -> libadd.so.1
│ ├── libadd.so.1 -> libadd.so.1.2
│ └── libadd.so.1.2
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
add 目录表示要使用的外部头文件和库文件,src/main.cpp 中的调用源码
#include<iostream>#include"add.h"intmain(int argc,char* argv[]){
std::cout <<"hello world"<< std::endl;
std::cout <<"AddFunc(3, 4)="<<AddFunc(3,4)<< std::endl;return0;}
根目录中 CMakeLists.txt 中的内容如下
cmake_minimum_required(VERSION 3.5)
project(add_test)
add_subdirectory(src)
src/CMakeLists.txt 中的内容如下
# 指定引用库的头文件路径,否则会提示找不到头文件
include_directories(${PROJECT_SOURCE_DIR}/add/include)
# 指定引用库的库文件路径,否则会提示 undefined reference to ,表示为引入库文件
link_directories(${PROJECT_SOURCE_DIR}/add/lib)
add_executable(add_test main.cpp)
# 为 hello 程序链接库文件 libadd.so
target_link_libraries(add_test add)
PROJECT_SOURCE_DIR 前面已经有说明,表示当前工程的源码路径。其中
target_link_libraries
的语法和用法如下
target_link_libraries(<target> [item1 [item2 [...]]]
[[debug|optimized|general] <item>] ...)
# 以下写法都可以
target_link_libraries(myproject add) # 连接动态库 libad.so,默认优先链接动态库
target_link_libraries(myproject libadd.a) # 显示指定链接静态库
target_link_libraries(myproject libadd.so) # 显示指定链接动态库
target_link_libraries(myproject -lcomm) # 链接动态库 libad.so
若动态库和静态库都存在,此时此时
target_link_libraries
指令 默认优先链接动态库 。使用外部构建方法,在 build/src 目录中会生成可执行文件 add_test 。
$ mkdir build
$ cd build
$ cmake ..
$ make
$ ./src/add_test
hello world
AddFunc(3, 4)=7
特殊的环境变量 CMAKE_INCLUDE_PATH 和 CAMKE_LIBRARY_PATH
这两个是环境变量等同于指定 cmake 编译时引入的头文件和库文件的路径。注意,它们是环境变量,而不是 cmake 的变量,可以在 Linux 的 bash 中进行设置。
版权归原作者 myw31415926 所有, 如有侵权,请联系我们删除。