cmake学习笔记1

基础概念

CMake是什么?
CMake是一个元构建系统(meta build-system),用于生产其他构建系统文件(如Makefile或Ninja)。

基础操作方式

CMake使用一个CMakeLists.txt文件描述配置,然后使用cmake驱动这个文件生成对应构建系统文件。
如果不想使用cmake命令行驱动CMakeLists.txt,也可以用cmake-gui进行可视化完成.

.
└── CMakeLists.txt
1 directory, 1 file

在这个目录执行如下命令可以生成默认的构建系统文件(一般为Makefile)

# -S 指定源码目录, -B 指定生产的构建文件的目录
cmake  -S . -B build 

运行后会在build目录下生成很多文件。

tree -L 2
.
├── CMakeLists.txt
└── build├── CMakeCache.txt├── CMakeFiles├── Makefile└── cmake_install.cmake3 directories, 4 files

你会惊讶的发现在build文件夹下有一个Makefile文件。如果你想生成Ninja可以使用-G命令完成。

cmake -G "Ninja" -S . -B build

如果想查询支持的构建系统用cmkae --help查询,如下所示在这里插入图片描述

由于cmake会生成不同的构建系统。比如makefile你会继续调用make命令去完成编译等流程。但是如果是ninja就要执行ninja命令去编译。为了屏蔽这个差异make提供了下列命令去无差别编译安装

# --VERBOSE用于指定输出详细信息 
camke --build .  --verbose
camke --install .

但可惜的cmake没有提供相关的clean命令,需要你自己根据平台调用如ninja cleanmake clean

变量类型

CMAKE中变量可以大致分两种:

  1. 缓存变量
  2. 非缓存变量

所以我们先明白这个非常重要的基础概念才能方便我们编写文件。

缓存变量

所有声明的变量会在cmake生成配置文件后会放入一个CMakeCache.txt文件中。
如果你再次修改CMakeLists.txt去修改一个缓存变量你会发现CMakeCache.txt不会更新,除非你手动删除这个文件。(如果使用命令行CMAKE -DKEY=VALUE 的缓存变量会强制更新CMakeCache.txt

缓存变量特性:

  1. 全局修改会被其他cmake感知
  2. 全局可访问(同级cmake也可以)
  3. 除显示强制覆盖否则不会重写该数值
  4. 修改CMakeLists.txt中的缓存变量在次用cmake重生成构建文件不会更新CMakeCache.txt
  5. 命令行传入的缓存变量每次cmake生产配置文件都会更新CMakeCache.txt

示例一

证明缓存变量会被写一个CMakeCache.txt文件中

我们看有如下目录文件

├── CMakeLists.txt
1 directory, 1 files
//CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(DEMO)
# 声明一个缓存变量
set(myvar "Hello" CACHE STRING "一个说明行描述可以无视")
# 声明一个非缓存变量
set(myNormalVar "Hello world")

执行如下命令

cmake   -S . -B build

生成如下文件和对应目录

.
├── CMakeLists.txt
└── build├── CMakeCache.txt├── CMakeFiles├── Makefile└── cmake_install.cmake
3 directories, 4 files

我们查找这里两个变量是否在CmakeCache.txt中

使用grep查找
grep -E "myvar|myNormalVar" ./build/CMakeCache.txt  
输出:
myvar:STRING=Hello

示例二

修改CMakeLists.txt的缓存变量不会引起CmakeCache.txt更新

# CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(DEMO)
# 声明一个缓存变量
set(myvar "Hello" CACHE STRING "一个说明行描述可以无视")
//生成配置文件
cmake -S . -B build
//查找变量
grep -E "myvar" ./build/CMakeCache.txt
//输出结果
myvar:STRING=Hello

此时我们修改CMakeLists.txt中的myvar变量为world

# CMakeLists.txt//...略
//修改一个变量
set(myvar "World" CACHE STRING "一个说明行描述可以无视")
//...略
//重新生成配置文件
cmake -S . -B build
//在此查找变量
grep -E "myvar" ./build/CMakeCache.txt
//输出结果 发现并没有改为World
myvar:STRING=Hello

示例二

CMake -DKey=value 命令行传入的缓存变量会强制刷新CMakeCache.txt

//第一次运行配置命令并查找
cmake -DmyKey=myvalue -S . -B build ; grep -E "myKey" ./build/CMakeCache.txt
myKey:UNINITIALIZED=myvalue
//第二次运行配置命令并查找
cmake -DmyKey=myvalue22222 -S . -B build ; grep -E "myKey" ./build/CMakeCache.txt
myKey:UNINITIALIZED=myvalue22222

示例三

我们缓存变量作用域和可见性

.
├── CMakeLists.txt
├── childDir01
│   └── CMakeLists.txt
└── childDir02└── CMakeLists.txt
3 directories, 3 files
//.CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(PARENT_PRO)set(myParentNormalVal "I'm myParentNormalVal")
set(myParentCacheVal "I'm myParentCacheVal" CACHE STRING "一个描述")#引入子cmake
add_subdirectory(childDir01)
add_subdirectory(childDir02)
# 打印子make修改后的变量
# 虽然CHILD01_PRO修改普通变量,但是由于作用域问题输出旧数值。 PARENT_PRO myParentNormalVal =  I'm myParentNormalVal
message("${PROJECT_NAME} myParentNormalVal =  ${myParentNormalVal}")
# 缓存变量全局修改都会生效,输出CHILD01_PRO修改后的数值。 PARENT_PRO myParentCacheVal = I'm myParentCacheValChild01 
message("${PROJECT_NAME} myParentCacheVal = ${myParentCacheVal}")
# 父cmake无妨访问子cmake的普通变量
message("${PROJECT_NAME} myChild01NormalVal =  ${myChild01NormalVal}")
# 子cmake定义的缓存变量全局都可以访问
message("${PROJECT_NAME} myChild01CacheVal = ${myChild01CacheVal}")//./child01/CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(CHILD01_PRO)
set(myChild01NormalVal "I'm myChild01NormalVal")
set(myChild01CacheVal "I'm myChild01CacheVal" CACHE STRING "一个描述" FORCE)
# 打印父变量
#子cmake可以任意访问父变量(普通变量修改仅所在的Cmake文件生效不能跨全局) 输出 CHILD01_PRO myParentNormalVal =  I'm myParentNormalVal
message("${PROJECT_NAME} myParentNormalVal =  ${myParentNormalVal}")
#子cmake可以任意访问父变量(注意缓存变量的修改全局都可以生效) 输出 CHILD01_PRO myParentCacheVal = I'm myParentCacheVal
message("${PROJECT_NAME} myParentCacheVal = ${myParentCacheVal}")
message("\r\n${PROJECT_NAME} change myParentNormalVal and myParentCacheVal :\r\n")
# 修改父变量
set(myParentNormalVal "I'm myParentNormalValChild01")
set(myParentCacheVal "I'm myParentCacheValChild01 " CACHE STRING "一个描述" FORCE)
# 打印修改变量后的数值
#修改普通变量仅在当前CMakeLists.txt生效。回到父时依旧是旧值。输出 CHILD01_PRO myParentNormalVal =  I'm myParentNormalValChild01
message("${PROJECT_NAME} myParentNormalVal =  ${myParentNormalVal}")
#修改缓存变量全局生效。回到父时输出改变后的数值。输出 CHILD01_PRO myParentCacheVal = I'm myParentCacheValChild01 
message("${PROJECT_NAME} myParentCacheVal = ${myParentCacheVal}")//./child02/CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(CHILD02_PRO)
# 打印父变量
#子cmake可以任意访问父变量(普通变量修改仅所在的Cmake文件生效不能跨全局) 输出 CHILD02_PRO myParentNormalVal =  I'm myParentNormalVal
message("${PROJECT_NAME} myParentNormalVal =  ${myParentNormalVal}")
#子cmake可以任意访问父变量(注意缓存变量的修改全局都可以生效) 输出 CHILD02_PRO myParentCacheVal = I'm myParentCacheValChild01 
message("${PROJECT_NAME} myParentCacheVal = ${myParentCacheVal}")
#打印同级的Cmake变量
#同级cmake变量无法访问 输出 CHILD02_PRO myChild01NormalVal =  
message("${PROJECT_NAME} myChild01NormalVal =  ${myChild01NormalVal}")
#缓存变量可以访问 输出 CHILD02_PRO myChild01CacheVal = I'm myChild01CacheVal
message("${PROJECT_NAME} myChild01CacheVal = ${myChild01CacheVal}")
#执行命令
cmake  -S . -B build
#输出结果
CHILD01_PRO myParentNormalVal =  I'm myParentNormalVal
CHILD01_PRO myParentCacheVal = I'm myParentCacheVal
CHILD01_PRO change myParentNormalVal and myParentCacheVal :
CHILD01_PRO myParentNormalVal =  I'm myParentNormalValChild01
CHILD01_PRO myParentCacheVal = I'm myParentCacheValChild01 CHILD02_PRO myParentNormalVal =  I'm myParentNormalVal
CHILD02_PRO myParentCacheVal = I'm myParentCacheValChild01 
CHILD02_PRO myChild01NormalVal =  
CHILD02_PRO myChild01CacheVal = I'm myChild01CacheValPARENT_PRO myParentNormalVal =  I'm myParentNormalVal
PARENT_PRO myParentCacheVal = I'm myParentCacheValChild01 
PARENT_PRO myChild01NormalVal =  
PARENT_PRO myChild01CacheVal = I'm myChild01CacheVa

非缓存变量

反向参考变量

变量声明方式

set

你可以使用set命令如下声明如下变量MYVAR 。(注意此处非缓存变量类型)
set(<variable> <value>... [PARENT_SCOPE])

cmake_minimum_required(VERSION 3.14)
project(DEMO)
set(MYVAR "MYVALUE")
message("变量MYVAR=${MYVAR}")

当然变量可以拼接多个字符串并自动使用进行分割

cmake_minimum_required(VERSION 3.14)
project(DEMO)
set(MYVAR "MYVALUE" "MYVALUE2" "MYVALUE3")
message("变量MYVAR=${MYVAR}")

输出:

cmake -S . -B build 
变量MYVAR=MYVALUE;MYVALUE2;MYVALUE3
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /Users/fanjack/Desktop/learnMake/build

上面声明的是一个非缓存类型的普通变量。我们来看看声明缓存变量
set(<variable> <value>... CACHE <type> <docstring> [FORCE])

cmake_minimum_required(VERSION 3.14)
project(DEMO)
# 声明一个缓存变量
set(myvar "Hello" CACHE STRING "一个说明")
message("myvar is ${myvar}")  
# 想修改缓存变量 但是由于缓存变量一定被设置除非使用FORCE关键字不然不允许重写
set(myvar "Hello2" CACHE STRING  "一个说明")
message("myvar is ${myvar}") 
# 使用FORCE关键字重写
set(myvar "Hello3" CACHE STRING "一个说明" FORCE)
message("myvar is ${myvar}")  

对应的输出:

myvar is Hello
myvar is Hello
myvar is Hello3

选项变量

option(<variable> "<help_text>" [value]) (自动为缓存变量)

option(USE_MYMATH "Use my math implementation" ON)
//为ON表示条件为真输出 Using my math implementation
if(USE_MYMATH)message("Using my math implementation")
else()message("Using standard math library")  
endif()

环境变量

访问环境变量 $ENV{XXX} 其中XXX为环境变量名,makefile会自动将环境转化为makefile变量。

举例

cmake_minimum_required(VERSION 3.14)
project(DEMO)
#因为环境变量存在PATH所以输出相关数值
message("env $ENV\{PATH\} = $ENV{PATH}")
#因为PATH是环境而不是CMAKE变量所以不会有任何输出
message("env $\{PATH\} = ${PATH}")

传递可见性

在CMAKE有很多函数可以定义头文件目录或宏等,在函数中一个参数叫可见性的属性。这个属性在多CMakeLists中显得尤为重要。假设target A 依赖target B。那么target B部分定义的属性是否对于target A可见?
在cmake一般有如下三个可见性属性

<INTERFACE|PUBLIC|PRIVATE>

  • INTERFACE 对自身不可见,但是对于依赖自身的target可见
  • PUBLIC 对自身和依赖自身的target都可见
  • PRIVATE 仅自身可见

我们举例如下

.
├── CMakeLists.txt
├── main.cpp
└── mycalclib├── CMakeLists.txt├── mycalc.cpp└── mycalc.h2 directories, 5 files
//main.cpp
#include "mycalclib/mycalc.h"
#include <iostream>
using namespace std;
int main(int argc, char *args[]) {mycalc d;d.run();
#ifdef  FOOcout<<"main "<<"FOO "<<FOO<<endl;
#elsecout << "main " << "nothing" << endl;
#endifreturn 0;
}
# ./CMakeLists.txt
cmake_minimum_required(VERSION 3.26)
project(learnC)set(CMAKE_CXX_STANDARD 17)
add_subdirectory(mycalclib)
add_executable(learnC main.cpp)
target_link_libraries(learnC mycalc)
# ./mycalclib/CMakeLists.txt
cmake_minimum_required(VERSION 3.26)
project(mycalclib)
set(CMAKE_CXX_STANDARD 17)
add_library(mycalc STATIC mycalc.cpp)
#定义一个宏变量,名为mycalc,且是私有的也就是库本身才可见。
target_compile_definitions(mycalc PRIVATE FOO=1)
//mycalc.cpp
#include "mycalc.h"
#include <iostream>
using namespace std;
mycalc::mycalc() {
}
void mycalc::run() {
#ifdef  FOOcout<<"mycalc "<<"FOO "<<FOO<<endl;
#elsecout << "mycalc " << "nothing" << endl;
#endif
}
//mycalc.h
#ifndef LEARNC_MYCALC_H
#define LEARNC_MYCALC_H
class mycalc{
public:mycalc();void run();
};
#endif //LEARNC_MYCALC_H

我们最后编译输出

mycalc FOO 1
main nothing

由于target_compile_definitions(mycalc PRIVATE FOO=1)是私有定义,在main.cpp是不可见的,但是对于库本身是可见。我们改为INTERFACE再次运行。
target_compile_definitions(mycalc INTERFACE FOO=1)

mycalc nothing
main FOO 1

public运行结果,target_compile_definitions(mycalc PUBLIC FOO=1)

mycalc FOO 1
main FOO 1

配置头文件

configure_file文档
camke提供了配置头文件,可以留用这个文件根据条件生成C++中的标准头文件等。

配置文件有一些奇特占位符语法:

  • #cmakedefine VAR
  • ${VAR}
  • @VAR@
  • $CACHE{VAR}

#cmakedefine VAR 表示如果cmake存在一个变量VAR可以让if返回为true,那么这一行会被换为#define VAR
${VAR}/@VAR@/$CACHE{VAR} VAR 表示如果cmake存在一个变量VAR可以让if返回为true,那么这一行会被换为对应的变量.如果没有那么会替换为空

我们举例如下
假设我们的配置头文件叫foo.h.in

//foo.h.in
#cmakedefine FOO_STRING 
#define FOO_STRING2 "${FOO_STRING2_VALUE}"
#cmakedefine FOO_STRING3

对应cmka文件

cmake_minimum_required(VERSION 3.14)
project(DEMO)
# 因为FOO_STRING 被定义了当if条件为变量时会为true.所以#cmakedefine FOO_STRING 转为 # define FOO_STRING 
set(FOO_STRING "Enable")
# 因为FOO_STRING2_VALUE 被定义了当if条件为变量时会为true. 因此foo.h.in的FOO_STRING2_VALUE会被替换为I'm FOO_STRING2_VALUE  
set(FOO_STRING2_VALUE "I'm FOO_STRING2_VALUE ")
# 定义了一个可选变量由于是OFF if会返回true。#cmakedefine FOO_STRING3 不会转化为# define FOO_STRING3 
option(FOO_STRING3 "Use my math implementation" OFF)
#设置配置文件和对应的输出文件
configure_file(foo.h.in foo.h )

执行命令后会在当前目录生产一个foo.h文件

//foo.h
#define FOO_STRING 
#define FOO_STRING2 "I'm FOO_STRING2_VALUE "
/* #undef FOO_STRING3 */

配置文件

CMake 保姆级教程【C/C++】
CMake 保姆级教程(上)
CMake 保姆级教程(下)
CMake官方教程

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

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

相关文章

websokcet服务端实现

一/websokcet服务端实现 步骤一&#xff1a; springboot底层帮我们自动配置了websokcet&#xff0c;引入maven依赖 1 2 3 4 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</arti…

AI图片智能选区抠像解决方案

高质量的图片处理往往依赖于繁琐的手动操作&#xff0c;耗费大量时间与精力。美摄科技推出了一款革命性的AI图片智能选区抠像解决方案&#xff0c;旨在帮助企业轻松实现图片的高效处理&#xff0c;提升内容创作效率与质量。 美摄科技的AI图片智能选区抠像解决方案&#xff0c;…

AFCI 应用笔记二之数据采集

1. 简介 基于监督学习的神经网络算法需要大量数据作为输入&#xff0c;模型完全由数据驱动&#xff0c;其数据质量是算法有效的必要条件&#xff0c;所以如何高效的采集到数据&#xff0c;以及正确的标注或分析是极其重要的&#xff0c;如果第一步有问题&#xff0c;后续的所有…

C++搭建深度学习的推理框架

我们的目的是:借助C++搭建一个类似于pytorch,tensorflow的深度学习框架,对标pytorch,tensorflow实现对应的功能。由于本人能力有限,下面本人将借助C++搭建一个简单的全连接神经网络,并且尝试解释里面的算子定义和计算图构建。 算子定义 回顾pytorch里面搭建的全连接神经网…

ESP32S3网络编程学习笔记(1)—— Wi-Fi扫描实验

前言 &#xff08;1&#xff09;如果有嵌入式企业需要招聘湖南区域日常实习生&#xff0c;任何区域的暑假Linux驱动/单片机/RTOS的实习岗位&#xff0c;可C站直接私聊&#xff0c;或者邮件&#xff1a;zhangyixu02gmail.com&#xff0c;此消息至2025年1月1日前均有效 &#xff…

基于DPDK的VPP 插件demo代码

VPP的插件编写&#xff0c; 首先要把VPP 工程下载下来&#xff0c; 编译通过。 然后按照example程序的套中来编写插件。 还有一个前提&#xff0c; 就是测试机上已经具备了DPDK 已经可用版本。 1. 下载VPP。 可以从github上下载VPP的指定版本的zip包&#xff0c; 也可以用…

2024年租用阿里云服务器多少钱一年?连夜整理分享

阿里云服务器租用价格表2024年最新&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元&#xff0c;ECS u1服务器2核4G5M固定带宽199元一年&#xff0c;2核4G4M带宽轻量服务器一年165元12个月&#xff0c;2核…

__ne__()函数详解

在Python中&#xff0c;ne 是一个特殊方法&#xff0c;用于定义不等于&#xff08;!&#xff09;操作符的行为。当你使用 ! 操作符来比较两个类的实例时&#xff0c;Python会自动调用这个方法。如果这个方法没有在你的类中定义&#xff0c;那么 ! 操作符会使用 eq 方法的结果来…

【C++】C++ primer plus 第十二章--类和动态内存分配

动态内存和类 关于静态数据成员 类之作声明&#xff0c;不分配内存&#xff0c;因此静态成员变量在类中不能进行初始化&#xff0c;需要在类外进行。特殊情况&#xff1a; 存在可以在类中声明静态成员并初始化的情况&#xff0c;成员类型为const整型或者const枚举类型。 特殊…

软考高级架构师:嵌入式系统的内核架构

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

2024/4/1—力扣—二叉树的最近公共祖先

代码实现&#xff1a; 思路&#xff1a; 递归判断左子树和右子树&#xff0c;查找p或者q是否在当前节点的子树上 1&#xff0c;在同一子树上&#xff0c;同一左子树&#xff0c;返回第一个找到的相同值&#xff0c;同一右子树上&#xff0c;返回第一个找到的相同值 2&#xff0…

Oracle23免费版简易安装攻略

installation-guide 1 安装 root用户下 wget https://yum.oracle.com/repo/OracleLinux/OL8/developer/x86_64/getPackage/oracle-database-preinstall-23c-1.0-1.el8.x86_64.rpm wget https://download.oracle.com/otn-pub/otn_software/db-free/oracle-database-free-23c-1…

UML 绘制工具 starUML 入门介绍

拓展阅读 常见免费开源绘图工具 OmniGraffle 创建精确、美观图形的工具 UML-架构图入门介绍 starUML UML 绘制工具 starUML 入门介绍 PlantUML 是绘制 uml 的一个开源项目 UML 等常见图绘制工具 绘图工具 draw.io / diagrams.net 免费在线图表编辑器 绘图工具 excalidr…

linux lua版本升级

要在Linux上升级Lua到5.4版本&#xff0c;你需要执行以下步骤&#xff1a; 1、下载Lua 5.4源代码&#xff1a; 首先&#xff0c;你需要从Lua的官方网站下载Lua 5.4的源代码。你可以访问Lua的官方网站或使用wget或curl命令从命令行下载。 wget http://www.lua.org/ftp/lua-5.4.x…

工具推荐-针对Nacos利器-NacosExploitGUI_v4.0

Nacos是由阿里所开发的一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 工具简介 集成Nacos的各种poc Nacos控制台默认口令漏洞(nacos,nacos)Nacostoken.secret.key默认配置(QVD-2023-6271)Nacos-clientYaml反序列化漏洞Nacos Jraft Hessian反序列化漏洞…

代码随想录算法训练营第46天|139.单词拆分|关于多重背包,你该了解这些!|背包问题总结篇!

代码随想录算法训练营第46天|139.单词拆分|关于多重背包&#xff0c;你该了解这些&#xff01;|背包问题总结篇&#xff01; 详细布置 关于 多重背包&#xff0c;力扣上没有相关的题目&#xff0c;所以今天大家的重点就是回顾一波 自己做的背包题目吧。 139.单词拆分 视频讲…

PET-SQL:基于大模型的两阶段Text2SQL方法

简介 PET-SQL出自论文《PET-SQL: A Prompt-enhanced Two-stage Text-to-SQL Framework with Cross-consistency》&#xff0c;将基于大模型的Text2SQL分为两个阶段进行&#xff0c;在第一阶段使用数据表schema信息、数据表采样数据、相似问答问答对生成初步的SQL(PreSQL)&…

【边缘智能】00_边缘计算发展背景

本系列是个人学习《边缘就算基础知识入门》的笔记&#xff0c;仅为个人学习记录&#xff0c;欢迎交流&#xff0c;感谢批评指正 移动物联设备产生海量数据&#xff0c;数据密集型移动智能应用&#xff0c;计算密集、动态性高&#xff0c;实时性强 传统云计算架构 基于广域互联…

大学课堂点名程序

大学课堂点名程序 from gtts import gTTS import os import tkinter as tk import pygame import csv import random from datetime import datetime from tkinter import messagebox from tkinter import simpledialog input_data="student1" def langDu(text):tts…

matrix-breakout-2-morpheus 靶机渗透

信息收集&#xff1a; 1.nmap存活探测&#xff1a; nmap -sn -r 192.168.10.1/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-04-06 12:13 CST Nmap scan report for 192.168.10.1 Host is up (0.00056s latency). MAC Address: 00:50:56:C0:00:08 (VMware) Nmap…