使用CMake构建静态库和动态库
- 一、准备工作
- 二、动态库的构建
- 2.1 工程改造
- 2.2 编译动态库
- 2.3 更多的说明
- 三、静态库的构建
- 3.1 错误的尝试
- 3.2 新的构建指令
- 四、动态库的版本号
- 五、安装动态库和头文件
一、准备工作
本机演示环境为:
主机windows11 + vscode
虚拟机安装的是RHEL7.6系统
使用vscode远程ssh连接linux虚拟机。
使用CMake Tool自动创建一个名为hellolib的工程。创建方式此处不再赘述。可以参考我的另一篇博文:
《vscode使用CMake Tool插件构建第一个CMake的helloworld工程》
下面会对其进行一番改造。
二、动态库的构建
2.1 工程改造
hellolib工程根目录下新建一个子目录lib,放入源文件hello.hpp和hello.cpp
hello.hpp:
#pragma once
void say_hello();
hello.cpp:
#include "hello.hpp"
#include <iostream>
void say_hello(){
std::cout << "Hello, from hellolib!\n";
}
在lib子目录下新建一个CMakeLists.txt, 内容如下:
set(LIBHELLO_SRC hello.cpp)
add_library(hello SHARED ${LIBHELLO_SRC})
修改hellolib工程根目录下的CMakeLists.txt, 内容如下:
cmake_minimum_required(VERSION 3.0.0)
project(hellolib VERSION 0.1.0)
add_subdirectory(lib)
# 下面都是CMake Tool自动生成的,本例中非必须
include(CTest)
enable_testing()
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
现在的工程看起来是这个样子:
2.2 编译动态库
仍然采用外部构建,工程根目录下新建一个build子目录:
cd build
cmake ..
make
此时我们就可以在 build/lib 子目录中得到一个 libhello.so , 这就是我们期望的动态库。
如果你要指定 libhello.so 生成的位置,可以通过在主工程文件 CMakeLists.txt 中修改 ADD_SUBDIRECTORY(lib)指令来指定一个编译输出位置或者在 lib/CMakeLists.txt 中添加SET(LIBRARY_OUTPUT_PATH <路径>)来指定一个新的位置。
2.3 更多的说明
关于上面的指令 add_library , 其语法格式如下:
ADD_LIBRARY(libname [SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)
你不需要写全 libhello.so,只需要填写 hello 即可,cmake 系统会自动为你生成
libhello.X
库的类型有三种:
- SHARED,动态库
- STATIC,静态库
- MODULE,在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待。
EXCLUDE_FROM_ALL 参数的意思是这个库不会被默认构建,除非有其他的组件依赖或者手
工构建。
三、静态库的构建
3.1 错误的尝试
同样使用上面的指令,我们在支持动态库的基础上再为工程添加一个静态库,按照一般的习惯,静态库名字跟动态库名字应该是一致的,只不过后缀是.a 罢了。
下面我们用这个指令再来添加静态库:
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
然后再在 build 目录进行外部编译,我们会发现,静态库根本没有被构建,仍然只生成了一个动态库。因为 hello 作为一个 target 是不能重名的,所以,静态库构建指令无效。
如果我们把上面的 hello 修改为 hello_static:
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
就可以构建一个 libhello_static.a 的静态库了。
这种结果显示不是我们想要的,我们需要的是名字相同的静态库和动态库,因为 target 名称是唯一的,所以,我们肯定不能通过 ADD_LIBRARY 指令来实现了。
3.2 新的构建指令
既然不能通过 ADD_LIBRARY 指令来实现, 那么这时候我们需要用到另外一个指令
SET_TARGET_PROPERTIES,其基本语法是:
SET_TARGET_PROPERTIES(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)
这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和 API 版本。
在本例中,我们需要作的是向 lib/CMakeLists.txt 中添加一条:
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
这样,我们就可以同时得到 libhello.so/libhello.a 两个库了。
此时,lib子目录下的CMakeLists.txt内容是这样的:
set(LIBHELLO_SRC hello.cpp)
set(LIBNAME hello)
add_library(${LIBNAME} SHARED ${LIBHELLO_SRC})
add_library(hello_static STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME ${LIBNAME})
再次构建:
cd build
make clean
cmake ..
make
四、动态库的版本号
按照规则,动态库是应该包含一个版本号的,看起来像这个样子:
libhello.so.1.2
libhello.so ->libhello.so.1
libhello.so.1->libhello.so.1.2
为了实现动态库版本号,我们仍然需要使用 SET_TARGET_PROPERTIES 指令。具体使用方法如下:
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
将上述指令加入 lib/CMakeLists.txt 中,
set(LIBHELLO_SRC hello.cpp)
set(LIBNAME hello)
add_library(${LIBNAME} SHARED ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES VERSION 1.2 SOVERSION 1)
add_library(hello_static STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME ${LIBNAME})
五、安装动态库和头文件
动态库或者静态库构建完成后,还需要将libhello.a, libhello.so.x 以及 hello.hpp 安装到系统目录,才能真正让其他人开发使用,在本例中我们将 hello 的共享库安装到<prefix>/lib目录,将 hello.hpp 安装到<prefix>/include/hello 目录。
这里需要使用install指令,关于install指令的详细说明,请参考我的另一篇博文:
《第一个完整的CMake工程》
我们向 lib/CMakeLists.txt 中添加install指令:
set(LIBHELLO_SRC hello.cpp)
set(LIBNAME hello)
add_library(${LIBNAME} SHARED ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES VERSION 1.2 SOVERSION 1)
set(LIBNAMESTATIC hello_static)
add_library(${LIBNAMESTATIC} STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(${LIBNAMESTATIC} PROPERTIES OUTPUT_NAME ${LIBNAME})
install(TARGETS ${LIBNAME} ${LIBNAMESTATIC}
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
install(FILES hello.hpp DESTINATION include/hello)
重新构建并安装:
cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
sudo make install
这里需要注意,如果是普通用户执行make install,可能会因为缺少权限导致执行失败,所以需要加上sudo,执行完毕后,就可以看到头文件和库文件已经安装到指定目录了,执行效果如下: