Main Takeaway
使用CubeMX遇到使用makefile的时候,且HW要求使用CMake来编译项目,特此记录学习make,makefile and CMake——仅基础知识以看懂为主
此章学习CMake
简介
CMake是服务于将源代码转换成可执行的文件的工具。本身是一个工具集由五个可执行的程序组成:cmake、ctest、cpack、cmake-gui和ccmake
- cmake用于在构建项目的第一步,进行项目的配置、生成和构建项目的主要可执行文件的工作。
- ctest用于运行和报告测试结果的测试驱动程序。
- cpack用来生成安装程序和源包的打包程序。
- cmake-gui是 cmake 的图形界面。
- ccmake(注意ccmake比cmake多了一个c)也是用于图形界面,与cmake-gui不同的地方在于ccmake是基于控制台(terminal)的图形界面
CMake就是“协调器”——它清楚哪些步骤需要完成,理解最终目标是什么,以及忙碌于为构建工作找到合适的“工人”和“材料”。这个过程有三个阶段:配置、生成、构建阶段
设计初衷
设计、编码和测试。在更改了代码之后,工具将以智能的、快速的、可扩展的方式,在不同的操作系统和环境中以相同的方式工作。支持多个集成开发环境(IDE) 和持续集成(CI) 流水,这些流水在更改提交到代码库后,就能对代码进行测试。
两个概念需要区分:
和 ,分别对应构建树和源码树;构建树是目标/输出目录的路径,源码树是源码所在的路径。 基本语法
注释
CMake使用#进行行注释,可以放在任何位置。
CMake使用#[[ ]]形式进行块注释。变量
三大类:普通变量、缓存变量、环境变量。注意作用域
- ${} 用于引用普通变量或缓存变量。
- $ENV{} 用于引用环境变量。
- $CACHE{} 用于引用缓存变量。
基本的变量操作指令是set()(),建议在变量名中只使用字母数字字符、减号(-) 和下划线(_),具体的使用方式为在设置变量时只需使用set()并提供名称和值,要取消变量的设置时可以使用unset()并提供名称。
1
2 set(Mystring "Text1")
message($(Mystring))Tips:${}告诉CMake遍历作用域堆栈,尝试将${MyString1}替换为具体的值供message命令打印出来
${} 语法进行变量的求值和展开时,是由内而外执行的
作用域
理解“副本“的概念是关键,当创建嵌套(子)作用域时,CMake只需用来自当前(父)作用域的所有变量的副本填充,后续嵌套(子)作用域命令将影响这些副本。但若完成了嵌套(子)作用域的执行,所有的副本都会删除,而原始的父作用域将恢复,嵌套作用域中操作的变量将不会更新到父作用域中
不管是针对CMake函数作用域还是CMake目录作用域,其都有两个特点:向下有效和数值拷贝生成副本,在不使用特殊关键字的情况下,嵌套(子)作用域针对普通变量的修改不会影响到父作用域。
针对变量,普通变量仅仅有效于当前作用域,而缓存变量和环境变量可以在全局作用域中使用。
环境变量
使用set(ENV{
} ) 指令用以声明,使用unset(ENV{ })来清除某一个环境变量 Tips:设定或读取环境变量时,都通过ENV前缀来访问环境变量,读取环境变量值时,要在ENV前加$符号;但if判断是否定义时,不用加$符号
设定环境变量后,其作用域只影响当前CMake进程,也就是说环境变量设定后是整个CMake进程的作用域都可用,但是不会影响CMake进程外的整个系统环境。
环境变量在启动CMake进程后会基于CMake在配置阶段中收集的信息在CMake生成阶段生成环境变量的副本,该副本会在单一的、全局的作用域中可用。即,若使用ENV 变量作为指令的参数,这些值将在CMake生成构建系统期间(配置阶段+生成阶段)插入,并且会将其嵌入到构建树中,在构建系统完成后即使再通过脚本或者命令行修改环境变量ENV{
}的value,在构建阶段时该环境变量值也不会更新成新的value(因为在构建系统中保存的是之前环境变量的副本) 缓存变量
缓存变量可以通过$CACHE{
} 语法来引用,而设置一个缓存变量使用set( CACHE [FORCE])指令
- FORCE:强制选项,强制修改变量值。——建议一直加上
与环境变量不同的是,缓存变量是CMake进程在配置阶段收集相关信息后存储在在构建树中的CMakeCache.txt 文件中的变量,缓存变量不可像环境变量中在脚本使用但是可以通过cmake-gui或者ccmake来声明。
Cache Variable缓存变量相当于一个全局变量,在同一个CMake工程中任何地方都可以使用,比如父目录,子目录等
控制结构
条件块
1
2
3
4
5
6
7 1、 if(<condition>)
2、 <commands>
3、 elseif(<condition>) # optional block, can be repeated
4、 <commands>
5、 else() # optional block
6、 <commands>
7、 endif()循环
1
2
3 1、 while(<condition>)
2、 <commands>
3、 endwhile()
1
2
3 1、 foreach(<loop_var> RANGE <min> <max> [<step>])
2、 <commands>
3、 endforeach()Tips:
和 参数变量可选择配置,默认的话从0开始,min和max都必须是非负整数,在RANGE中max和min都是包括在循环内部的
1
2 1、 foreach(<loop_variable> IN [LISTS <lists>] [ITEMS <items>])
# foreach(VAR 1 2 3 e f)eg:
1
2
3
4
5 1、 set(MY_LIST 1 2 3)
2、 foreach(VAR IN LISTS MY_LIST ITEMS e f)
3、 message(${VAR})
4、 endforeach()
# 1 2 3 e f压缩列表(ZIP_LISTS),
1 1、 foreach(<loop_var>... IN ZIP_LISTS <lists>)在压缩列表中CMake 将为每个提供的列表创建一个num_
变量,用每个列表中的项填充该变量。同时,除了使用CMake自动创建的num_ 变量,用户也可以自定义传递多个 变量名(每个列表一个),每个列表将使用单独的变量来存储. 针对多个列表的压缩处理,前提条件是这些待处理的多个列表中的元素个数是相同的,若列表之间的项数不同,CMake 将不会为较短的列表定义变量。
定义指令
macro()
查找和替换指令
与函数相反,宏不会在调用堆栈上创建单独的条目。所以宏中调用return() 将比在函数中返回调用语句的级别高一级(若已经在顶层作用域中,可能会终止执行)。在调用者的变量作用域中工作
1
2
3
4
5
6 1、 //CMake中的宏
2、 macro(<name> [<argument> ])
3、 <commands>
4、 endmacro()
5、 //调用宏
6、 <name>("called value")宏中对变量进行修改会影响全局作用域中的变量,但在macro()内部该变量没有修改,因为传递给宏的参数没有视为真正的变量,而是作为常量查找并替换指令
function()
实际子例程调用
为本地变量创建一个单独的作用域,这与macro() 命令不同,后者在调用者的变量作用域中工作,所以使用CMake的function需要注意变量的作用域问题。任何更改都将是函数的局部更改(除非指定了PARENT_SCOPE),不影响PARENT SCOPE。
实用指令
message():将文本打印到标准输出,并且CMake通过提供MODE 参数,可以自定义输出的样式
include():引用官方和CMake社区中已经配置好了的CMake模板,所谓的CMake模板就是将CMake代码划分到单独的.cmake文件中,以保持内容的有序和独立性。然后通过include()指令,从父列表文件引用
file():为了可以知道CMake 脚本可以做什么,CMake提供了一个可以快速浏览文件的操作命令:
1
2
3 1、 file(READ <filename> <out-var> [...])
2、 file({WRITE | APPEND} <filename> <content>...)
3、 file(DOWNLOAD <url> [<file>] [...])简而言之,file() 指令会以一种与系统无关的方式读取、写入和传输文件,并使用文件系统、文件锁、路径和存档
execute_process():有时需要使用系统中可用的工具,CMake提供execute_process()指令以用来运行其他进程,并收集它们的输出
1 1、 execute_process(COMMAND <cmd1> [<arguments>] [OPTIONS])简单的CMake构建
建议直接看原文!!!highly recommended
References
- 本文链接: http://example.com/2023/09/04/探CMake/
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!