Linux下CMake简明教程

2023-05-16

CMake是开源、跨平台的构建工具,可以让我们通过编写简单的配置文件去生成本地的Makefile,这个配置文件是独立于运行平台和编译器的,这样就不用亲自去编写Makefile了,而且配置文件可以直接拿到其它平台上使用,无需修改,非常方便。


本文主要讲述在Linux下如何使用CMake来编译我们的程序。


一 安装CMake

本文使用ubuntu18.04,安装cmake使用如下命令,

sudo apt install cmake

安装完成后,在终端下输入cmake -version查看cmake版本,


这样cmake就安装好了。


二 简单样例

首先让我们从最简单的代码入手,先来体验下cmake是如何操作的。编写main.c,如下,

#include <stdio.h>

int main(void)
{
    printf("Hello World\n");

    return 0;
}

然后在main.c相同目录下编写CMakeLists.txt,内容如下,

cmake_minimum_required (VERSION 2.8)

project (demo)

add_executable(main main.c)

第一行意思是表示cmake的最低版本要求是2.8,我们安装的是3.10.2;第二行是表示本工程信息,也就是工程名叫demo;第三行比较关键,表示最终要生成的elf文件的名字叫main,使用的源文件是main.c
在终端下切到main.c所在的目录下,然后输入以下命令运行cmake,
cmake .
会输出如下信息,



再来看看目录下的文件,



可以看到成功生成了Makefile,还有一些cmake运行时自动生成的文件。
然后在终端下输入make并回车,



可以看到执行cmake生成的Makefile可以显示进度,并带颜色。再看下目录下的文件,



可以看到我们需要的elf文件main也成功生成了,然后运行main,

运行成功!


三 同一目录下多个源文件

接下来进入稍微复杂的例子:在同一个目录下有多个源文件。
在之前的目录下添加2个文件,testFunc.c和testFunc.h。添加完后整体文件结构如下,

testFunc.c内容如下,

/*
** testFunc.c
*/

#include <stdio.h>
#include "testFunc.h"

void func(int data)
{
    printf("data is %d\n", data);
}

testFunc.h内容如下,

/*
** testFunc.h
*/

#ifndef _TEST_FUNC_H_
#define _TEST_FUNC_H_

void func(int data);

#endif

修改main.c,调用testFunc.h里声明的函数func(),

#include <stdio.h>

#include "testFunc.h"

int main(void)
{
    func(100);

    return 0;
}

修改CMakeLists.txt,在add_executable的参数里把testFunc.c加进来

cmake_minimum_required (VERSION 2.8)

project (demo)

add_executable(main main.c testFunc.c)

然后重新执行cmake生成Makefile并运行make,

然后运行重新生成的elf文件main,

运行成功!

可以类推,如果在同一目录下有多个源文件,那么只要在add_executable里把所有源文件都添加进去就可以了。但是如果有一百个源文件,再这样做就有点坑了,无法体现cmake的优越性,cmake提供了一个命令可以把指定目录下所有的源文件存储在一个变量中,这个命令就是 aux_source_directory(dir var)。
第一个参数dir是指定目录,第二个参数var是用于存放源文件列表的变量。

我们在main.c所在目录下再添加2个文件,testFunc1.c和testFunc1.h。添加完后整体文件结构如下,

testFunc1.c如下,

/*
** testFunc1.c
*/

#include <stdio.h>
#include "testFunc1.h"

void func1(int data)
{
    printf("data is %d\n", data);
}

testFunc1.h如下,

/*
** testFunc1.h
*/

#ifndef _TEST_FUNC1_H_
#define _TEST_FUNC1_H_

void func1(int data);

#endif

再修改main.c,调用testFunc1.h里声明的函数func1(),

#include <stdio.h>

#include "testFunc.h"
#include "testFunc1.h"

int main(void)
{
    func(100);
    func1(200);

    return 0;
}

修改CMakeLists.txt,

cmake_minimum_required (VERSION 2.8)

project (demo)

aux_source_directory(. SRC_LIST)

add_executable(main ${SRC_LIST})

使用aux_source_directory把当前目录下的源文件存列表存放到变量SRC_LIST里,然后在add_executable里调用SRC_LIST(注意调用变量时的写法)。
再次执行cmake和make,并运行main,

可以看到运行成功了。


四 不同目录下多个源文件

一般来说,当程序文件比较多时,我们会进行分类管理,把代码根据功能放在不同的目录下,这样方便查找。那么这种情况下如何编写CMakeLists.txt呢?
我们把之前的源文件整理一下(新建2个目录test_func和test_func1),整理好后整体文件结构如下,

把之前的testFunc.c和testFunc.h放到test_func目录下,testFunc1.c和testFunc1.h则放到test_func1目录下。

其中,CMakeLists.txt和main.c在同一目录下,内容修改成如下所示,

cmake_minimum_required (VERSION 2.8)

project (demo)

include_directories (test_func test_func1)

aux_source_directory (test_func SRC_LIST)
aux_source_directory (test_func1 SRC_LIST1)

add_executable (main main.c ${SRC_LIST} ${SRC_LIST1})

这里出现了一个新的命令:include_directories。该命令是用来向工程添加多个指定头文件的搜索路径,路径之间用空格分隔。
因为main.c里include了testFunc.h和testFunc1.h,如果没有这个命令来指定头文件所在位置,就会无法编译。当然,也可以在main.c里使用include来指定路径,如下

#include "test_func/testFunc.h"
#include "test_func1/testFunc1.h"

只是这种写法不好看。
另外,我们使用了2次aux_source_directory,因为源文件分布在2个目录下,所以添加2次。


五 正规一点的组织结构

正规一点来说,一般会把源文件放到src目录下,把头文件放入到include文件下,生成的对象文件放入到build目录下,最终输出的elf文件会放到bin目录下,这样整个结构更加清晰。让我们把前面的文件再次重新组织下,

我们在最外层目录下新建一个CMakeLists.txt,内容如下,

cmake_minimum_required (VERSION 2.8)

project (demo)

add_subdirectory (src)

这里出现一个新的命令add_subdirectory(),这个命令可以向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制的存放位置,具体用法可以百度。
这里指定src目录下存放了源文件,当执行cmake时,就会进入src目录下去找src目录下的CMakeLists.txt,所以在src目录下也建立一个CMakeLists.txt,内容如下,

aux_source_directory (. SRC_LIST)

include_directories (../include)

add_executable (main ${SRC_LIST})

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

这里又出现一个新的命令set,是用于定义变量的,EXECUTABLE_OUT_PATH和PROJECT_SOURCE_DIR是CMake自带的预定义变量,其意义如下,

  •     EXECUTABLE_OUTPUT_PATH :目标二进制可执行文件的存放位置
  •     PROJECT_SOURCE_DIR:工程的根目录

所以,这里set的意思是把存放elf文件的位置设置为工程根目录下的bin目录。(cmake有很多预定义变量,详细的可以网上搜索一下)

添加好以上这2个CMakeLists.txt后,整体文件结构如下,

下面来运行cmake,不过这次先让我们切到build目录下,然后输入以下命令,
cmake ..
Makefile会在build目录下生成,然后在build目录下运行make,

运行ok,我们再切到bin目录下,发现main已经生成,并运行测试,

测试OK!

这里解释一下为什么在build目录下运行cmake?从前面几个case中可以看到,如果不这样做,cmake运行时生成的附带文件就会跟源码文件混在一起,这样会对程序的目录结构造成污染,而在build目录下运行cmake,生成的附带文件就只会待在build目录下,如果我们不想要这些文件了就可以直接清空build目录,非常方便。

另外一种写法:
前面的工程使用了2个CMakeLists.txt,这种写法是为了处理需要生成多个elf文件的情况,最外层的CMakeLists.txt用于掌控全局,使用add_subdirectory来添加要生成elf文件的源码目录。

如果只生成一个elf文件,那么上面的例子可以只使用一个CMakeLists.txt,可以把最外层的CMakeLists.txt内容改成如下,

cmake_minimum_required (VERSION 2.8)

project (demo)

aux_source_directory (src SRC_LIST)

include_directories (include)

add_executable (main ${SRC_LIST})

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

同时,还要把src目录下的CMakeLists.txt删除。


六 动态库和静态库的编译控制

有时我们只需要编译出动态库,静态库,然后等着让其它程序去使用。让我们看下这种情况该如何使用cmake。首先按照如下重新组织文件,只留下testFunc.h和TestFunc.c,

我们会在build目录下运行cmake,并把生成的库文件存放到lib目录下。
最外层的CMakeLists.txt内容如下,

cmake_minimum_required (VERSION 2.8)

project (demo)

add_subdirectory (lib_testFunc)

lib_testFunc目录下的CMakeLists.txt如下,

aux_source_directory (. SRC_LIST)

add_library (testFunc_shared SHARED ${SRC_LIST})
add_library (testFunc_static STATIC ${SRC_LIST})

set_target_properties (testFunc_shared PROPERTIES OUTPUT_NAME "testFunc")
set_target_properties (testFunc_static PROPERTIES OUTPUT_NAME "testFunc")

set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

这里又出现了新的命令和预定义变量,

  •     add_library: 生成动态库或静态库(第1个参数指定库的名字;第2个参数决定是动态还是静态,如果没有就默认静态;第3个参数指定生成库的源文件)
  •     set_target_properties: 设置输出的名称,还有其它功能,如设置库的版本号等等
  •     LIBRARY_OUTPUT_PATH: 库文件的默认输出路径,这里设置为工程目录下的lib目录

好了,让我们进入build目录下运行cmake …,成功后再运行make,

cd到lib目录下进行查看,发现已经成功生成了动态库和静态库,

ps:可以看出前面使用set_target_properties重新定义了库的输出名字,如果不用set_target_properties也可以,那么库的名字就是add_library里定义的名字,只是我们连续2次使用add_library指定库名字时,这个名字不能相同,而set_target_properties可以把名字设置为相同,只是最终生成的库文件后缀不同,这样相对来说会好看点。

##七 对库进行链接
既然我们已经生成了库,那么就进行链接测试下。把build里的文件都删除,然后在在工程目录下新建src目录和bin目录,在src目录下添加一个main.c和一个CMakeLists.txt,整体结构如下,

main.c内容如下,

#include <stdio.h>

#include "testFunc.h"

int main(void)
{
    func(100);
    
    return 0;
}

修改工程目录下的CMakeLists.txt,如下,

cmake_minimum_required (VERSION 2.8)

project (demo)

add_subdirectory (lib_testFunc)

add_subdirectory (src)

只是使用add_subdirectory把src目录添加进来。
src目录下的CMakeLists.txt如下,

aux_source_directory (. SRC_LIST)

# find testFunc.h
include_directories (../lib_testFunc)

link_directories (${PROJECT_SOURCE_DIR}/lib)

add_executable (main ${SRC_LIST})

target_link_libraries (main testFunc)

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

这里出现2个新的命令,

    link_directories: 添加非标准的共享库搜索路径
    target_link_libraries: 把目标文件与库文件进行链接

cd到build目录下,然后运行cmake …,成功后再运行make,

make成功,进入到bin目录下查看,发现main已经生成,并运行,

运行成功!

ps:在lib目录下有testFunc的静态库和动态库,target_link_libraries (main testFunc)默认是使用动态库,如果lib目录下只有静态库,那么这种写法就会去链接静态库。也可以直接指定使用动态库还是静态库,写法是:target_link_libraries (main libtestFunc.so)target_link_libraries (main libtestFunc.a)

ps: 查看elf文件使用了哪些库,可以使用readelf -d ./xx来查看


七 总结

以上是自己学习CMake的一点学习记录,通过简单的例子让大家入门CMake,学习的同时也阅读了很多网友的博客。CMake的知识点还有很多,具体详情可以在网上搜索。总之,CMake可以让我们不用去编写复杂的Makefile,并且跨平台,是个非常强大并值得一学的工具。


参考:https://blog.csdn.net/whahu1989/article/details/82078563
 

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

Linux下CMake简明教程 的相关文章

  • 超长整型除法运算

    1017 A除以B 20分 本题要求计算 A B xff0c 其中 A 是不超过 1000 位的正整数 xff0c B 是 1 位正整数 你需要输出商数 Q 和余数 R xff0c 使得 A 61 B Q 43 R 成立 输入格式 xff1
  • 练习2-11 计算分段函数[2] (10分)

    本题目要求计算下列分段函数f x 的值 xff1a f2 11 注 xff1a 可在头文件中包含math h xff0c 并调用sqrt函数求平方根 xff0c 调用pow函数求幂 输入格式 输入在一行中给出实数x 输出格式 在一行中按 f
  • 习题4-2 求幂级数展开的部分和 (20分)

    已知函数e x可以展开为幂级数1 43 x 43 x 2 2 43 x 3 3 43 43 x k k 43 现给定一个实数x xff0c 要求利用此幂级数部分和求e x 的近似值 xff0c 求和一直继续到最后一项的绝对值小于0 0000
  • 练习7-10 查找指定字符 (15分)

    本题要求编写程序 xff0c 从给定字符串中查找某指定的字符 输入格式 xff1a 输入的第一行是一个待查找的字符 第二行是一个以回车结束的非空字符串 xff08 不超过80个字符 xff09 输出格式 xff1a 如果找到 xff0c 在
  • 习题9-5 通讯录排序 (20分)

    输入n个朋友的信息 xff0c 包括姓名 生日 电话号码 xff0c 本题要求编写程序 xff0c 按照年龄从大到小的顺序依次输出通讯录 题目保证所有人的生日均不相同 输入格式 输入第一行给出正整数n xff08 lt 10 xff09 随
  • C++提高运行速度

    ios base span class token punctuation span span class token punctuation span span class token function sync with stdio s
  • Java小项目(功能齐全)-停车场管理系统(中英文版)

    一 项目目的 xff1a 停车场管理系统 对停车场进行更系统的管理 xff0c 使整个过程更加高效有序 二 主要功能 xff1a 1 进入停车场 2 离开停车场 3 搜索信息 4 停车场当前车位明细查询 5 历史查询 三 附加功能 xff1
  • 应用YOLOV4 - DeepSort 实现目标跟踪

    转载自 https cloud tencent com developer article 1706259 本文分享利用yolov4 43 deepsort实现目标跟踪 xff0c 主要是讲解如何使用 xff0c 具体原理可以根据文中的参考
  • C++ char* 字符串处理、数组指针及传参

    编写函数 xff0c 将一个字符串 str 中指定的字符 ch 删去 xff08 包括重复出现的字符 xff09 xff0c 并编写主函 数进行调用测试 函数原型 void delchar char str char ch 输入 xff1a
  • STM32芯片写保护/解除写保护的方法

    一 写保护 1 目的 将Flash设置为写保护的目的 xff0c 是为了防止其他人通过J Link xff0c ULINK2等仿真器 xff0c 将Flash中的程序读取出来 设想一下 xff0c 你辛辛苦苦研发的产品 xff0c 别人通过
  • RG401 4G数传配置

    LP RG401为乐朴智能研发的一款4G无线数传模块 xff0c 支持接入移动 电信 联通运营商网络 xff0c 并且可以实现 3G 网络与 4G 网络之间的无缝切换 模块在运营商网络覆盖范围内可以实现点对点 一对多 多对多组网的不限距离数
  • 直流无刷电机与空心杯电机的区别

    浏览数 xff1a 132 日期 xff1a 2011 8 15 8 43 03 小 中 大 关闭注释 显示注释 直流无刷电机与空心杯电机的区别 空心杯电机CORELESS MOTOR xff0c 也叫无铁芯电机 xff0c 顾名思义 xf
  • 摘要认证

    1 摘要认证的改进 1 1 用摘要保护密码 摘要认证遵循 绝不通过网络发送密码 客户端发送一个 指纹 或者密码的 摘要 xff0c 是密码的不可逆扰码 1 2 单向摘要 z还要是对信息主体的浓缩 摘要是一个单向函数 xff0c 主要是将无线
  • 4路红外循迹模块使用教程

    4路红外循迹模块使用教程 文章目录 4路红外循迹模块使用教程模块详细信息 xff1a 模块接线模块使用相关代码 个人原创博客 xff1a 点击浏览 模块详细信息 xff1a 工作电压 xff1a DC 3 3V 5V 工作电流 xff1a
  • 循迹智能小车 循黑线 智能小车 红外循迹传感器 单片机

    循迹智能小车 循黑线 文章目录 循迹智能小车 循黑线硬件菜单硬件使用硬件组装程序设计前的理解程序代码 个人原创博客 xff1a 点我浏览 硬件菜单 单片机型号 xff1a STC16F40K128 4路红外循迹模块 小车底盘套件 xff08
  • 树莓派使用USB串口通信 CH340

    树莓派使用USB串口通信 CH340 个人博客原址 xff1a 树莓派使用USB串口通信 CH340 因为需要使用树莓派做自控方向的东西 xff0c 所以需要使用树莓派串口与各种外设进行通信 使用串口的话个人比较喜欢直接使用USB串口 xf
  • Python+OpenCV颜色识别 物体追踪

    Python 43 OpenCV颜色识别 物体追踪 对于颜色识别和imutils包的用法请浏览我得另一篇博客 xff1a OpenCV学习笔记 文章目录 Python 43 OpenCV颜色识别 物体追踪代码原理代码最终效果图 个人博客原址
  • OpenCV模板匹配识别图片中的数字

    OpenCV模板匹配识别图片中的数字 前言 本博客主要实现利用OpenCV的模板匹配识别图像中的数字 xff0c 然后把识别出来的数字输出到txt文件中 xff0c 如果识别失败则输出 读取失败 操作环境 xff1a OpenCV 4 1
  • Python + Skimage + OpenCV 使用技巧 实现连通区域染色

    Skimage库使用 前言 个人博客地址 最近发现Skimage库挺好用的 xff0c 可以和OpenCV搭配一起使用 xff0c 让图像处理更加灵活和方便 本博客只对Skimage库做简单的介绍 xff0c 细节使用的话推荐官网查看或百度
  • Atlas200DK环境配置

    Atlas200DK环境配置 个人博客网站 dd镜像安装 推荐使用dd镜像安装环境 xff0c 这种方式更快更方便 直接去网站下载需要版本的dd镜像 xff0c 然后利用Etcher烧录到sd卡中就行 参考链接 xff1a Atlas 20

随机推荐

  • Matlab搭建AlexNet实现手写数字识别

    Matlab搭建AlexNet实现手写数字识别 个人博客地址 文章目录 Matlab搭建AlexNet实现手写数字识别环境内容步骤准备MNIST数据集数据预处理定义网络模型定义训练超参数网络训练和预测 代码下载 环境 Matlab 2020
  • [DIY]自制一个有漂亮外观的90W 203H高频焊台 ---【up项目终于完结了】

    于2019年4月23日完成一体式多功能焊接工具的高频烙铁部分 xff1b https www bilibili com video av50217593 论坛帖子发于数码之家 https www mydigit cn forum php m
  • sklearn实现基于TF-IDF的KNN新闻标题文本分类

    sklearn实现基于TF IDF的KNN新闻标题文本分类 文章目录 sklearn实现基于TF IDF的KNN新闻标题文本分类数据集下载读取数据集中文分词去除停用词TF IDF算法提取文本特征KNN分类器的设计完整代码下载 数据集下载 点
  • java序列化

    引言 将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java 系列技术中一个较为重要的技术点 xff0c 在大部分情况下 xff0c 开发人员只需要了解被序列化的类需要实现 Serializable 接口 xff0c 使用
  • stm32串口中断收发数据环形缓冲区的设计

    Function Name USART2 IRQHandler Description This function handles USART2 global interrupt request Input None Output None
  • 图解快速区别——串口、COM口、UART、TTL、RS-232、RS-485

    参考 xff1a 串口 COM口 UART口 TTL RS 232 RS 485区别详解 作者 xff1a flyingju 发布时间 xff1a 2017 09 16 10 30 31 网址 xff1a https blog csdn n
  • P900数传端口定义

    P900数传端口引脚
  • P900数传配置教程

    typec线连接后按住SB2不松手后按住SB1 xff0c 分别松开SB2 SB1 xff0c 点击XCTU软件 http www pc6 com softview SoftView 602005 html 大写的 AT amp F7 设置
  • ubuntu查看软件安装路径

    ubuntu怎么查看软件安装位置在哪 服务器 亿速云 1 执行程序查看 在终端使用type执行软件程序查看 type google chrome 2 通过进程查看对应的软件程序 在终端使用以下命令查看所有进程名 ps e 再使用以下过滤命令
  • Ubuntu中wine程序安装windows软件中文乱码问题

    Ubuntu中wine程序安装windows软件中文乱码如何解决 1 安装wine sudo apt install wine 2 安装中文程序方法 下载exe文件在命令行执行 wine 文件名 exe 3 中文乱码原因分析 查看 home
  • ubuntu通过Trickle,wondershaper限制网速(上传下载速度)

    原文连接 xff1a https www ngui cc 51cto show 727932 html action 61 onClick 在Linux下没有Windows使用360那样去限制某个软件的速度 但是通过Trickle可以设置某
  • Ubuntu 16.04 重置密码(忘记密码)

    Ubuntu 16 04 重置密码 xff08 忘记密码 xff09 http blog topspeedsnail com archives 6042 忘记了你的Ubuntu用户密码 xff0c 登录不了系统 xff1b 不要紧 xff0
  • ubuntu下socket通信

    第一部分为C 43 43 实现 xff0c 第二部分为python实现 第一部分 该socket 的功能是在客户端输入了两个浮点数组成的字符串 xff0c 在服务端接受后将其转换为浮点数输出 xff0c 统一时刻只能有一个客户端连接 服务端
  • 关于螺旋桨的计算

    关于螺旋桨的计算 转载 http blog sina com cn s blog 4c2264b80100086z html 发给大家一些关与螺旋桨的计算公式 2007 02 07 13 08 46 功率 xff08 W xff09 直径
  • PID参数整定快速入门(调节器参数整定方法)

    转载地址 http yunrun com cn tech 440 html PID 参数整定方法很多 xff0c 常见的工程整定方法有临界比例度法 衰减曲线法和经验法 云南昌晖仪表制造有限公司以图文形式介绍以临界比例度法和衰减曲线法整定调节
  • STM8 CAN总线的IdMask模式的讲解

    http www stmcu org article id 328212 STM8 CAN总线的IdMask模式的讲解 发布时间 xff1a 2016 08 27 来源 xff1a ST社区 标签 xff1a STM8 CAN总线 IdMa
  • gitlab使用http方式提交代码不输入密码

    gitlab使用http方式提交代码不输入密码 记得这个问题很久之前就折腾过 xff0c 今天从gitlab上克隆一个仓库 xff0c 第一时间肯定是想着使用ssh公私钥进行验证 xff0c 这样以后也不需要再输入密码 xff0c 可是公司
  • STM32运行时程序卡在B.处

    STM32运行时程序卡在B 处的问题处理的一天多 xff0c 终于找到原因 1 xff0c 表现 我所使用的芯片是stm32f427vit6 xff0c stm32f4系列芯片外设多数都相同 xff0c 407 xff0c 405的 用户也
  • Keil 查看文件路径的方法

    目录 方法一 xff1a 方法二 xff1a 方法一 xff1a 在工程种选择任意一个文件 xff0c 然后点击右键 xff0c 选择 34 Option for File 34 就可以看到这个文件的路径了 方法二 xff1a 在文件框种右
  • Linux下CMake简明教程

    CMake是开源 跨平台的构建工具 xff0c 可以让我们通过编写简单的配置文件去生成本地的Makefile xff0c 这个配置文件是独立于运行平台和编译器的 xff0c 这样就不用亲自去编写Makefile了 xff0c 而且配置文件可