[cmake]CMake 生成静态库与动态库

本文介绍了如何使用CMake构建静态库和动态库,包括HelloFunc函数的实现,以及如何设置输出名称、动态库版本,安装库和头文件的步骤。还讨论了如何在外部项目中链接和使用这些库,以及CMake的相关语法和注意事项。
摘要由CSDN通过智能技术生成

CMake 生成库

假设我们存在一个这样的任务:

  1. 建立一个静态库和动态库,提供 HelloFunc 函数以供其他程序编程使用,HelloFunc 向终端输出 Hello World 字符串。
  2. 安装头文件与共享库。

静态库和动态库的区别

  • 静态库的扩展名一般为“.a”或“.lib”;动态库的扩展名一般为“.so”或“.dll”。
  • 静态库在编译时会直接整合到目标程序中,编译成功的可执行文件可独立运行(如果程序编译成功,即使离开静态库,程序也是可以独立运行)。
  • 动态库在编译时不会放到连接的目标程序中,即可执行文件无法单独运行(如果程序编译成功,必须要有动态库的存在程序才可以运行,比如使用windows运行一些游戏程序时,会报缺少 .dll 文件的错误,导致程序无法正常运行,其实就是缺少动态库)。

CMake 生成库简单实例

按照惯例,我们先来一个简单地实例,以便对 CMake 生成库有一个直观的了解。

  1. 创建以下工程结构
yxm@192:~/test3$ tree
.
├── build
├── CMakeLists.txt
└── lib
    ├── CMakeLists.txt
    ├── hello.cpp
    └── hello.h

2 directories, 4 files

hello.h中的内容

#ifndef HELLO_H
#define Hello_H

void HelloFunc();

#endif

hello.cpp中的内容

#include <iostream>
#include "hello.h"

void HelloFunc(){
    std::cout << "Hello World" << std::endl;
}

项目中的cmake内容

PROJECT(HELLO)
ADD_SUBDIRECTORY(lib bin)

lib中CMakeLists.txt中的内容

SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
  1. ADD_LIBRARY 指令详细可见下文 CMake 语法。
  2. 外部编译过程:
    1. 进入 build,运行 cmake …
    2. 在 build 目录下,运行 make 命令编译 Makefile 文件,并生成动态库。

CMake 同时构建静态库与动态库

生成动态库与静态库

有上面的例子可以看出,使用 ADD_LIBRARY 指令就可以同时构建静态和动态库:

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})

但是如果使用这种方式,只会构建一个动态库,不会构建出静态库,虽然静态库的后缀是.a,此时我们可以修改静态库的名字,这样是可以同时构建动态库和静态库:

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})

但是我们往往希望他们的名字是相同的,只是后缀不同而已,此时可以使用 SET_TARGET_PROPERTIES 指令(该指令详细可见下文 CMake 语法),修改lib目录下CMakeLists.txt文件:

SET(LIBHELLO_SRC hello.cpp)

ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})

# 对hello_static的重名为hello
SET_TARGET_PROPERTIES(hello_static PROPERTIES  OUTPUT_NAME "hello")
# cmake 在构建一个新的target 时,会尝试清理掉其他使用这个名字的库,如果没有清理还是会只会构建一个动态库,不会构建出静态库
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
    
# 对hello_static的重名为hello
SET_TARGET_PROPERTIES(hello PROPERTIES  OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)

外部编译过程:

  1. 进入 build,运行 cmake …
  2. 在 build 目录下,运行 make 命令编译 Makefile 文件,并生成动态库与静态库。

修改动态库版本号

同时我们还可以修改动态库的版本号

// 一般动态库都有一个版本号的关联
libhello.so.1.2
libhello.so ->libhello.so.1
libhello.so.1->libhello.so.1.2

修改 lib/CMakeLists.txt ,重新构建看看结果:

SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello_static PROPERTIES  OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello PROPERTIES  OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)

安装共享库和头文件

本例中我们将 hello 的共享库安装到 <prefix>/lib 目录;将 hello.h 安装到 <prefix>/include/hello 目录,这样共享库才能够被调用。

修改lib目录下CMakeLists.txt文件:

SET(LIBHELLO_SRC hello.cpp)

ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})

SET_TARGET_PROPERTIES(hello_static PROPERTIES  OUTPUT_NAME "hello")

SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)


ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
    
SET_TARGET_PROPERTIES(hello PROPERTIES  OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
    
# 文件放到该目录下
INSTALL(FILES hello.h DESTINATION include/hello)

# 二进制,静态库,动态库安装都用TARGETS
# ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执行目标二进制。
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)

外部编译过程:

进入 build,运行 cmake -DCMAKE_INSTALL_PREFIX=/usr …

注意:安装的时候,指定一下路径,放到系统下,-D之后加不加空格都可。

注意:直接安装在 usr 系统目录下,以便后续可以直接调用

在 build 目录下,运行 make 命令编译 Makefile 文件,并生成动态库与静态库。

在 build 目录下,运行 make install

使用外部动态库和头文件

  1. 新建一个目录来使用外部共享库和头文件,创建以下工程结构:
yxm@192:~/test4$ tree
.
├── build
├── CMakeLists.txt
└── src
    ├── CMakeLists.txt
    └── main.cpp

2 directories, 3 files

main.cpp中的内容

#include <hello.h>

int main(){
	HelloFunc();
}

项目中的CMakeLists.txt内容

PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)

src中CMakeLists.txt中的内容

ADD_EXECUTABLE(hello main.cpp)

外部编译过程:

  1. 进入 build,运行 cmake …
  2. 在 build 目录下,运行 make 命令编译 Makefile 文件。注意此时 make 会报错:

解决:make 后头文件找不到的问题

make 时会提示找不到头文件,两种解决方法:

修改成 include <hello/hello.h> ,但这样修改代码冗余。

当然也可以使用一下关键字:INCLUDE_DIRECTORIES (详细见下文 CMake 语法)

这里使用第二种方法,在src下的 CMakeLists.txt 文件中加入头文件搜索路径:

INCLUDE_DIRECTORIES(/usr/include/hello)
ADD_EXECUTABLE(hello main.cpp) 

外部编译过程:

  1. 进入 build,运行 cmake …
  2. 在 build 目录下,运行 make 命令编译 Makefile 文件。注意此时还是 make 会报错:

解决:找到引用的函数问题

报错信息:undefined reference to `HelloFunc()',所以我们需要将 .so 文件关联起来。

解决方法有两种:

  • 关键字:LINK_DIRECTORIES 添加非标准的共享库搜索路径
    指定第三方库所在路径,LINK_DIRECTORIES(/home/myproject/libs)
  • 关键字:TARGET_LINK_LIBRARIES 添加需要链接的共享库(详细见下文 CMake 语法)

​ TARGET_LINK_LIBRARIES 的时候,只需要给出动态链接库的名字就行了。

这里使用第二种方法,在src下的 CMakeLists.txt 文件中添加需要链接的共享库(主要要插在executable的后面):

INCLUDE_DIRECTORIES(/usr/include/hello)
ADD_EXECUTABLE(hello main.cpp) 
TARGET_LINK_LIBRARIES(hello libhello.so)

外部编译过程:

进入 build,运行 cmake …

在 build 目录下,运行 make 命令编译 Makefile 文件。

在 build/bin 目录下,运行 ./hello ,执行结果如下:

yxm@192:~/test4/build$ cd bin/
yxm@192:~/test4/build/bin$ ./hello 
Hello World

注意:如果你的 linux 虚拟机是64位会报错,需要移动动态库到64位下:

使用外部静态库

上面的例子使用的是外部动态库,如果想要使用外部静态库,步骤也是相同的,只需要将上面例子中.so换成.a即可,不过使用外部静态库不需要头文件。

TARGET_LINK_LIBRARIES(main libhello.a)

补充:

特殊的环境变量 CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH

注意:这两个是环境变量而不是 cmake 变量,可以在linux的bash中进行设置

我们上面例子中使用了绝对路径INCLUDE_DIRECTORIES(/usr/include/hello)来指明include路径的位置,我们还可以使用另外一种方式,使用环境变量export CMAKE_INCLUDE_PATH=/usr/include/hello

CMake 语法

(1)ADD_LIBRARY 语法

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})

  • hello:就是正常的库名,生成的名字前面会加上lib,最终产生的文件是libhello.so
  • SHARED,动态库 STATIC,静态库
  • ${LIBHELLO_SRC} :源文件

(2)SET_TARGET_PROPERTIES 语法

这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和 API 版本

对hello_static的重名为hello,例如:SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME “hello”)

指定动态库版本和 API 版本,例如:SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)

其中VERSION 指代动态库版本,SOVERSION 指代 API 版本。

(3)INCLUDE_DIRECTORIES 语法

找头⽂件:可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割

(4)TARGET_LINK_LIBRARIES 语法

TARGET_LINK_LIBRARIES 用于从链接库到执⾏⽂件上

  • 28
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
CMake中,你可以使用add_library命令来设置生成动态库静态库。add_library命令用于创建一个新的库目标,并指定库的类型。 要生成动态库,可以将add_library命令的第一个参数设置为库的名称,然后将第二个参数设置为SHARED。例如: ```cmake add_library(mylib SHARED source1.cpp source2.cpp) ``` 上面的例子中,我们创建了一个名为mylib的动态库目标,并将源文件source1.cpp和source2.cpp添加到该目标中。生成动态库文件将具有与平台相关的扩展名(如.so、.dll或.dylib)。 要生成静态库,可以将add_library命令的第一个参数设置为库的名称,然后将第二个参数设置为STATIC。例如: ```cmake add_library(mylib STATIC source1.cpp source2.cpp) ``` 上面的例子中,我们创建了一个名为mylib的静态库目标,并将源文件source1.cpp和source2.cpp添加到该目标中。生成静态库文件将具有与平台相关的扩展名(如.a、.lib或.a)。 默认情况下,CMake将根据操作系统和构建配置(如Release或Debug)来决定使用动态库还是静态库。但你也可以使用set_target_properties命令来显式地指定目标生成的库类型。例如: ```cmake set_target_properties(mylib PROPERTIES LINKER_LANGUAGE CXX) ``` 上面的例子中,我们将目标mylib的链接器语言设置为C++,这将影响生成的库的类型。 总之,通过在add_library命令中使用SHARED或STATIC作为第二个参数,可以设置生成动态库静态库。你也可以使用set_target_properties命令来显式指定库的类型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FL1768317420

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值