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.cmake

3 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 myChild01CacheVal

PARENT_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.h

2 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  FOO
    cout<<"main "<<"FOO "<<FOO<<endl;
#else
    cout << "main " << "nothing" << endl;
#endif
    return 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  FOO
    cout<<"mycalc "<<"FOO "<<FOO<<endl;
#else
    cout << "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官方教程

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

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

相关文章

【数据结构】ArrayList详解

目录 前言 1. 线性表 2. 顺序表 3. ArrayList的介绍和使用 3.1 语法格式 3.2 添加元素 3.3 删除元素 3.4 截取部分arrayList 3.5 其他方法 4. ArrayList的遍历 5.ArrayList的扩容机制 6. ArrayList的优缺点 结语 前言 在集合框架中&#xff0c;ArrayList就是一个…

【Linux】环境基础开发工具使用——vim使用

Linux 软件包管理器 yum 什么是软件包 1.在 Linux 下安装软件 , 一个通常的办法是下载到程序的源代码 , 并进行编译 , 得到可执行程序 . 2.但是这样太麻烦了 , 于是有些人把一些常用的软件提前编译好 , 做成软件包 ( 可以理解成 windows 上的安装程序) 放在一个服务器…

C#,简单,精巧,实用的文件夹时间整理工具FolderTime

点击下载本文软件&#xff08;5积分&#xff09;&#xff1a; https://download.csdn.net/download/beijinghorn/89071073https://download.csdn.net/download/beijinghorn/89071073 百度网盘&#xff08;不需积分&#xff09;&#xff1a; https://pan.baidu.com/s/1FwCsSz…

ThreadLocal上传下载文件

文章目录 ThreadLocal1.基本介绍1.什么是ThreadLocal&#xff1f;2.示意图 2.快速入门1.创建普通java项目2.编写代码1.T1.java2.T1Service.java3.T2Dao.java4.Dog.java 3.结果 3.ThreadLocal源码解读1.set方法2.set方法总结3.get方法 上传下载文件1.基本介绍1.基本说明2.文件上…

Spring Cloud介绍

一、SpringCloud总体概述 Cloud Foundry Service Broker&#xff1a;通用service集成进入Cloud Foundry Cluster&#xff1a;服务集群 Consul&#xff1a;注册中心 Security&#xff1a;安全认证 Stream&#xff1a;消息队列 Stream App Starters&#xff1a;Spring Cloud Stre…

Redis 客户端

Redis 客户端 客户端-服务器结构 Redis 同 Mysql 一样&#xff0c;也是一个客户端-服务器结构的程序&#xff0c;结构如下图&#xff1a; 注&#xff1a;Redis 客户端和服务器可以在同一个主机上&#xff0c;也可以在不同主机上 Redis 客户端的多种形态 自带的命令行客户端&…

【Qt 学习笔记】详解Qt中的信号和槽

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 详解Qt中的信号与槽 文章编号&#xff1a;Qt 学习笔记 / 12 文章目录…

【Node.js】短链接

原文链接&#xff1a;Nodejs 第六十二章&#xff08;短链接&#xff09; - 掘金 (juejin.cn) 短链接是一种缩短长网址的方法&#xff0c;将原始的长网址转换为更短的形式。短链接的主要用途之一是在社交媒体平台进行链接分享。由于这些平台对字符数量有限制&#xff0c;长网址可…

旋转花键有哪些优缺点?

旋转花键是在花键外筒的外径上装上专用的轴承外套&#xff0c;使之运转动作&#xff0c;适用于水平多关节机械手臂&#xff08;SCARA&#xff09;、产业用机器人、自动装载机、镭射加工机、搬送装置、机械加工中心的ATC装置等各项设备。 目前&#xff0c;旋转花键的应用越来越普…

redis 哨兵

文章目录 前言主从复制的问题怎么人工恢复故障主节点 Redis Setinel 架构使用 docker 来配置哨兵结构安装 docker编排 redis 主从节点编排 redis 哨兵节点 观察哨兵模式的作用主从切换的具体流程小结 前言 redis 主从复制模式下, 一旦主节点出现故障, 不能提供服务的时候, 就需…

刷题之Leetcode283题(超级详细)

283.移动零 283. 移动零https://leetcode.cn/problems/move-zeroes/ 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nu…

第十讲 Query Execution Part 1

1 处理模型【Processing Model】 DBMS 的处理模型【Processing Model】定义了系统如何执行【execute】查询计划【Query Plan】。 针对不同的工作负载进行不同的权衡。 方法1&#xff1a;迭代器模型【Iterator Model】 方法2&#xff1a;物化模型【Materialization Model】 方…

创建和启动线程

概述 Java语言的JVM允许程序运行多个线程&#xff0c;使用java.lang.Thread类代表线程&#xff0c;所有的线程对象都必须是Thread类或其子类的实例。 Thread类的特性 每个线程都是通过某个特定Thread对象的run()方法来完成操作的&#xff0c;因此把run()方法体称为线程执行体。…

数据结构之堆底层实现的循序渐进

题外话 把没写的都补回来! 正题 堆 概念 堆是一棵完全二叉树&#xff0c;因此可以层序的规则采用顺序的方式来高效存储&#xff0c; 大根堆:指根结点比左右孩子都大的堆 小根堆:指根结点比左右孩子都小的堆 性质 1.堆中某个节点的值总是不大于或不小于其父节点的值 2…

CCIE-14-MPLS_and_BGP

目录 实验条件网络拓朴 环境配置开始配置配置MPLSR1访问R6检测结果R6访问R1检测结果 实验条件 网络拓朴 环境配置 在我的资源里可以下载&#xff08;就在这篇文章的开头也可以下载&#xff09; 开始配置 R1<->R2&#xff1a;EBGP R2<->R5&#xff1a;IBGP&…

蓝桥杯备考3

P8196 [传智杯 #4 决赛] 三元组 题目描述 给定一个长度为 n 的数列 a&#xff0c;对于一个有序整数三元组 (i,j,k)&#xff0c;若其满足 1≤i≤j≤k≤n 并且&#xff0c;则我们称这个三元组是「传智的」。 现在请你计算&#xff0c;有多少有序整数三元组是传智的。 输入格式…

小米手机澎湃OS,不Root查看电池健康

首先&#xff0c;在键盘拨号界面&#xff0c;输入*#*#284#*#*&#xff0c;会调用问题反馈APP来生成当前系统的故障日志&#xff0c;如果提示你需要授权什么就点确认 稍等几分钟&#xff0c;会得到一个压缩包&#xff0c;保存在目录MIUI/debug_log下 这里为了方便&#xff0c;我…

肖恩带你学C语言·文件操作(上)

1. 为什么使用文件 如果没有文件&#xff0c;我们写的程序的数据是存储在电脑的内存中&#xff0c;如果程序退出&#xff0c;内存回收&#xff0c;数据就丢失了&#xff0c;等再次运行程序&#xff0c;是看不到上次程序的数据的&#xff0c;如果要将数据进行持久化的保存&…

打造自然资源“一张图”管理平台,推动生态文明建设新篇章

在信息化时代的浪潮下&#xff0c;自然资源管理正面临着前所未有的挑战与机遇。传统的资源管理模式已经难以满足当前生态环境保护与经济发展的双重需求&#xff0c;我们需要一个全新的平台&#xff0c;一个集信息集成、智能分析、决策支持于一体的自然资源“一张图”管理平台。…

数据可视化-地图可视化-Python

师从黑马程序员 基础地图使用 基础地图演示 视觉映射器 具体颜色对应的代码可以在http://www.ab173.com/中查询RGB颜色查询对照表 from pyecharts.charts import Map from pyecharts.options import VisualMapOpts#准备地图对象 mapMap() #准备数据 data[("北京",…