使用CMake构建静态库和动态库

一、准备工作

本机演示环境为:
主机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)

现在的工程看起来是这个样子:

20230101180244

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

20230101183109

四、动态库的版本号

按照规则,动态库是应该包含一个版本号的,看起来像这个样子:

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})

20230101191651

五、安装动态库和头文件

动态库或者静态库构建完成后,还需要将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,执行完毕后,就可以看到头文件和库文件已经安装到指定目录了,执行效果如下:

20230101193850

猜你喜欢

转载自blog.csdn.net/hubing_hust/article/details/128515724