CMake的原理与使用方法

news2025/1/18 11:48:46

一.为什么需要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.coloradmin.cn/o/1719990.html

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

相关文章

2024年上半年系统架构设计师——案例第四题——智能汽车相关

写在前面 趁着还记得请&#xff0c;留个纪念。MongoDB的概念完全没记&#xff0c;综合下来只好选这个了。 这题基本上是有自动驾驶背景知识的人的福音&#xff0c;也是第一次考的和现代热点前沿技术相关的内容&#xff0c;而不是考些老嵌入式架构的&#xff08;上一次看到的还…

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

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

如何在Windows 10上更改默认系统字体,这里有详细步骤

Windows 10的默认系统字体Segoe UI看起来相当不错。但是,如果你有更好的替代品,你可以更改Windows 10 PC上的默认系统字体。我们将向你展示如何执行此操作。 如何使用注册表编辑器更改默认系统字体 在撰写本文时,“设置”和“控制面板”都没有更改默认系统字体的选项。这意…

信息素养大赛晋级复赛,你们都刷题库了吗?

2024年全国青少年信息素养大赛初赛成绩之前发布了&#xff0c;今天同学们可以查询是否晋级信息素养大赛复赛了&#xff0c;复赛应该会难度比较大&#xff0c;7月复赛&#xff0c;8月全国总决赛&#xff0c;据6547网题库获悉&#xff0c;今年初赛的人数近22万人。 查询方式&…

QT打包成exe文件运行(非安装包)

在QT Creator里面构建那里&#xff0c;点击Release构建完会生成类似这样的文件夹 文件生成的路径在项目概要里面 在该文件里面可以看到release文件夹&#xff0c;release进去后就可以看到有个exe文件&#xff0c;但是还需要导入一些三方库&#xff0c;目前点击会出现类似的错误…

解决bind error: Address already in use

是端口复用问题 产生原因 程序突然退出系统但是没有释放端口 问题解决 首先通过 //显示进程信息 ps -la //杀死相关进程 kill -9 xxxx然后添加socket设置 int on1; if(setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0){perror("setsockopt")…

【主动均衡和被动均衡】

文章目录 1.被动均衡2.主动均衡1.被动均衡 被动均衡一般通过电阻放电的方式,对电压较高的电池进行放电,以热量形式释放电量,为其他电池争取更多充电时间。这样整个系统的电量受制于容量最少的电池。充电过程中,锂电池一般有一个充电上限保护电压值,当某一串电池达到此电压…

三十二篇:转化决策为行动:探索决策支持系统的深层价值

转化决策为行动&#xff1a;探索决策支持系统的深层价值 1. DSS的精髓&#xff1a;定义与核心功能 1.1 定义与作用 在现代商业的快速演变中&#xff0c;决策支持系统&#xff08;Decision Support Systems, DSS&#xff09;已成为企业获得竞争优势的重要工具。DSS是一种利用先…

golang语言的gofly快速开发框架如何设置多样的主题说明

本节教大家如何用gofly快速开发框架后台内置设置参数&#xff0c;配置出合适项目的布局及样式、主题色&#xff0c;让你您的项目在交互上加分&#xff0c;也是能帮你在交付项目时更容易得到客户认可&#xff0c;你的软件使用客户他们一般都是不都技术的&#xff0c;所以当他们拿…

Jmeter实战教程入门讲解

前言 通过前面对Jmeter元件的讲解&#xff0c;大家应该都知道常用元件的作用和使用了。编写Jmeter脚本前我们需要知道Jmeter元件的执行顺序&#xff0c;可以看看我这篇性能测试学习之路&#xff08;三&#xff09;—初识Jmeter来了解下。下面我将以工作中的一个简单的实例带大…

基于开源项目ESP32 SVPWM驱动无刷电机开环速度测试

基于开源项目ESP32 SVPWM驱动无刷电机开环速度测试 ✨本篇硬件电路和代码来源于此开源项目&#xff1a;https://github.com/MengYang-x/STM3F401-FOC/tree/main&#x1f4cd;硬件电路和项目介绍&#xff0c;立创开源广场&#xff1a;https://oshwhub.com/shadow27/tai-yang-nen…

http协议及httpd安装组成

文章目录 一、http协议http协议通信过程http相关技术网站访问量HTTP工作机制HTTP协议版本HTTP请求访问的完整过程HTTP报文头部响应报文 二、httpd安装组成apache介绍和特点工作模式&#xff08; MPM multi-processing module &#xff09;Http相关文件Http编译安装httpd常见配置…

SqlServer还原系统库步骤及问题解决

还原master 需要切换到binn目录 Cd C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Binn 关闭服务 用单用户模式启动 SQL Server 默认实例 sqlservr.exe -m 直接单用户登录 恢复master备份文件 RESTORE DATABASE master FROM DISK E:\dbbak\txic_ke…

【SpringMVC】_设置响应状态码与Header

目录 1. 设置响应状态码 2. 设置响应Header 2.1 设置Content-Type 2.1.1 不使用RequestMapping的produce属性 2.1.2 使用RequestMapping的produce属性 2.2 设置/新增其他Header 1. 设置响应状态码 Spring是基于servlet实现的&#xff0c;设置HTTP响应的状态码可以通过se…

精武杯复现(服务器部分)

起镜像连ssh 这里是raid5重组&#xff0c;123组成一个数据盘&#xff0c;4是系统盘&#xff0c;仿真的时候记得全选 第一步就是先配网&#xff0c;在etc/sysconfig/network-script里边&#xff0c;cat ifcfg-ens33 发现是dhcp&#xff0c;并且没有启动 直接ifup ifcfg-ens3…

蓝桥杯单片机第五届国赛题目

前言&#xff1a;针对串口的练手&#xff0c;此处只作代码记录&#xff0c;不进行分析和展示 目录 题目代码底层驱动主程序核心代码 题目 代码 注&#xff1a;EEPROM的五组后丢弃用一个记录次数的变量进行循环即可&#xff0c;我没有写这一部分代码。 底层驱动 IIC unsign…

低代码开发与人工智能技术在商品推荐系统中的应用

引言 低代码开发和人工智能技术的背景和重要性 随着数字化转型的深入&#xff0c;企业在信息技术领域面临着前所未有的挑战和机遇。快速变化的市场需求、日益复杂的技术环境以及高度竞争的商业环境&#xff0c;迫使企业不断寻求高效的开发和运营解决方案。低代码开发平台应运而…

【新能源大巴BMS结构与乘用车的区别】

新能源大巴BMS结构与乘用车的区别 这篇文章主要介绍新能源大巴的电池和BMS的结构与乘用车的区别。 主要有&#xff0c;新能源大巴行业、新能源电池系统结构和新能源大巴的BMS系统。 第一部分 新能源大巴行业 其实数数全球的商用车(大巴卡车)&#xff0c;大致的方向还是沿着就…

动态路由协议实验——RIP

动态路由协议实验——RIP 什么是RIP ​ RIP(Routing Information Protocol,路由信息协议&#xff09;是一种内部网关协议&#xff08;IGP&#xff09;&#xff0c;是一种动态路由选择协议&#xff0c;用于自治系统&#xff08;AS&#xff09;内的路由信息的传递。RIP协议基于…

瑞吉外卖项目学习笔记(二)后台系统的员工管理业务开发

一、完善登录功能 1.1 问题分析 1.2 代码实现 package com.itheima.reggie.filter;//这是一个过滤器类 //登录检查过滤器import com.alibaba.fastjson.JSON; import com.itheima.reggie.common.R; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf…