cmake(3):编译库和链接可执行文件

2023-05-16

1. 说明

在实际开发的过程当中,我们会经常需要将部分程序编译成静态或动态库的形式,供其他应用程序调用而不是将所有文件一次编译为一个可执行文件。这篇笔记就记录使用cmake编译动态和静态库以及将库链接到可执行文件中的过程。

1.1 程序功能

总计三个文件:

  • utils.cpp/utils.h:定义了一个简单的printmsg()函数供主函数调用,该函数会将传入的字符串打印出来。
  • hello.cpp:主程序,调用printmsg()打印"Hello world"。

1.2 CMakeLists.txt说明

1. 在build路径下编译

由于cmake下没有诸如"make clean"之类的命令,无法一次清除所有的生成文件,那么在编译时产生的各种编译文件会造成管理的麻烦。所以这里采用out-of-source方式,创建一个build目录,在build目录中编译。

mkdir build
cd build
cmake ..
make

此时如果要清除上次编译的文件只要删除build目录或者在build目录下删除所有文件即可。

2. 生成静态/动态库,并与可执行文件链接

# shared lib
add_library(util_s SHARED utils.cpp)    # 生成动态库libutil_s.so
add_executable (test_s hello.cpp)       # 生成可执行文件test_s
target_link_libraries(test_s util_s)    # 链接可执行文件和动态库

# static lib
add_library(util_a STATIC utils.cpp)    # 生成静态库libutils_a.a
add_executable(test_a hello.cpp)        # 生成可执行文件test_a
target_link_libraries(test_a util_a)    # 链接可执行文件和动态库

库名称不需要写全,make时会自动根据当前系统补全前/后缀,如libutil_s.so/libutil_sa.s。

链接了静态库的可执行文件自不必说,它可以在任何路径下执行。但是对于链接动态库的test_s文件,在执行时载入动态库,根据strace的路径看到,虽然没有将当前路径添加到环境变量$LD_LIBRARY_PATH,但是它首先会从本地查找libutil_s.so文件,然后才会依次去环境变量的路径下查找。所以此时的test_s只能在编译出来的当前路径下或者libutil_s.so被拷贝到系统路径时执行。

3. 动态库版本控制

set_target_properties(util_s PROPERTIES VERSION ${PROJECT_VERSION})

使用该命令会在编译路径下生成一个libutil_s.so的软链接,对应后缀为版本号,比如我这里使用PROJECT_VERSION的变量为0.0.1,也是直接指定版本号。
结果如下:
在这里插入图片描述

4. 安装库文件和头文件

如果在编译时能够将编译出的库文件和头文件拷贝到系统对应的环境变量路径下,可以供其他开发人员方便地使用,提高软件开发的效率。这里可以使用cmake下的install命令来指定路径。

install(TARGETS util_a util_s
        LIBRARY DESTINATION lib
        ARCHIVE DESTINATION lib)

install(FILES utils.h DESTINATION include/utils)

编译时执行:

$ cmake -DCMAKE_INSTALL_PREFIX=/usr ..
$ make
$ sudo make install
[sudo] password for montage:
[ 25%] Built target util_s
[ 50%] Built target util_a
[ 75%] Built target test_s
[100%] Built target test_a
Install the project...
-- Install configuration: ""
-- Installing: /usr/lib/libutil_a.a
-- Installing: /usr/lib/libutil_s.so.0.0.1
-- Up-to-date: /usr/lib/libutil_s.so
-- Up-to-date: /usr/include/utils/utils.h

此时库文件会自动拷贝到指定的/usr/lib,头文件拷贝到/usr/include下:
在这里插入图片描述
在这里插入图片描述

2. 代码示例

2.1 源文件代码

utils.h

#ifndef __CMAKE_UTILS__
#define __CMAKE_UTILS__
#include <iostream>
#include <string>

void printmsg(std::string msg);

#endif

utils.cpp

#include "utils.h"

void printmsg(std::string msg)
{
    std::cout << msg << std::endl;
}

hello.cpp

#include <iostream>
#include <string>
#include "utils.h"

int main()
{
    std::cout << "Hello, this is my first cmake sample" << std::endl;
    printmsg("Hello world!");

    return 0;
}

2.2 CMakeLists.txt文件

cmake_minimum_required (VERSION 3.0.0)
project (cmake_test VERSION 0.0.1)

# shared lib
add_library(util_s SHARED utils.cpp)
set_target_properties(util_s PROPERTIES VERSION ${PROJECT_VERSION})

add_executable (test_s hello.cpp)
target_link_libraries(test_s util_s)

# static lib
add_library(util_a STATIC utils.cpp)

add_executable(test_a hello.cpp)
target_link_libraries(test_a util_a)

install(TARGETS util_a util_s
        LIBRARY DESTINATION lib
        ARCHIVE DESTINATION lib)

install(FILES utils.h DESTINATION include/utils)

2.3 运行结果

在这里插入图片描述

3. 命令解析

3.1add_library

使用指定的源文件将库添加到项目中。
add_library命令有多种命令格式.

用法1(Normal Libraries)

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [source1] [source2 ...])

使用source…指定的源文件来构建一个名为的库。默认情况下会在调用命令对应的目录中创建该库,可以通过ARCHIVE_OUTPUT_DIRECTORY, LIBRARY_OUTPUT_DIRECTORY, 和 RUNTIME_OUTPUT_DIRECTORY 属性来修改创建位置。

  • name:库名称,注意是库的逻辑名称,实际输出时会根据平台加上对应的前后缀,如Linux平台下libname.so。

  • STATIC/SHARED/MODULE:指定库的类型,如果没有显示指定,则根据变量BUILD_SHARED_LIBS来判断。

    • STATIC:静态库
    • SHATED:动态库
    • MODULE:不会链接到目标文件中,但可以使用dlopen之类的操作在运行时动态加载
  • EXCLUDE_FROM_ALL:用于排除不必构建的包,可选。

  • source1…:用于构建库的源文件

注:对于SHARED和MODULE,cmake会自动设置属性POSITION_INDEPENDENT_CODE 为ON。而SHEARED或STATIC可能会标记FRAMEWORK来构建macOS框架。
如果该库不导出任何symbols,那么它不能被声明为SHARED库。

用法2(Imported Libraries)


```powershell
add_library(<name> <SHARED|STATIC|MODULE|OBJECT|UNKNOWN> IMPORTED
            [GLOBAL])

从外部导入指定库。

  • name:库名称
  • SHARED|STATIC|MODULE|OBJECT|UNKNOWN:库类型,UNKNOWN库类型通常仅在查找模块的实现中使用。它允许使用导入的库的路径(通常使用find_library()命令找到),而不必知道它是什么类型的库。这在Windows中静态库和DLL的导入库都具有相同文件扩展名的Windows上尤其有用。
  • IMPORTED:固定参数,表示导入库
  • GLOBAL:扩展目标库的作用域,使得可以在项目的任意位置使用它。

注:对于从哪里导入库的问题,除了上述提到的find_library()命令外,更重要的是定义在属性IMPORTED_LOCATION和IMPORTED_OBJECTS 中指定的路径。

用法3(Object Libraries)

add_library(<name> OBJECT <src>...)

创建一个对象库。对象库编译源文件,但不将其目标文件归档或链接到库中。不同于其他类型的库,对象库可以在使用诸如add_library和add_executalbe命令创建其他目标文件的时候作为参数或源文件添加进去,使用方式如:

# objlib是对象库名称
add_library(... $<TARGET_OBJECTS:objlib> ...)
add_executable(... $<TARGET_OBJECTS:objlib> ...)

注:对象库可能只包含编译源,头文件和其他不会影响普通库链接的文件(例如.txt)。它们可能包含生成此类源的自定义命令,但不包含PRE_BUILD,PRE_LINK或POST_BUILD命令。

用法4(Alias Libraries)

add_library(<name> ALIAS <target>)

创建一个别名目标(别名目标是可以在只读上下文中与二进制目标名称互换使用的名称),以便可用于在后续命令中引用。 不会作为生成目标出现在生成的构建系统中。 可能不是非全局导入的目标或ALIAS。 ALIAS目标可用作链接目标以及从中读取属性的目标。也可以使用常规if(TARGET)子命令测试它们的存在。 name不能用于修改target的属性,也就是说,它不能用作set_property(),set_target_properties(),target_link_libraries()等的操作数。ALIAS目标可能无法安装或导出。

用法5(Interface Libraries)

add_library(<name> INTERFACE [IMPORTED [GLOBAL]])

创建一个接口库。尽管接口库可能具有设置的属性,并且可以安装,导出和导入,但它不会直接创建构建输出。通常,使用以下命令在接口目标上填充INTERFACE_ *属性:

  • set_property(),
  • target_link_libraries(INTERFACE),
  • target_link_options(INTERFACE),
  • target_include_directories(INTERFACE),
  • target_compile_options(INTERFACE),
  • target_compile_definitions(INTERFACE),
  • target_sources(INTERFACE)
    然后它就可以像其他库一样,用作 target_link_libraries() 的参数。

3.2 target_link_libraries

此命令有多种格式,这里只讲解最基本的格式。

用法

target_link_libraries(<target> ... <item>... ...)
  • target:要链接的目标文件,通常是可执行文件或库。
  • item:被链接的文件,有以下几种情况:
    • 库名称:链接命令将会自动包含库的完整路径及其依赖;
    • 包含完整路径的库:链接命令通常保留库的完整路径,如果库发生变化(如删除或移动位置),则需要重新链接以更新依赖
    • 普通的库名:指已经存在于系统内的第三方库或标准库。链接命令会要求链接器搜索该库(例如foo变为-lfoo或foo.lib)。
    • 链接标志:以-开头但不是-l或-framework的项目名称被视为链接器标志。请注意,出于传递依赖关系的目的,此类标志将与任何其他库链接项一样对待,因此通常可以安全地将它们指定为不会传播到依赖项的私有链接项。
    • 生成器表达式:如包含一系列变量或库的list,链接命令会依次链接列表中的库。
    • 调试:优化或常规关键字后紧跟另一个。关键字之后的项目将仅用于相应的构建配置。

作用
将名为item的库链接到目标target文件中。其中target必须是通过add_library()或者add_executable()命令生成的库或可执行文件,且不能为别名。在CMP0079策略没有被设置为NEW时,target必须已经存在于当前的目录下。如果对同一个target多次重复调用此命令,那么对应的item会根据调用顺序依次追加到文件中。

3.3 install

这个命令为项目创建安装规则,在安装过程中按顺序执行在源目录中对此命令的调用指定的规则。如在示例中将库和头文件安装到指定的目录中去。
这个命令同样有多种命令格式,这里只分析示例中用到的两种。

用法1(Installing Targets)

install(TARGETS targets... [EXPORT <export-name>]
        [[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
          PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]
         [DESTINATION <dir>]
         [PERMISSIONS permissions...]
         [CONFIGURATIONS [Debug|Release|...]]
         [COMPONENT <component>]
         [NAMELINK_COMPONENT <component>]
         [OPTIONAL] [EXCLUDE_FROM_ALL]
         [NAMELINK_ONLY|NAMELINK_SKIP]
        ] [...]
        [INCLUDES DESTINATION [<dir> ...]]
        )

指定目标文件的安装规则。

用法2(Installing Files)

install(<FILES|PROGRAMS> files...
        TYPE <type> | DESTINATION <dir>
        [PERMISSIONS permissions...]
        [CONFIGURATIONS [Debug|Release|...]]
        [COMPONENT <component>]
        [RENAME <name>] [OPTIONAL] [EXCLUDE_FROM_ALL])

指定文件的安装规则

3.4 set_target_properties

通常目标(比如可执行文件或库)具有一些属性值,这些属性会影响该目标的构建方式。set_target_properties()命令就用于设置目标对应的属性。

用法

set_target_properties(target1 target2 ...
                      PROPERTIES prop1 value1
                      prop2 value2 ...)

该命令的语法是列出要更改的所有目标,然后提供下一步要设置的值。您可以使用任何所需的prop值对,并稍后使用get_property()或get_target_property()命令将其提取。

  • target1…:目标名称
  • PROPERTIES:固定参数
  • prop:属性名称,如示例中的VERSION
  • value:要设置的属性值,如示例中的0.0.1
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

cmake(3):编译库和链接可执行文件 的相关文章

  • SLAM学习笔记(三) 后端优化与回环检测

    后端优化是对相机位姿和点位置的再度优化 xff08 相较于前端优化规模更大 xff09 xff0c 回环检测是对地图进行的一种修正 xff08 当机器人回到原来位置时 xff0c 地图也实现一个闭环 xff09 后端优化与回环检测 后端优化
  • 基于STM32 HAL库的自定义USB HID设备通信

    最近有个项目需要用到STM32的高速通信 xff0c 准备调试一下STM32的USB功能 xff0c 但是发现F103C8T6只有全速USB xff0c 因此作为HID设备一帧只能发送64字节 xff0c 最小间隔是1ms xff0c 显然
  • PX4-Autopilot安装

    Ubuntu 18 04 43 ROS Melodic 1 PX4源码下载 1 先mkdir一个文件夹 xff0c 在该文件夹下进行源码下载 xff08 因为在github下载很慢 xff0c 所以后续到PX4 Autopilot目录下再单
  • MAVROS(1)offboard模式(手动和roslaunch启动)

    官方教程 xff1a https docs px4 io master en ros mavros offboard html 1 编写功能包 参考 xff1a https blog csdn net weixin 44917390 art
  • VINS-Fusion安装

    1 安装Ceres Solver 官方教程 xff1a http ceres solver org installation html 参考教程 xff1a https blog csdn net qq 27251141 article d
  • APM-MAVROS连接

    1 运行mavros roslaunch mavros apm launch fcu url 61 34 dev ttyUSB0 921600 34 2 读取topic之前先运行以下命令 xff0c 修改飞控广播频率 rosservice
  • PX4 Avoidance(3DVFH*)

    Github地址 xff1a https github com PX4 PX4 Avoidance readme PX4用户指南 xff1a computer vision obstacle avoidance PX4 Avoidance下
  • FreeRTOS源码分析与应用开发04:消息队列

    目录 1 队列结构 2 创建队列 2 1 动态创建队列 2 1 1 xQueueCreate函数 2 1 2 xQueueGenericCreate函数 2 1 3 xQueueGenericReset函数 2 2 静态创建队列 2 2 1
  • APM-SITL Gazebo MAVROS 仿真

    1 配置APM SITL环境 1 下载安装Ardupilot 参考链接 xff1a 官方教程 注意 xff1a 文件install prereqs ubuntu sh路径在 ardupilot Tools environment insta
  • Ego-planner-swarm安装及报错解决

    项目地址 xff1a https github com ZJU FAST Lab ego planner swarmhttps github com ZJU FAST Lab ego planner swarm https github c
  • 单片机和嵌入式系统的区别

    单片机和嵌入式系统的区别 嵌入式和单片机并不是一对相对的概念 xff0c 嵌入式系统包括硬件和软件部分 xff0c 而单片机是单片微型计算机 Single Chip Microcomputer 的简称 xff0c 即微控制单元 Microc
  • 程序员的5个级别,你属于哪一个等级?

    码农和程序员虽说是调侃 xff0c 但是实质上还真的是不一样 还别说 xff0c 程序员还是有分等级的 比如有技术专家 xff0c 初级专员等 程序员的级别不同 xff0c 薪水也是有着天壤之别 免费领取Python学习资料可以加小编的微信
  • FutureTask的使用示例

    今天看书 xff0c 有关于 FutureTask 的介绍 xff0c 感觉还蛮有意思的 xff0c 可以用它来做一些比较花时间的事情 下面打个通俗的比方来说明一下它的用处 xff1a 比如 xff0c 早上一大早的去公交站台等公交 xff
  • 用脚本创建快捷方式

    64 echo off set shortCutPath 61 C Documents and Settings administrator 桌面 set shortCutName 61 报表 set StartPath 61 起始位置 s
  • activemq配置wss协议

    wss是加密协议 xff0c 必须配置https证书 span class token operator lt span sslContext span class token operator gt span span class tok
  • MATLAB 初学者 课堂笔记

    1 预定义变量 xff1a pi i j inf xff1a 无穷大 eps a xff1a a 与大于 a 的最小的浮点数之间的距离 xff0c 距离越小表示精度越高 默认a 61 1 2 矩阵运算 xff1a 左除 C 61 A B 6
  • 备赛笔记:Opencv学习:颜色识别

    OpenCV颜色识别一般要以下步骤 xff1a 1 颜色空间转换 xff0c 将BGR转换为HSV xff0c 用色调区分颜色 2 按照阈值滤出所识别的颜色 3 消除噪点 xff0c 平滑边界 3 提取连续域 xff0c 提取要识别的颜色
  • 树莓派buster安装ROS完整记录

    我敢说这一教程全网找不到相同的 我今天在安装ROS时查了各方教程 xff1a 官方Wiki xff0c Google xff0c CSDN都各查了不下10篇 xff0c 终于综合各教程内容花了大半天下好了 使用硬件及软件 xff1a 树莓派
  • Linux操作系统原理与应用06:系统调用

    目录 1 Linux中的各种接口 1 1 LSB标准 1 2 Linux API 1 2 1 概述 1 2 2 Linux内核系统调用接口 1 2 3 C标准库 1 3 Linux ABI 1 4 内核API 1 5 系统调用与各种接口的关
  • 将csdn博客转换成makedown形式的文件并保存

    span class token comment 作者 Rain span span class token keyword import span re span class token keyword import span parse

随机推荐

  • VIBE:3D人体姿态预测项目复现笔记

    VIBE是一个的3D人体姿态预测开源项目 xff0c 需要基于该项目作一些开发 xff0c 首先需要能够搭建和是的环境成功复现它 不过 xff0c 这个项目的复现的 xff0c 真的不是一星半点的艰难 1 系统选择 之前一直用的Window
  • geometry_msgs设计的几个消息类型,定义的数据类型详解

    Point 点 float64 x xff0c float64 y xff0c float64 z Point32 float32 x xff0c float32 y xff0c float32 z 一般使用Point xff0c 大规模点
  • 分享个好用的开源录屏工具 Captura

    百度 或 点击 Captura 8 0 Download 进入官网下载 安装后是 如果折起来是这样 xff1a 红色框框那个是折叠按钮 红色圆形按钮是 开始录制 结束录制 和其他的录制按钮样式 xff0c 都差不多 xff0c 自行探索吧
  • 使用 aptitude解决ubuntu下apt-get install g++依赖问题

    问题描述 xff1a ubuntu下运行C 43 43 程序 xff0c 给出了如下错误提示 程序 g 43 43 尚未安装 使用以下命令安装 xff1a sudo apt get install g 43 43 执行 得出如下错误 正在读
  • 学习笔记-Raspberry Pi Zero W-4:串口(UART)的配置和使用

    4 1 开启UART 据官方所言 xff08 https www raspberrypi org documentation configuration uart md xff09 xff1a 树莓派CPU内部有两个串口 xff0c 一个P
  • CAAnimation——基本动画,关键帧动画和贝塞尔路径

    概述 在做对于图层的动画效果时 xff0c 往往直接改变属性或者使用隐式动画是不能满足我们的需求的 xff0c 所以我们就用到了显式动画 xff0c CAAnimation 它可以管理重复动画 准确的控制时间和步调 xff0c 并且能设定图
  • IOS详解TableView——性能优化及手工绘制UITableViewCell

    提高表视图的性能 UITableView作为应用中最常用的视图 xff0c 它的性能优化问题几乎是经常提及 下面对在非网络访问情况下的表视图性能优化进行了主要的几点说明 xff1a 1 自定义类或XIB文件时 在系统提供的样式不能满足我们的
  • IOS详解TableView——实现九宫格效果

    根据需求九宫格的效果可以有很多种 九宫格效果应用比较广泛 xff0c 实现也多种多样 xff0c 比如选项抽屉效果 这里写了一个在UITableView上显示九宫格效果的Demo 思路 xff1a 在Cell上初始化自定义按钮 xff0c
  • IOS详解TableView——内置刷新,EGO,以及搜索显示控制器

    这几天因为住的地方的网出了一点问题 xff0c 除了能上Q xff0c 上微博以外其他的网页全都无法登陆 博客也就没有跟进 今天恢复了 xff0c 所以继续更新博客 也希望大家能继续评论或私自给我一些建议或者交流 今天找到了以前一个Tabl
  • Linux设备驱动基础01:Linux设备驱动概述

    目录 1 设备驱动的作用 2 有无操作系统时的设备驱动 2 1 无操作系统 2 1 1 硬件 驱动和应用程序的关系 2 1 2 单任务软件典型架构 2 2 有操作系统 2 2 1 硬件 驱动 操作系统和应用软件的关系 3 Linux设备分类
  • IOS回调机制——代理,通知中心以及Block

    Xcode5 0正式版 IOS7和Xcode5正式版在昨天正式可以下载 IOS7不多说了 xff0c 交互设计 xff0c 界面风格 xff0c 操作的简化程度都属于比较领先的水平 这里来说说Xcode5正式版 xff0c 和以前的Xcod
  • IOS飞机大战OC版

    前一阵子看到了很多版本的打飞机游戏 xff0c 有Java版的C 43 43 版本的还有C语言版的 这几天闲着的时候写了一个OC版的 xff0c 也正好是因为答应朋友写这个游戏来把飞机都换成他照片 没有用Cocos2d框架 xff0c 用的
  • Swift的可选链,类型转换和扩展

    可选链 Optional Chaining 可选链是一种请求或调用属性 xff0c 方法 xff0c 子脚本的过程 可选性体现于请求或调用的目标当前可能为nil 若不为nil则成功调用 xff0c 否则返回nil并将链失效 调用可选链的返回
  • iOS小米遥控器的手势监听及UI实现

    这篇文章通过实例实现了一个类似小米手势遥控器的功能页面 效果图如下所示 xff1a 触摸事件的响应通过对系统的触摸实践监听来进行 通过一个数组来对点的集合进行缓存和分析 void touchesBegan NSSet touches wit
  • 博客搬家至Github

    为了使用Markdown写作更方便一些 xff0c 以后将使用github pages来管理博客 地址 xff1a Rannie s Page 欢迎来访
  • C++使用http向服务器发送json数据

    span class token macro property span class token directive hash span span class token directive keyword include span spa
  • 如何使用Git将Github项目拉到本地

    如何使用Git将Github项目拉到本地 前言 因为国内访问GIthub速度比较慢 xff0c 复制粘贴代码又慢效率也低 xff0c 所以建议下载Git工具 xff0c 直接把Github的项目整个下载到本地的文件夹 安装配置git 步骤如
  • 笔记本 - 数据分析百宝箱

    Numpy 一 基本操作 xff1a 属性 xff1a improt numpy as np 生成数组 xff1a array 61 np array 1 2 3 2 3 4 xff0c dtype 61 np int float arra
  • Faiss(5):IndexIVFPQ原理

    说明 原本想尝试自己从头写 xff0c 但看了下网上的各位前辈的博客后 xff0c 感觉自己还是才疏学浅 xff0c 没有理解透彻 xff0c 所以在这里做个搬运工 xff0c 偶尔加些个人的理解在里面 原文链接 xff1a https b
  • cmake(3):编译库和链接可执行文件

    1 说明 在实际开发的过程当中 xff0c 我们会经常需要将部分程序编译成静态或动态库的形式 xff0c 供其他应用程序调用而不是将所有文件一次编译为一个可执行文件 这篇笔记就记录使用cmake编译动态和静态库以及将库链接到可执行文件中的过