CMake 学习四:CMake 构建静态库和动态库

Posted myw31415926

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CMake 学习四:CMake 构建静态库和动态库相关的知识,希望对你有一定的参考价值。

文章目录

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 中的内容

#ifndef ADD_H_
#define ADD_H_

int AddFunc(int m, int n);

#endif  // ADD_H_

源文件 lib/add.cpp 中的内容

#include "add.h"

int AddFunc(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 31 17:49 libadd.a
lrwxrwxrwx. 1 mayw mayw   11 Dec 31 17:50 libadd.so -> libadd.so.1
lrwxrwxrwx. 1 mayw mayw   13 Dec 31 17:50 libadd.so.1 -> libadd.so.1.2
-rwxrwxr-x. 1 mayw mayw 7896 Dec 31 17: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
$ make install
[ 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"

int main(int argc, char* argv[])

    std::cout << "hello world" << std::endl;
    std::cout << "AddFunc(3, 4)=" << AddFunc(3, 4) << std::endl;

    return 0;

根目录中 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 中进行设置。

使用 cmake 如何静态链接一些库和动态链接其他库?

【中文标题】使用 cmake 如何静态链接一些库和动态链接其他库?【英文标题】:with cmake how to link some libs statically and others dynamically? 【发布时间】:2013-06-18 16:52:02 【问题描述】:

如何告诉 CMake 静态链接一些库和动态链接其他库?

我想编译一个静态链接到所有依赖库的 C++ exe,除了 glic

谢谢

【问题讨论】:

【参考方案1】:

库的 CMake 方法是首先使用 find_library 找到它们,然后使用 target_link_libraries 中的结果。

find_library 调用期间选择使用静态库还是动态库:

如果您不介意使用哪个版本,请致电find_library(MYLIB mylib)

如果你想要一个静态库,使用find_library(MYLIB libmylib.a)(这是针对linux的,你会在windows等上搜索.lib)

如果你想要动态库,请使用find_library(MYLIB libmylib.so)

然后测试是否使用if (MYLIB) 找到您的库并将其链接到您的目标:target_link_libraries(mytarget $MYLIB)

【讨论】:

以上是关于CMake 学习四:CMake 构建静态库和动态库的主要内容,如果未能解决你的问题,请参考以下文章

使用 cmake 如何静态链接一些库和动态链接其他库?

CMake--静态库与动态库构建

CMake同时生成静态库和动态库

CMake入门学习

CMake入门学习

linux下CMake的简单介绍