CMake 常用总结二:CMake 生成静态库与动态库

2023-05-16

引言

CMake 实践帮助我们对 CMake 有一个系统全面的了解,并且有大量示例以供参考,至少在实际项目中可以让我们有能力看懂并修改项目中现有的 CMake 。

阅读完 CMake 实践文档,认为自己的任务也就结束了,可这样总感觉不是自己的东西,不如整理一下并吸收其中自己认为最有用的东西,这样也能极大的减轻自己的记忆负担。

与此同时 CMake 实践行文组织过于复杂,不方便遇到问题时快速查阅,所以我做了一些调整与总结,希望能够对读者更加友好。

本文不能代替 CMake 实践文档,有时间还是把 CMake 实践文档认真阅读一遍。

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
    
  2. hello.h中的内容

    #ifndef HELLO_H
    #define Hello_H
    
    void HelloFunc();
    
    #endif
    
  3. hello.cpp中的内容

    #include <iostream>
    #include "hello.h"
    
    void HelloFunc(){
        std::cout << "Hello World" << std::endl;
    }
    
  4. 项目中的cmake内容

    PROJECT(HELLO)
    ADD_SUBDIRECTORY(lib bin)
    
  5. lib中CMakeLists.txt中的内容

    SET(LIBHELLO_SRC hello.cpp)
    ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
    

    ADD_LIBRARY 指令详细可见下文 CMake 语法。

  6. 外部编译过程:

    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)

外部编译过程:

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

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

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

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

  3. 在 build 目录下,运行 make install

    在这里插入图片描述

使用外部动态库和头文件

  1. 新建一个目录来使用外部共享库和头文件,创建以下工程结构:

    yxm@192:~/test4$ tree
    .
    ├── build
    ├── CMakeLists.txt
    └── src
        ├── CMakeLists.txt
        └── main.cpp
    
    2 directories, 3 files
    
  2. main.cpp中的内容

    #include <hello.h>
    
    int main(){
    	HelloFunc();
    }
    
  3. 项目中的CMakeLists.txt内容

    PROJECT(HELLO)
    ADD_SUBDIRECTORY(src bin)
    
  4. src中CMakeLists.txt中的内容

    ADD_EXECUTABLE(hello main.cpp)
    
  5. 外部编译过程:

    1. 进入 build,运行 cmake …

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

      在这里插入图片描述

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

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

  1. 修改成 include <hello/hello.h> ,但这样修改代码冗余。
  2. 当然也可以使用一下关键字: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)

外部编译过程:

  1. 进入 build,运行 cmake …

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

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

    yxm@192:~/test4/build$ cd bin/
    yxm@192:~/test4/build/bin$ ./hello 
    Hello World
    
  4. 注意:如果你的 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 用于从链接库到执⾏⽂件上

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CMake 常用总结二:CMake 生成静态库与动态库 的相关文章

  • git clone 出现fatal: unable to access ‘https://github 类错误解决方法

    git clone 遇到问题 xff1a fatal unable to access https github comxxxxxxxxxxx Failed to connect to xxxxxxxxxxxxx 问题 将命令行里的http
  • 进程通信的方式

    进程通信 1 进程通信的概念 进程是一个独立的资源分配单元 xff0c 不同进程 xff08 主要是指不同的用户进程 xff09 之间的资源是独立的 xff0c 没有关联的 xff0c 不能在一个进程中直接访问另一个进程的资源 但是 xff
  • 网络通信的过程

    网络通信的过程 封装 上层协议时如何使用下层协议提供的服务的呢 xff1f 其实这是通过封装实现的 应用程序是在发送到物理网络上之前 xff0c 将沿着协议栈从上往下依次传递 每层协议都将在上层数据的基础上加上自己的头部信息 xff08 有
  • TCP三次握手、四次挥手

    TCP通信流程 TCP和UDP TCP和UDP区别如下 xff1a UDP xff1a 用户数据报文协议 xff0c 面向无连接 xff0c 可以单播 xff0c 多播 xff0c 广播 xff0c 面向数据报 xff0c 不可靠 TCP
  • Qt的多线程编程

    Qt线程 基本概念 并发 当有多个线程在操作时 xff0c 如果系统只有一个CPU xff0c 则它根本不可能真正同时进行一个以上的线程 xff0c 它只能把CPU运行时间划分成若干个时间段 xff0c 再将时间段分配给各个线程执行 xff
  • CMake编译C++文件

    这篇文章介绍如何使用cmake工具编译一个最简单的helloworld cpp文件 首先创建一个空的文件夹 mkdir cmake test 在该文件夹下 xff0c 我们新建一个helloworld cpp文件 span class to
  • 智能小车建图导航-在rviz中导航(运行)

    笔记来源 xff1a 机器人开发与实践 xff08 古月 xff09 或者直接运行这个脚本文件 xff1a xff08 如果你没有在 bracsh文件中加入source xff0c 建议加入或者在脚本文件的上面中添加source xff0c
  • 004-S500无人机-相关的器件参数以及计算

    这篇博客主要是记录S500无人机的相关器件的参数 xff0c 参数的来源来源于holybro官网 xff1a https shop holybro com 我这里进行参数的归纳以及计算 一 电机 xff08 2216 880kv xff09
  • TX2 学习记录(开启板载/USB摄像头)

    刚拿到手一个TX2 xff0c 简单地学习一下这块板子 xff0c 因为是学长留下来的板子 xff0c 所以刷机的步骤我就省略了 xff0c 各位小伙伴可以参考其他大佬的博客进行刷机 xff0c 再来就记录一下一些操作指令吧 打开USB摄像
  • ubuntu16.04中进行ROS通信编程

    ROS通信学习 基础知识学习字段ROS通信小例子一 创建一个工作区二 创建一个ROS工程包三 创建通信的发 收节点四 测试程序的正确性 图像ROS通信小例子视频ROS通信小例子多机ROS通信 基础知识学习 x1f31f 话题与服务的区别 话
  • 2021电赛F题智能送药小车方案分析(openMV数字识别,红线循迹,STM32HAL库freeRTOS,串级PID快速学习,小车自动返回)

    2021全国大学生电子设计竞赛F题智能送药小车 前提 xff1a 本篇文章重在分享自己的心得与感悟 xff0c 我们把最重要的部分 xff0c 摄像头循迹 xff0c 摄像头数字识别问题都解决了 xff0c 有两种方案一种是openARTm
  • CARLA常见错误解决方案以及常见的问题解决方案

    记录Linux环境 Windows环境下常见的运行自动驾驶仿真器CARLA出现的错误 问题1 问题1比较基础 xff0c 创建虚拟环境以及删除虚拟环境 conda create span class token operator span
  • cmd找不到conda以及通过cmd启用Anaconda中的Python环境(base)

    问题 xff1a 在cmd中输入python无法进入或启用python ipython conda jupyter notebook 一 解决方法 xff1a 在系统环境中添加Anaconda路径 lt 1 gt 1 打开高级系统设置 xf
  • c语言实现strcat函数

    char strcat char strDestination const char strSource 一 函数介绍 作用 xff1a 连接字符串的函数 xff0c 函数返回指针 xff0c 两个参数都是指针 xff0c 第一个参数所指向
  • C/C++的static关键字作用(转载)

    一 限制符号的作用域只在本程序文件 若变量或函数 xff08 统称符号 xff09 使用static修饰 xff0c 则只能在本程序文件内使用 xff0c 其他程序文件不能调用 xff08 非static的可以通过extern 关键字声明该
  • crc校验

    参考链接 xff1a https www cnblogs com esestt archive 2007 08 09 848856 html 一 CRC校验原理 1 CRC校验全称为循环冗余校验 xff08 Cyclic Redundanc
  • ubuntu安装eclipse教程

    在安装eclipse之前 xff0c 要先安装JDK xff0c 一 安装JDK 1 从官网上下载JDK 链接 xff1a https www oracle com java technologies downloads 选择的jdk文件一
  • UDP通信入门篇

    UDP通信属于网络通信中的一种方式 xff0c 需要用套接字来进行通信 初接触UDP通信时 xff0c 不知道需要链接静态库 pragma comment lib ws2 32 lib xff0c 导致自己在前期浪费了很多时间去排查问题 除
  • window11配置深度学习环境

    Anaconda 43 PyCharm 43 CUDA 43 CUDNN 43 PyTorch 1 Anaconda安装 下载路径 xff1a https www anaconda com 安装方式 xff1a 以管理员身份安装 中间选项
  • python配置opencv环境后,读取图片,报错:can‘t open/read file: check file path/integrity

    运行出错代码 xff1a import cv2 import numpy as np image 61 cv2 imread 39 C Pictures 桌面背景图片切换 wallhaven 6oq1k7 jpg 39 cv2 IMREAD

随机推荐

  • 断言

    代码中放置一些假设 xff0c 通过判断假设是否为真 xff0c 进而判断程序是否正确 断言就是用来测试程序中的假设是否正确的 xff0c 若果假设被违反 xff0c 那么就中断程序的执行 断言assert是定义在assert h中的 宏
  • STM32输出SPWM波,HAL库,cubeMX配置,滤波后输出1KHz正弦波

    SPWM波 对于功率方向 输出SPWM波是必须要掌握的 工程 stm32生成spwm代码Keil工程链接资源 引用spwm波定义 PWM波形就是指占空比可变的波形 SPWM波形是指脉冲宽度按正弦规律变化且和正弦波等效的PWM波形 两者的区别
  • C语言链表写法,练习链表

    C语言链表写法 xff0c 练习链表 建立要一个文件 xff1a LinkList h 内容 xff1a span class token macro property span class token directive keyword
  • 树莓派摄像头 C++ OpenCV YoloV3 实现实时目标检测

    树莓派摄像头 C 43 43 OpenCV YoloV3 实现实时目标检测 本文将实现树莓派摄像头 C 43 43 OpenCV YoloV3 实现实时目标检测 xff0c 我们会先实现树莓派对视频文件的逐帧检测来验证算法流程 xff0c
  • RTK定位原理

    一 卫星测距原理说明 天上的卫星发送数据被便携式RTK终端接收到 xff0c 卫星和终端之间的距离D 61 C T C为光速 xff0c T为卫星发送的信号到达便携式RTK终端的时间 xff0c 通过时间乘以距离可以获得卫星和便携式终端的实
  • 网络协议与网络编程(单电脑信息传输)

    C C 43 43 网络编程 单电脑信息传输 Copyright Jingmin Wei Pattern Recognition and Intelligent System School of Artificial and Intelli
  • Pytorch:全连接神经网络-MLP回归

    Pytorch 全连接神经网络 解决 Boston 房价回归问题 Copyright Jingmin Wei Pattern Recognition and Intelligent System School of Artificial a
  • Pytorch:卷积神经网络-空洞卷积

    Pytorch 空洞卷积神经网络 Copyright Jingmin Wei Pattern Recognition and Intelligent System School of Artificial and Intelligence
  • Pytorch:目标检测网络-人体关键点检测

    Pytorch 目标检测网络 人体关键点检测 Copyright Jingmin Wei Pattern Recognition and Intelligent System School of Artificial and Intelli
  • Pytorch:图像风格快速迁移

    Pytorch 图像风格快速迁移 残差网络 xff0c 固定风格任意内容 Copyright Jingmin Wei Pattern Recognition and Intelligent System School of Artifici
  • 网络爬虫:Beautiful Soup库&&信息组织与提取

    爬虫 xff1a Beautiful Soup库 amp amp 信息组织与提取 Copyright Jingmin Wei Pattern Recognition and Intelligent System School of Arti
  • 网络爬虫:中国大学排名定向爬虫

    中国大学排名定向爬虫 已更新2021版本 网络爬虫专栏链接 Copyright Jingmin Wei Pattern Recognition and Intelligent System School of Artificial and
  • python互联网程序设计GUI程序设计和网络程序设计(人机互动聊天软件)

    1 项目意义 1 了解网络的结构 2 了解网络传输协议 3 掌握基本的网络编程方法 2 总体设计 使用 TCP 协议实现人机聊天互动 程序具有服务端和客户端 1 必备功能 要求服务端代码具有一定的智能 能够根据不完整的问题识别客户端真正 要
  • Pytorch:图卷积神经网络-半监督学习实现,对比SVM, LP

    Pytorch 图卷积神经网络 半监督网路实践 对比SVM LP Copyright Jingmin Wei Pattern Recognition and Intelligent System School of Artificial a
  • Pytorch:图像语义分割-FCN, U-Net, SegNet, 预训练网络

    Pytorch 图像语义分割 FCN U Net SegNet 预训练网络 Copyright Jingmin Wei Pattern Recognition and Intelligent System School of Artific
  • Pytorch:图像语义分割-基于VGG19的FCN8s实现

    Pytorch 图像语义分割 基于VGG19的FCN8s语义分割网络实现 Copyright Jingmin Wei Pattern Recognition and Intelligent System School of Artifici
  • ROS机器人自主定位导航

    ROS机器人定位导航仿真 智能车 目录 ROS机器人定位导航仿真 智能车 一 创建ROS工作空间1 创建racecar ws文件夹 src文件夹2 初始化工作空间3 下载racecar源代码包4 编译工程 二 仿真1 设置环境变量2 启动赛
  • 【无标题】

    ZED Pro1 xff1a ZED深度相机进行点云可视化和保存 96 提示 xff1a 我在这里想得到深度相机获取的点云数据并且进行可视化 xff0c 虽然光放以及给出一部分代码 xff0c 但是并不能支撑我完成所有工作 xff0c 所以
  • linux系统下使用gcc编译C++程序出现XXX未定义的引用的处理

    在使用gcc编译程序的时候 xff0c 出现了XXX未定义的引用的错误 xff0c 如图 错误原因分析 xff1a 首先 xff0c 我创建了类System xff0c 但是在类System中只有构造函数析构函数以及其他成员函数的定义 xf
  • CMake 常用总结二:CMake 生成静态库与动态库

    引言 CMake 实践帮助我们对 CMake 有一个系统全面的了解 xff0c 并且有大量示例以供参考 xff0c 至少在实际项目中可以让我们有能力看懂并修改项目中现有的 CMake 阅读完 CMake 实践文档 xff0c 认为自己的任务