CMake学习(1): CMake基本使用

https://subingwen.cn/cmake/CMake-primer/

1. CMake 概述

CMake是一个项目构建工具,并且是跨平台的。Cmake跟Makefile其实是差不多的,只不过makefile更底层些。大多是 IDE 软件都集成了 make,比如:VS 的 nmake、linux 下的 GNU make、Qt 的 qmake 等,如果自己动手写 makefile,会发现,makefile 通常依赖于当前的编译平台,而且编写 makefile 的工作量比较大,解决依赖关系时也容易出错。

而 CMake 恰好能解决上述问题, 其允许开发者指定整个工程的编译流程,在根据编译平台,自动生成本地化的Makefile和工程文件,最后用户只需 make 编译即可,所以可以把 CMake 看成一款自动生成 Makefile 的工具,其编译流程如下图:

在这里插入图片描述

  • 蓝色虚线表示使用makefile构建项目的过程
  • 红色实线表示使用 cmake 构建项目的过程

首先需要创建一个脚本文件CMakeLists.txt文件,在该文件中指定一些指令,然后执行cmake命令,执行cmake后会生成一个Makefile文件,此时makefile文件中会存在一些编译指令,最后再执行make命令就可以执行makefile中的一系列指令生成可执行文件(编译过程:先调用预处理器,再调用汇编器,再调用连接器,最后生成可执行文件)。

cmake不仅仅可以以生成可执行文件,还可以生成库文件,包括:动态库和静态库。生成了这些库文件就可以引入到另外的第三方项目中,为什么给第三方项目使用的是动态库或者静态库,而不是使用源代码呢?原因主要有两点: 1.为了对源代码的保密 2. 比如一个库是由100个源文件编译生成,如果直接100个源文件引入到第三方项目中是非常麻烦和臃肿的,并且不容易维护。为了便利和可维护,将这100个源文件编译为库文件,供第三方项目调用。

介绍完 CMake 的作用之后,再来总结一下它的优点

  • 跨平台
  • 能够管理大型项目
  • 简化编译构建过程和编译过程
  • 可扩展:可以为 cmake 编写特定功能的模块,扩充 cmake 功能

2. CMake 的使用

CMake 支持大写、小写、混合大小写的命令。如果在编写 CMakeLists.txt 文件时使用的工具有对应的命令提示,那么大小写随缘即可,不要太过在意。

2.1 注释

2.1.1 注释行

CMake 使用 # 进行行注释,可以放在任何位置。

# 这是一个 CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.0.0)

2.1.2 注释块

CMake 使用#[[ ]]形式进行块注释。

#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)

2.2 只有源文件

2.2.1 共处一室

(1) 准备工作,为了方便测试,在我本地电脑准备了这么几个测试文件

  • add.c
#include <stdio.h>
#include "head.h"

int add(int a, int b)
{
    return a+b;
}
  • sub.c
#include <stdio.h>
#include "head.h"

// 你好
int subtract(int a, int b)
{
    return a-b;
}
  • mult.c
#include <stdio.h>
#include "head.h"

int multiply(int a, int b)
{
    return a*b;
}
  • div.c
#include <stdio.h>
#include "head.h"

double divide(int a, int b)
{
    return (double)a/b;
}
  • head.h
#ifndef _HEAD_H
#define _HEAD_H
// 加法
int add(int a, int b);
// 减法
int subtract(int a, int b);
// 乘法
int multiply(int a, int b);
// 除法
double divide(int a, int b);
#endif

  • main.c
#include <stdio.h>
#include "head.h"

int main()
{
    int a = 20;
    int b = 12;
    printf("a = %d, b = %d\n", a, b);
    printf("a + b = %d\n", add(a, b));
    printf("a - b = %d\n", subtract(a, b));
    printf("a * b = %d\n", multiply(a, b));
    printf("a / b = %f\n", divide(a, b));
    return 0;
}

(2) 上述文件的目录结构如下:

$ tree
.
├── add.c
├── div.c
├── head.h
├── main.c
├── mult.c
└── sub.c

(3)添加 CMakeLists.txt 文件
在上述源文件所在目录下添加一个新文件 CMakeLists.txt,文件内容如下:

cmake_minimum_required(VERSION 3.0)
project(CALC)
add_executable(app add.c div.c main.c mult.c sub.c)

接下来依次介绍一下在 CMakeLists.txt 文件中添加的三个命令:

  • cmake_minimum_required:指定使用的 cmake 的最低版本

    • 可选,非必须,如果不加可能会有警告 -
  • project:定义工程名称,并可指定工程的版本、工程描述、web 主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。

# PROJECT 指令的语法是:
project(<PROJECT-NAME> [<language-name>...])
project(<PROJECT-NAME>
       [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
       [DESCRIPTION <project-description-string>]
       [HOMEPAGE_URL <url-string>]
       [LANGUAGES <language-name>...])
  • add_executable:定义工程会生成一个可执行程序
add_executable(可执行程序名 源文件名称)

这里的可执行程序名和 project 中的项目名没有任何关系

源文件名可以是一个也可以是多个,如有多个可用空格或 ; 间隔

# 样式1
add_executable(app add.c div.c main.c mult.c sub.c)
# 样式2
add_executable(app add.c;div.c;main.c;mult.c;sub.c)

(4) 执行 CMake 命令
万事俱备只欠东风,将 CMakeLists.txt 文件编辑好之后,就可以执行cmake命令了。

# cmake 命令原型
$ cmake CMakeLists.txt文件所在路径
$ tree
.
├── add.c
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── mult.c
└── sub.c

0 directories, 7 files
robin@OS:~/Linux/3Day/calc$ cmake .

当执行 cmake 命令之后,CMakeLists.txt 中的命令就会被执行,所以一定要注意给 cmake 命令指定路径的时候一定不能出错。

执行命令之后,看一下源文件所在目录中是否多了一些文件:

$ tree -L 1
.
├── add.c
├── CMakeCache.txt         # new add file
├── CMakeFiles             # new add dir
├── cmake_install.cmake    # new add file
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── Makefile               # new add file
├── mult.c
└── sub.c

我们可以看到在对应的目录下生成了一个 makefile 文件,此时再执行 make 命令,就可以对项目进行构建得到所需的可执行程序了。

$ make
Scanning dependencies of target app
[ 16%] Building C object CMakeFiles/app.dir/add.c.o
[ 33%] Building C object CMakeFiles/app.dir/div.c.o
[ 50%] Building C object CMakeFiles/app.dir/main.c.o
[ 66%] Building C object CMakeFiles/app.dir/mult.c.o
[ 83%] Building C object CMakeFiles/app.dir/sub.c.o
[100%] Linking C executable app
[100%] Built target app

# 查看可执行程序是否已经生成
$ tree -L 1
.
├── add.c
├── app					# 生成的可执行程序
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── Makefile
├── mult.c
└── sub.c

最终可执行程序 app 就被编译出来了(这个名字是在 CMakeLists.txt 中指定的)。

2.2.2 VIP 包房

通过上面的例子可以看出,如果在 CMakeLists.txt 文件所在目录执行了 cmake 命令之后就会生成一些目录和文件(包括 makefile 文件),如果再基于 makefile文件执行 make 命令,程序在编译过程中还会生成一些中间文件和一个可执行文件,这样会导致整个项目目录看起来很混乱,不太容易管理和维护,此时我们就可以把生成的这些与项目源码无关的文件统一放到一个对应的目录里边,比如将这个目录命名为 build:

$ mkdir build
$ cd build
$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/Linux/build

现在 cmake 命令是在 build 目录中执行的,但是 CMakeLists.txt 文件是 build 目录的上一级目录中,所以 cmake 命令后指定的路径为..,即当前目录的上一级目录。

当命令执行完毕之后,在 build 目录中会生成一个makefile文件

$ tree build -L 1
build
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
└── Makefile

1 directory, 3 files

这样就可以在 build 目录中执行 make 命令编译项目,生成的相关文件自然也就被存储到 build 目录中了。这样通过 cmake 和 make 生成的所有文件就全部和项目源文件隔离开了,各回各家,各找各妈。

参考

作者: 苏丙榅
链接: https://subingwen.cn/cmake/CMake-primer/
来源: 爱编程的大丙

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/27032.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python之函数(参数,匿名函数,局部变量和全局变量)

文章目录 前言一、函数的参数 1、形参和实参2、必传参数&#xff08;也叫&#xff1a;必须参数&#xff09;3、关键字传参4.、默认参数5、不定长参数6、传参的顺序二、匿名函数&#xff08;lambda函数&#xff09; 1. 定义及特点语法格式2. lambda函数的特点三、函数返回值retu…

【测试开发】实训记录日志

软件测试系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 了解测试开发和软件测试 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 …

SSD源码总结

一、生成默认框 默认框的宽高 默认框的宽高是相对于原图的尺寸计算出来的。 默认框的中心 默认框的中心是相对于特征图的尺寸计算出来的。 二、将真实框分配给默认框 1、区分正负样本 1.1、选取正样本 计算真实框&#xff08;bboxs&#xff09;与每个默认框&#xff08;…

SpringMVC-【回顾】

回顾MVC架构 什么是mvc&#xff1a;模型、视图、控制器 -----软件设计规范 回顾servlet maven项目导入依赖&#xff08;webmvc,servlet-api,jsp-api,jstl,junit&#xff09;创建子模块&#xff0c;在子模块中添加框架支持&#xff08;在子模块中导入依赖jsp、servlet【因为父…

2018 年一月联考逻辑真题

2018 年一月联考逻辑真题 三、逻辑推理&#xff1a;第 26-55 小题&#xff0c;每小题 2 分&#xff0c;共 60 分。下列每題给出的A.、 B.、C.、D.五个选项中&#xff0c;只有一项是符合试题要求的。请在答题卡上将所选项的字母涂黑。 真题&#xff08;2018-26&#xff09;-翻译…

区块链的基本介绍

目录 1、简介 2、区块链的分类 2.1 公有链 2.2 联盟链 2.3 私有链 3、区块链特征 4、区块链结构 5、区块链对记账权利的分配方式 5.1 POW 5.2 PoS 5.3 DPoS 6、Defi、NFT、 gameFi 7、DAPP 7.1 DAPP 的核心要素 8、比特币 8.1 比特币简介 8.2 比特币数字签名…

基站机房:保障通信网络稳定,如何解决安全隐患?

基站机房作为无线通信网络的关键组成部分&#xff0c;承载着大量的网络设备和通信设施&#xff0c;对于运营商和通信服务提供商来说具有重要意义。 无论是大型运营商还是通信服务提供商&#xff0c;动环监控系统都将成为他们成功运营和管理通信网络的关键工具。 客户案例 案例…

Java学习路线(22)——测试框架Junit

一、单元测试概念 单元测试就是针对最小的功能单元编写测试代码&#xff0c;Java程序最小的功能单元是方法&#xff0c;因此&#xff0c;单元测试就是针对Java方法的测试&#xff0c;进而检查方法正确性。 二、Junit测试框架 &#xff08;一&#xff09;概念&#xff1a; Jun…

HBase:(三)HBase API

HBase:(一)安装部署_只爱大锅饭的博客-CSDN博客hbase部署安装https://blog.csdn.net/qq_35370485/article/details/130988364?spm1001.2014.3001.5501 1.创建maven项目 2.添加依赖 <dependency><groupId>org.apache.hbase</groupId><artifactId>hba…

【Java基础】注解与反射

一、学习笔记 &#xff08;本文内容基本源自参考链接1视频教程&#xff09; 1、注解的含义 1&#xff09;注解&#xff08;annotation)是从jdk5.0开始引入的新技术&#xff0c;其作用&#xff1a;不是程序本身&#xff0c;可对程序作解释&#xff08;该作用与注释comment相同…

MFC(十二)多个对话框

我们来制定多个对话框&#xff0c;每个对话框都有不同的功能&#xff0c;单击下一步&#xff0c;即可跳转到下一个对话框 1.新建一个启动按钮 2.在资源视图&#xff0c;Dialog里面&#xff0c;右键-->添加资源---->dialog>选择IDD PROPPAGE_SMALL新建 属性页&#…

同浏览器下多窗口进行跨源通信、同源通信

同浏览器下多窗口进行跨源通信、同源通信 多页面通信运用到了“发布订阅”的设计模式&#xff0c;一个页面发布指令&#xff0c;其他页面进行订阅并进行相应的行为操作&#xff01; 一、跨源通信 window.postMessage() window.postMessage() 方法可以安全地实现跨源通信。通常…

Vue.js 中的数据双向绑定是如何实现的?

Vue.js 中的数据双向绑定是如何实现的&#xff1f; Vue.js 是一款流行的前端框架&#xff0c;它的核心功能之一是数据双向绑定。本文将介绍 Vue.js 中数据双向绑定的实现原理&#xff0c;并附上相关代码实例。 什么是数据双向绑定&#xff1f; 在传统的前端开发中&#xff0c…

智能应用搭建平台——LCHub低代码表单 vs 流程表单 vs 仪表盘

1. LCHub低代码如何选择 「流程表单」:填报数据,并带有流程审批功能,适合报销、请假申请或其他工作流; 「表单」:填报数据,并带有数据协作功能,如修改、删除、导入、导出,并可以给不同的人不同的管理权限; 「仪表盘」:数据分析处理、结果展示功能,如数据汇总、趋…

JavaSSM笔记(一)

**建议&#xff1a;**对Java开发还不是很熟悉的同学&#xff0c;最好先花费半个月到一个月时间大量地去编写小项目&#xff0c;不推荐一口气学完&#xff0c;后面的内容相比前面的内容几乎是降维打击&#xff0c;一口气学完很容易忘记之前所学的基础知识&#xff0c;尤其是Java…

Python--注释

Python--注释 <font size4, colorblue> 一、Python中注释的形式<font size4, colorblue> 1、单行注释&#xff1a;使用“#”符号注释<font size4, colorblue> 2、多行注释&#xff1a;使用一对三个英文单引号注释<font size4, colorblue> 3、多行注释&…

通用文字识别OCR 之实现数字化教材

引言 通用文字 OCR 识别 API 是一种功能强大的服务&#xff0c;可用于多场景、多语种的整图文字检测和识别&#xff0c;通过将OCR技术应用于学校环境&#xff0c;可以实现教育资源的数字化和学习过程的自动化。 本文将探讨通用文字识别OCR 在学校的实际应用&#xff0c;希望对…

如何在 JavaScript 中创建自定义警告框

本文将介绍如何使用 jQuery UI、SweetAlert2 和自定义警报功能在 JavaScript 中创建自定义警报框。 使用 jQuery UI 创建自定义警告框 我们可以使用 jQuery UI 来模仿 JavaScript 本机 alert() 函数的功能。 尽管 jQuery UI 有很多 API&#xff0c;您可以使用它的 dialog() AP…

基于摄影测量的三维重建【终极指南】

我们生活的时代非常令人兴奋&#xff0c;如果你对 3D 东西感兴趣&#xff0c;更是如此。 我们有能力使用任何相机&#xff0c;从感兴趣的物体中捕捉一些图像数据&#xff0c;并在眨眼间将它们变成 3D 资产&#xff01; 这种通过简单的数据采集阶段进行的 3D 重建过程是许多行业…

泰克AFG31000系列任意波函数发生器应用

模拟电路检定 这是一个模拟世界。所有物理量均使用模拟信号捕获和表示。因此&#xff0c;需要检定放大器、滤波器和转换器等模拟电路的性能。 InstaView? 技术避免在阻抗不匹配的 DUT 上增加的波形不确定性频率范围为 25 MHz 至 250 MHz由于信号保真度高&#xff0c;无需使…
最新文章