CMake学习(5): 链接静(动)态库

通过add_library可以生成我们所需的动态库或者静态库,我们要把制作出的库文件发布出去供第三方使用。在发布的时候需要提供两种文件(1) 库文件(静态库/动态库) (2)头文件 (保存了库文件中函数、变量申明); 库文件对应的就是源文件,只不过我们对若干的源文件做了打包操作生成了一个二进制文件。

在编写程序的过程中,可能会用到一些系统提供的动态库或者自己制作出的动态库或者静态库文件,cmake 中也为我们提供了相关的加载动态库的命令。

1. 链接静态库

src
├── add.cpp
├── div.cpp
├── main.cpp
├── mult.cpp
└── sub.cpp

现在我们把上面 src 目录中的 add.cpp、div.cpp、mult.cpp、sub.cpp 编译成一个静态库文件 libcalc.a

1.1 编译静态库

CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${
    
    PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库/静态库生成路径
set(LIBRARY_OUTPUT_PATH ${
    
    PROJECT_SOURCE_DIR}/lib)
add_library(calc STATIC ${
    
    SRC_LIST})
cd build
cmake ..
make

编译完成后,就会在指定的lib目录下,生成静态库libcalc.a,目录的结构如下所示

$ tree 
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h
├── lib
│   └── libcalc.a     # 制作出的静态库的名字
└── src
    └──  add.cpp
    └──  div.cpp
    └──  mult.cpp
    └──  sub.cpp

1.2 链接静态库

在 cmake 中,链接静态库的命令如下:

link_libraries(<static lib> [<static lib>...])

link_libraries其中使用的是libraries,说明可以指定多个静态库。

  • 参数 1:指定出要链接的静态库的名字
    • 可以是全名 libxxx.a
    • 也可以是掐头(lib)去尾(.a)之后的名字 xxx=
  • 参数 2-N:要链接的其它静态库的名字

如果该静态库不是系统提供的(自己制作或者使用第三方提供的静态库),只提供要链接的静态库名,可能出现静态库找不到的情况,因为系统不知道这些库文件存放位置。此时可以将静态库的路径也指定出来,使用如下命令指定要链接的库路径

link_directories(<lib_path>)
  • 可以看出link_directories使用的是复数的directories说明可以指定多个链接的库路径。因为我们会同时链接多个库文件,这些库文件的会出现在不同的目录中,此时需要指定多个库目录。

这样,修改之后的 CMakeLists.txt 文件内容如下:

cmake_minimum_required(VERSION 3.0)
project(CALC)
# 搜索指定目录下源文件
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 链接静态库
link_libraries(calc)
add_executable(app ${SRC_LIST})
cd build
cmake ..
make

这样就编译生成了可执行文件app, 可执行文件链接了静态库calc,从而可以调用实现好的加减乘除运算

1.3 链接静态库

  • 在利用add_executable生成可执行文件时,一方面需要指定源文件SRC,同时由于源文件中还调用了库文件中的代码(库文件也是源代码),此时如果只指定源文件SRC是编译不通过的,需要将库文件的源代码也同时追加到app中。
  • 如何追加到app中去呢?此时就需要利用link_libraries链接静态库,链接了静态库之后,在生成可执行文件的时候,它除了加载${SRC}中的代码,它同时会加载link_libraries中指定的库文件中的源代码。
  • 注意: 如果在生成可执行文件时,链接的是静态库, 这个静态库文件会打包到可执行文件app中。如果链接的是动态库里面的数据就不会打包到可执行文件中。只有当应用程序(可执行文件)调用到动态库中的程序后,动态库才会被加载到内存中

2. 链接动态库

在程序编写过程中,除了在项目中引入静态库,好多时候也会使用一些标准的或者第三方提供的一些动态库,关于动态库的制作、使用以及在内存中的加载方式和静态库都是不同的,在此不再过多赘述,如有疑惑请参考

在 cmake 中链接动态库的命令如下

target_link_libraries(
    <target> 
    <PRIVATE|PUBLIC|INTERFACE> <item>... 
    [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)

target_link_libraries不仅可以链接动态库,静态库也可以用它来链接。

  • target:指定要加载动态库的文件的名字

    • 该文件可能是一个源文件
    • 该文件可能是一个动态库文件
    • 该文件可能是一个可执行文件
  • PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为 PUBLIC

    • 如果各个动态库之间没有依赖关系,无需做任何设置,三者没有没有区别,一般无需指定,使用默认的 PUBLIC 即可。

    • 动态库的链接具有传递性,如果动态库 A 链接了动态库 B、C,动态库 D 链接了动态库 A,此时动态库 D 相当于也链接了动态库 B、C,并可以使用动态库 B、C 中定义的方法。

target_link_libraries(A B C)
target_link_libraries(D A)
  • PUBLIC:在 public 后面的库会被 Link 到前面的 target 中,并且里面的符号也会被导出,提供给第三方使用。
  • PRIVATE:在 private 后面的库仅被 link 到前面的 target 中,并且终结掉,第三方不能感知你调了啥库
  • INTERFACE:在 interface 后面引入的库不会被链接到前面的 target 中,只会导出符号。

2.1 链接系统动态库

动态库的链接和静态库是完全不同的:

  • 静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。
  • 动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存

因此,在 cmake 中指定要链接的动态库的时候,应该将命令写到生成了可执行文件之后:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 添加并指定最终生成的可执行程序名
add_executable(app ${SRC_LIST})
# 指定可执行程序要链接的动态库名字
target_link_libraries(app pthread)

target_link_libraries(app pthread) 中:

  • app: 对应的是最终生成的可执行程序的名字
  • pthread:这是可执行程序要加载的动态库,这个库是系统提供的线程库,全名为 libpthread.so,在指定的时候一般会掐头(lib)去尾(.so)
  • 注意: 如果pthread不是系统中的库,或者环境变量中配置好的库,此时还需要通过link_directories来指定库的目录

案例——链接第三方动态库

现在,自己生成了一个动态库,对应的目录结构如下:

$ tree 
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h            # 动态库对应的头文件
├── lib
│   └── libcalc.so        # 自己制作的动态库文件
└── main.cpp              # 测试用的源文件

3 directories, 4 files

假设在测试文件 main.cpp 中既使用了自己制作的动态库libcalc.so又使用了系统提供的线程库,此时 CMakeLists.txt 文件可以这样写:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
include_directories(${PROJECT_SOURCE_DIR}/include)
add_executable(app ${SRC_LIST})
target_link_libraries(app pthread calc)

在第六行中,pthread、calc 都是可执行程序 app 要链接的动态库的名字。当可执行程序 app 生成之后并执行该文件,会提示有如下错误信息

$ ./app 
./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory

这是因为可执行程序启动之后,去加载 calc 这个动态库,但是不知道这个动态库被放到了什么位置,,所以就加载失败了,在 CMake 中可以在生成可执行程序之前,通过命令指定出要链接的动态库的位置,指定静态库位置使用的也是这个命令:

link_directories(path)

所以修改之后的 CMakeLists.txt 文件应该是这样的:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 指定源文件或者动态库对应的头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 指定要链接的动态库的路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 添加并生成一个可执行程序
add_executable(app ${SRC_LIST})
# 指定要链接的动态库
target_link_libraries(app pthread calc)

通过 link_directories 指定了动态库的路径之后,在执行生成的可执行程序的时候,就不会出现找不到动态库的问题了。

温馨提示:使用 target_link_libraries 命令就可以链接动态库,也可以链接静态库文件

猜你喜欢

转载自blog.csdn.net/weixin_38346042/article/details/131069948