CMake语法


概述

  首先我们都知道Makefile带来的好处就是自动化编译,但是如果跨平台那样Makefile就都需要重写。而 cmake 就是针对这个问题所诞生,允许开发者编写一种与平台无关的CMakeLists.txt文件来制定整个工程的编译流程,再根据具体的编译平台,生成本地化的Makefile和工程文件,最后执行make编译。
在这里插入图片描述


一、示例引入

  新建一个main.c文件,内容如下:

#include "stdio.h"
int main()
{
	printf("hello\n");
	return 0;
}

  新建一个CMakeLists.txt文件,内容如下:

project(HELLO)
add_executable(hello ./main.c)

  新建一个文件夹build用来存放CMake编译文件,进入文件夹输入cmake ../命令执行CMake
在这里插入图片描述
  此时就可以在build文件夹里生成Makefile文件,输入make运行Makefile
在这里插入图片描述

二、语法规则

  1. 注释
      在 CMakeLists.txt 文件中,使用#号进行单行注释,或者#[[ ]]块注释
#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)
  1. 基本格式
[command](para1,para2,...)

  常用命令:

command说明
add_executable可执行程序目标
add_library库文件目标
add_subdirectory去指定目录中寻找新的CMakeLists.txt文件
aux_source_directory收集目录中的文件名并赋值给变量
cmake_minimum_required设置 cmake 的最低版本号要求
get_target_property获取目标的属性
include_directories设置所有目标头文件的搜索路径,相当于 gcc 的-I 选项
link_directories设置所有目标库文件的搜索路径,相当于 gcc 的-L 选项
link_libraries设置所有目标需要链接的库
list列表相关的操作
message用于打印、输出信息
project设置工程名字
set设置变量
set_target_properties设置目标属性
target_include_directories设置指定目标头文件的搜索路径
target_link_libraries设置指定目标库文件的搜索路径
target_sources设置指定目标所需的源文件

  具体含义可以查看官方文档:
https://cmake.org/cmake/help/v3.5/manual/cmake-commands.7.html

  1. 双引号
      命令中多个参数之间使用空格进行分隔,而 cmake会将双引号引起来的内容作为一个整体,当它当成一个参数,假如你的参数中有空格(空格是参数的一部分),那么就可以使用双引号,如下所示:
message(Hello World)
message("Hello World")

  第一个message命令传入了两个参数,在第一个message命令中,打印信息时,会将两个独立的字符串Hello和World都打印出来,而且 World 会紧跟在Hello 之后,输出如下:

HelloWorld

  而第二个 message 命令只传入一个参数,空格为整体,输出如下:

Hello World
  1. 宏定义
      cmake提供了定义宏的方法,cmake中函数function和宏定义macro在某种程度上来说是一样的,都是创建一段有名字的代码可以在后面被调用,还可以传参数。通过macro命令定义宏,如下所示:
macro(<name> [arg1 [arg2 [arg3 ...]]])
	COMMAND1(ARGS ...)
	COMMAND2(ARGS ...)
	...
endmacro(<name>)

  测试如下:

# macro 宏
macro(abc arg1 arg2)
	if(DEFINED ARGC)
		message(true)
	else()
		message(false)
	endif()
endmacro()
# function 函数
function(xyz arg1 arg2)
	if(DEFINED ARGC)
		message(true)
	else()
		message(false)
	endif()
endfunction()
# 调用宏
abc(A B C D)
# 调用函数
xyz(A B C D)

在这里插入图片描述
  以上输出说明宏定义只是字符串代替,而不是变量

三、变量

  1. 变量的使用
      在 CMakeLists.txt 文件中可以使用变量,使用set命令可以对变量进行设置,通过${}方式来引用变量,如下:
#设置变量 MY_VAL
set(MY_VAL "Hello World!")
#引用变量 MY_VAL
message(${MY_VAL})

  可以看出定义变量的格式是变量名+变量值,以下链接可以查到所有内置变量及其介绍:
https://cmake.org/cmake/help/v3.5/manual/cmake-variables.7.html

  1. 提供信息的变量:
      可以用message命令输出信息
变量说明
PROJECT_SOURCE_DIR工程顶层目录,也就是顶层 CMakeLists.txt 源码所在目录
PROJECT_BINARY_DIR工 程 BINARY_DIR , 也 就 是 顶 层 CMakeLists.txt 源码的BINARY_DIR
CMAKE_SOURCE_DIR与 PROJECT_SOURCE_DIR 等价
CMAKE_BINARY_DIR与 PROJECT_BINARY_DIR 等价
CMAKE_CURRENT_SOURCE_DIR当前源码所在路径
CMAKE_CURRENT_BINARY_DIR当前源码的 BINARY_DIR
CMAKE_MAJOR_VERSIONcmake 的主版本号
CMAKE_MINOR_VERSIONcmake 的次版本号
CMAKE_VERSIONcmake 的版本号(主+次+修订)
PROJECT_VERSION_MAJOR工程的主版本号
PROJECT_VERSION_MINOR工程的次版本号
PROJECT_VERSION工程的版本号
CMAKE_PROJECT_NAME工程的名字
PROJECT_NAME工程名,与 CMAKE_PROJECT_NAME 等价

  CMakeLists.txt 文件内容如下:

cmake_minimum_required("VERSION" "3.5")
project(HELLO)
message(${PROJECT_SOURCE_DIR})
message(${PROJECT_BINARY_DIR})

  这样进行cmake后就会打印相应信息:
在这里插入图片描述

  1. 描述系统的变量
变量说明
CMAKE_HOST_SYSTEM_NAME运行 cmake 的操作系统的名称(其实就是 uname -s)
CMAKE_HOST_SYSTEM_PROCESSOR运行 cmake 的操作系统的处理器名称(uname -p)
CMAKE_HOST_SYSTEM运行 cmake 的操作系统(复合信息)
CMAKE_HOST_SYSTEM_VERSION运行 cmake 的操作系统的版本号(uname -r)
CMAKE_HOST_UNIX如果运行 cmake 的操作系统是 UNIX 和类 UNIX,则该变量为 true,否则是空值
CMAKE_HOST_WIN32如果运行 cmake 的操作系统是 Windows,则该变量为 true,否则是空值
CMAKE_SYSTEM_NAME目标主机操作系统的名称
CMAKE_SYSTEM_PROCESSOR目标主机的处理器名称
CMAKE_SYSTEM目标主机的操作系统(复合信息)
CMAKE_SYSTEM_VERSION目标主机操作系统的版本号
ENV用于访问环境变量
UNIX与 CMAKE_HOST_UNIX 等价
WIN32与 CMAKE_HOST_WIN32 等价
  1. 改变行为的变量
      可以用set命令进行行为控制
变量说明
BUILD_SHARED_LIBS控制 cmake 是否生成动态库
CMAKE_BUILD_TYPE指定工程的构建类型,release 或 debug
CMAKE_SYSROOT对应编译器的在–sysroot 选项
CMAKE_IGNORE_PATH设置被 find_xxx 命令忽略的目录列表
CMAKE_INCLUDE_PATH为 find_file()和 find_path()命令指定搜索路径的目录列表
CMAKE_INCLUDE_DIRECTORIES_BEFORE用于控制 include_directories()命令的行为
CMAKE_LIBRARY_PATH指定 find_library()命令的搜索路径的目录列表
CMAKE_MODULE_PATH指定要由 include()或 find_package()命令加载的CMake 模块的搜索路径的目录列表
CMAKE_PROGRAM_PATH指定 find_program()命令的搜索路径的目录列表
  1. 控制编译的变量
变量说明
EXECUTABLE_OUTPUT_PATH可执行程序的输出路径
LIBRARY_OUTPUT_PATH库文件的输出路径
  1. 使用list进行字符串拼接
list(APPEND <list> [<element> ...])
# APPEND表示进行数据追加,后边的参数和set就一样
  1. 变量作用域
      众所周知,Linux的目录是树状结构,所以嵌套的 CMake 也是一个树状结构,最顶层的 CMakeLists.txt 是根节点,其次都是子节点。因此,我们需要了解一些关于 CMakeLists.txt 文件变量作用域的一些信息:
  • 根节点CMakeLists.txt中的变量全局有效
  • 父节点CMakeLists.txt中的变量可以在子节点中使用
  • 子节点CMakeLists.txt中的变量只能在当前节点中使用

四、控制结构

4.1 条件判断

  在cmake中可以使用条件判断,条件判断形式如下:

if(expression)
	# then section.
	command1(args ...)
	command2(args ...)
	...
elseif(expression2)
	# elseif section.
	command1(args ...)
	command2(args ...)
	...
else(expression)
	# else section.
	command1(args ...)
	command2(args ...)
	...
endif(expression)

  else和endif括号中的expression可写可不写,如果写了,就必须和if中的expression一致。expression就是一个进行判断的表达式,表达式对照表如下:

表达式truefalse说明
constant如果constant为1、ON、YES、TRUE、Y 或非零数,则为真如果constant为0、OFF 、 NO 、FALSE 、 N 、IGNORE 、NOTFOUND、空字符串或以后缀-NOTFOUND 结尾,则为 False。布尔值大小写不敏感;如果与这些常量都不匹配,则将其视为变量或字符串
variable|string已经定义并且不是false的变量未定义或者是false的变量变量就是字符串
NOT expressionexpression为falseexpression为true
expr1 AND expr2expr1 和 expr2 同时为trueexpr1 和 expr2 至少有一个为 false
expr1 OR expr2expr1 和 expr2 至少有一个为 trueexpr1 和 expr2 都是 false
COMMAND namename 是一个已经定义的命令、宏或者函数name 未定义
TARGET namename 是add_executable() 、add_library() 或add_custom_target() 定义的目标name 未定义
TEST namename 是由 add_test()命令创建的现有测试名称name 未创建
EXISTS pathpath 指定的文件或目录存在path 指定的文件或目录不存在仅适用于完整路径
IS_DIRECTORY pathpath 指定的路径为目录path 指定的路径不为目录仅适用于完整路径
IS_SYMLINK pathpath 为符号链接path 不是符号链接仅适用于完整路径
IS_ABSOLUTE pathpath 为绝对路径path 不是绝对路径
variable|stringMATCHES regexvariable 与正则表达式regex 匹配成功variable 与正则表达式 regex 匹配失败
variable|string IN_LIST variable右边列表中包含左边的元素右边列表中不含左边的元素
DEFINED variable如果给定的变量已定义,则为真。如果给定的变量未定义只要变量已经被设置,它是真还是假并不重要。(注意宏不是变量。)
variable|string LESS variable|string如果给定的字符串或变量的值是有效数字且小于右侧的数字,则为真。左侧的数字大于或等于右侧的数字
variable|string GREATER variable|string如果给定的字符串或变量的值是有效数字且大于右侧的数字,则为真。左侧的数字小于或等于右侧的数字
variable|string EQUAL variable|string如果给定的字符串或变量的值是有效数字并且等于右侧的值,则为真左侧的数字不等于右侧的数字

  上表中只是列出其中一部分表达式 ,还有其它一些表达式见以下链接:
https://cmake.org/cmake/help/v3.5/command/if.html

if(NOT GG)
	message(true)
else()
	message(false)
endif()
#输出为:true。因为GG没定义

4.2 循环语句

4.2.1 foreach循环

  foreach 循环的基本用法如下所示:

foreach(loop_var arg1 arg2 ...)
	command1(args ...)
	command2(args ...)
	...
endforeach(loop_var)

  endforeach 括号中的loop_var可写可不写,如果写了,就必须和 foreach 中的loop_var一致。参数 loop_var 是一个循环变量,循环过程中会将参数列表中的变量依次赋值给他,类似于C语言 for 循环中经常使用的变量 i。

  1. RANGE关键字
foreach(loop_var RANGE stop)
foreach(loop_var RANGE start stop [step])
  • 对于第一种方式,循环会从 0 到指定的数字 stop,包含 stop,stop 不能为负数。
  • 而对于第二种,循环从指定的数字 start 开始到 stop 结束,步长为 step,不过 step 参数是一个可选参数,如果不指定,默认 step=1;三个参数都不能为负数,而且 stop 不能比 start 小。
foreach(loop_var RANGE 1 4 1)
	message("${loop_var}")
endforeach()

在这里插入图片描述

  1. IN关键字
foreach(loop_var IN [LISTS [list1 [...]]] [ITEMS [item1 [...]]])
# foreach 循环测试
set(my_list A B C D)
foreach(loop_var IN LISTS my_list)
	message("${loop_var}")
endforeach()

在这里插入图片描述

4.2.2 while循环

while(condition)
	command1(args ...)
	command1(args ...)
	...
endwhile(condition)

  endwhile括号中的condition可写可不写,如果写了,就必须和while中的 condition 一致。cmake中while循环的含义与C语言中while循环的含义相同,但条件 condition 为真时,执行循环体中的命令,而条件condition的语法形式与if条件判断中的语法形式相同。

4.2.3 break、continue

  • break()命令用于跳出循环,和在 C 语言中的作用是一样的
set(loop_var 10)
while(loop_var GREATER 0) 	#loop_var>0 时 执行循环体
	message("${loop_var}")
	if(loop_var LESS 6) 	#当 loop_var 小于 6message("break")
		break() #跳出循环
	endif()
	math(EXPR loop_var "${loop_var} - 1")	#loop_var--
endwhile()

在这里插入图片描述

  • continue()命令用于结束本次循环,执行下一次循环
# 打印所有偶数
set(loop_var 10)
while(loop_var GREATER 0) #loop_var>0 时 执行循环体
	math(EXPR var "${loop_var} % 2") #求余
	
	if(var EQUAL 0) #如果 var=0,表示它是偶数
		message("${loop_var}") #打印这个偶数
		math(EXPR loop_var "${loop_var} - 1")#loop_var--
		continue() # 执行下一次循环
	endif()
	
	math(EXPR loop_var "${loop_var} - 1")#loop_var--
endwhile()

在这里插入图片描述

五、函数

  1. 定义函数
      cmake 提供了function命令用于定义一个函数,使用方法如下所示:
function(<name> [arg1 [arg2 [arg3 ...]]])
	command1(args ...)
	command2(args ...)
	...
endfunction(<name>)

  endfunction括号中的name可写可不写,如果写了,就必须和function括号中的name一致。第一个参数name表示函数的名字,arg1、arg2…表示传递给函数的参数。调用函数的方法其实就跟使用命令一样

  1. 使用return命令
      在function函数中也可以使用C语言中的return语句退出函数,如下所示:
# 函数名: xyz
function(xyz)
	message(Hello)
	return() # 退出函数
	message(World)
endfunction()
# 调用函数
xyz()

在这里插入图片描述

  1. 可变参函数
      在cmake中,调用函数时实际传入的参数个数不需要等于函数定义的参数个数(甚至函数定义时,参数个数为0),但是实际传入的参数个数必须大于或等于函数定义的参数个数,如下所示:
# 函数名: xyz
function(xyz arg1)
	message(${arg1})
endfunction()
# 调用函数
xyz(Hello World China)
  1. 函数的内部变量
      所谓函数的内部变量,指的就是在函数内部使用的内置变量:
函数中的内部变量说明
ARGVXX是一个数字,譬如ARGV0、ARGV1、ARGV2、ARGV3…,这些变量表示函数的参数,ARGV0为第一个参数、ARGV1位第二个参数,依次类推!
ARGV实际调用时传入的参数会存放在 ARGV 变量中(如果是多个参数,那它就是一个参数列表)
ARGN假如定义函数时参数为 2 个,实际调用时传入了4个,则ARGN存放了剩下的2个参数(如果是多个参数,那它也是一个参数列表)
ARGC调用函数时,实际传入的参数个数
# 函数名: xyz
function(xyz arg1 arg2)
	message("ARGC: ${ARGC}")
	message("ARGV: ${ARGV}")
	message("ARGN: ${ARGN}")
	message("ARGV0: ${ARGV0}")
	message("ARGV1: ${ARGV1}")
	# 循环打印出各个参数
	set(i 0)
	foreach(loop ${ARGV})
		message("arg${i}: " ${loop})
		math(EXPR i "${i} + 1")
	endforeach()
endfunction()
# 调用函数
xyz(A B C D E F G)

在这里插入图片描述

  1. 函数的作用域
      使用 function()定义的函数,我们需要对它的使用范围进行一个简单地了解,譬如有如下工程目录结构:
├── build
├── CMakeLists.txt	#顶层
├── hello
	├── CMakeLists.txt	#子源码

  如果我们在子层定义一个函数,在顶层可以调用;反之亦可

六、文件操作

  使用file命令进行文件操作

  1. 写文件
file(WRITE <filename> <content>...)		#给定内容生成文件
file(APPEND <filename> <content>...)	#给定内容追加到文件末尾

#由内容生成文件的命令为:
file(GENERATE OUTPUT output-file
	<INPUT input-file|CONTENT content>
	[CONDITION expression])
  • output-file:指定输出文件名,可以带路径(绝对路径或相对路径);
  • INPUT input-file:指定输入文件,通过输入文件的内容来生成输出文件;
  • CONTENT content:指定内容,直接指定内容来生成输出文件;
  • CONDITION expression:如果表达式 expression 条件判断为真,则生成文件、否则不生成文件。

  同样,指定文件既可以使用相对路径、也可使用绝对路径,不过在这里,相对路径被解释为相对于当前源码的 BINARY_DIR 路径,而不是当前源码路径。

# 由前面生成的 wtest.txt 中的内容去生成 out1.txt 文件
file(GENERATE OUTPUT out1.txt INPUT "${PROJECT_SOURCE_DIR}/wtest.txt")

# 由指定的内容生成 out2.txt
file(GENERATE OUTPUT out2.txt CONTENT "This is the out2.txt file")

# 由指定的内容生成 out3.txt,加上条件控制,用户可根据实际情况
# 用表达式判断是否需要生成文件,这里只是演示,直接是 1
file(GENERATE OUTPUT out3.txt CONTENT "This is the out3.txt file" CONDITION 1)

在这里插入图片描述

  1. 读文件
# 字节读取
file(READ <filename> <variable> [OFFSET <offset>] [LIMIT <max-in>] [HEX])

  从名为filename的文件中读取内容并将其存储在variable中。可选择从给定的offset开始,最多读取max-in字节。HEX选项使数据转换为十六进制表示(对二进制数据有用)

# 以字符串形式读取
file(STRINGS <filename> <variable> [<options>...])

  这个命令专用于读取字符串,会将文件中的二进制数据将被忽略,回车符(\r, CR)字符被忽略。

  • filename:指定需要读取的文件,可使用绝对路径、也可使用相对路径,相对路径被解释为相对于当前源码路径。
  • variable:存放字符串的变量。
  • options:可选的参数,可选择 0 个、1 个或多个选项,这些选项包括:
参数作用
LENGTH_MAXIMUM <max-len>读取的字符串的最大长度;
LENGTH_MINIMUM <min-len>读取的字符串的最小长度;
LIMIT_COUNT <max-num>读取的行数;
LIMIT_INPUT <max-in>读取的字节数;
LIMIT_OUTPUT <max-out>存储到变量的限制字节数;
NEWLINE_CONSUME把换行符也考虑进去;
NO_HEX_CONVERSION除非提供此选项,否则 Intel Hex 和 Motorola S-record 文件在读取时会自动转换为二进制文件。
REGEX <regex>只读取符合正则表达式的行;
ENCODING <encoding-type>指定输入文件的编码格式,目前支持的编码有:UTF-8、UTF-16LE、UTF-16BE、UTF-32LE、UTF-32BE。
# 从 input.txt 文件读取字符串
file(STRINGS "${PROJECT_SOURCE_DIR}/input.txt" out_var)
message("${out_var}")
# 限定读取字符串的最大长度
file(STRINGS "${PROJECT_SOURCE_DIR}/input.txt" out_var LENGTH_MAXIMUM 5)
message("${out_var}")
# 限定读取字符串的最小长度
file(STRINGS "${PROJECT_SOURCE_DIR}/input.txt" out_var LENGTH_MINIMUM 4)
message("${out_var}")
# 限定读取行数
file(STRINGS "${PROJECT_SOURCE_DIR}/input.txt" out_var LIMIT_COUNT 3)
message("${out_var}")

在这里插入图片描述

  1. 文件重命名
# oldname 指的是原文件,newname 指的是重命名后的新文件
file(RENAME <oldname> <newname>)
  1. 删除文件.
file(REMOVE [<files>...])
file(REMOVE_RECURSE [<files>...])

  REMOVE选项将删除给定的文件,但不可以删除目录;而REMOVE_RECURSE选项将删除给定的文件或目录、以及非空目录

  1. 计算文件的 hash 值
file(<MD5|SHA1|SHA224|SHA256|SHA384|SHA512> <filename> <variable>)

  file()命令可以计算指定文件内容的加密散列(hash 值)并将其存储在变量中。MD5|SHA1|SHA224|SHA256|SHA384|SHA512 表示不同的计算 hash 的算法,必须要指定其中之一

七、环境配置

7.1 设置交叉编译

  默认情况下,cmake 会使用主机系统(运行 cmake 命令的操作系统)的编译器来编译我们的工程,但如果是编译其他系统的工程就需要用到交叉编译器。
  我们使用的交叉编译器如下:

arm-poky-linux-gnueabi-gcc #C 编译器
arm-poky-linux-gnueabi-g++ #C++编译器

  配置交叉编译器,文件名为arm-linux-setup.cmake

# 配置 ARM 交叉编译
set(CMAKE_SYSTEM_NAME Linux) #设置目标系统名字
set(CMAKE_SYSTEM_PROCESSOR arm) #设置目标处理器架构

# 指定编译器的 sysroot 路径
set(TOOLCHAIN_DIR /opt/fsl-imx-x11/4.1.15-2.1.0/sysroots)
set(CMAKE_SYSROOT ${TOOLCHAIN_DIR}/cortexa7hf-neon-poky-linux-gnueabi)

# 指定交叉编译器 arm-gcc 和 arm-g++
set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/armpoky-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/x86_64-pokysdk-linux/usr/bin/arm-poky-linuxgnueabi/arm-poky-linux-gnueabi-g++)

# 为编译器添加编译选项
set(CMAKE_C_FLAGS "-march=armv7ve -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7")
set(CMAKE_CXX_FLAGS "-march=armv7ve -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

  此时 CMakeLists.txt 文件内容如下(剔除了交叉编译的配置项):

# CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(HELLO)
add_executable(main main.c)

  此时工程目录结构如下所示:

├── arm-linux-setup.cmake
├── build
├── CMakeLists.txt
└── main.c

  指定配置文件,用以下命令执行cmake:

cmake -DCMAKE_TOOLCHAIN_FILE=cfg_file_path ../arm-linux-setup.cmake

  通过**-DCMAKE_TOOLCHAIN_FILE选项指定配置文件,-D**是 cmake 命令提供的一个选项,通过该选项可以创建一个缓存变量(缓存变量就是全局变量,在整个工程中都是生效的,会覆盖 CMakeLists.txt 源码中定义的同名变量 ),所以 -DCMAKE_TOOLCHAIN_FILE其实就是设置了缓存变量CMAKE_TOOLCHAIN_FILE,它的值就是“=”号后面的内容,cmake 会执行 CMAKE_TOOLCHAIN_FILE变量所指定的源文件,对交叉编译进行设置

在这里插入图片描述

注意:如果编译报错需要安装最新版本的CMake,地址为:
https://github.com/Kitware/CMake/releases

7.2 作用域

  1. 函数作用域
      当在函数内通过 set 将变量 var 与当前函数作用域绑定时,变量 var仅在函数作用域内有效,出了这个作用域,如果这个作用域外也有同名的变量 var,那么使用的将是域外同名变量 var;func1()内部调用 func2(),嵌套调用的函数 func2()内部如果也引用变量 var,那么该变量 var 应该是 func1()内部定义的变量,如果有的话;如果 func1()内部没有绑定变量 var,那么就会使用 func1()作用域外定义的变量 var,依次向外搜索。
      测试代码如下所示:
# 函数 xyz
function(xyz)
	message("函数内部")
	message("${ABC}")
	set(ABC "Hello China!")#设置变量 ABC
	message("${ABC}")
endfunction()

set(ABC "Hello World!")#定义变量 ABC
xyz() # 调用函数
message("函数外部")
message("${ABC}")

在这里插入图片描述
  可以看出函数内修改变量,离开函数后值不变。如果需要在函数内修改外部定义的变量,需在调用 set 命令时在参数列表末尾加上PARENT_SCOPE关键字

function(xyz)
	set(ABC "Hello China!" PARENT_SCOPE) #加上 PARENT_SCOPE
endfunction()

set(ABC "Hello World!")
xyz() # 调用函数
message("${ABC}")

  所以可以用PARENT_SCOPE来实现函数返回值

  1. 目录作用域(Directory Scope)
      子目录会将父目录的所有变量拷贝到当前 CMakeLists.txt 源码中,当前 CMakeLists.txt 中的变量的作用域仅在当前目录有效。目录作用域有两个特点:向下有效(上层作用域中定义的变量在下层作用域中是有效的),值拷贝。譬如目录结构如下所示:
├── CMakeLists.txt
└── sub_dir
	└── CMakeLists.txt

  父目录 CMakeLists.txt 文件内容如下:

cmake_minimum_required(VERSION 3.5)
project(TEST)
set(parent_var "Hello parent")
message("parent-<parent_var>: ${parent_var}")
add_subdirectory(sub_dir)
message("parent-<parent_var>: ${parent_var}")

  子源码 CMakeLists.txt 内容:

message("subdir-<parent_var>: ${parent_var}")
set(parent_var "Hello child")
message("变量修改之后")
message("subdir-<parent_var>: ${parent_var}")

在这里插入图片描述
  可以看出子源码变量的改变不会影响父源码的parent_var

  1. 全局作用域(Persistent Cache 持久缓存、缓存变量)
      缓存变量在整个 cmake 工程的编译生命周期内都有效,所以这些变量的作用域是全局范围的,工程内的其他任意目录都可以访问缓存变量,注意 cmake 是从上到下来解析 CMakeLists.txt 文件的。
      缓存变量可以通过 set 命令来定义,使用 set 命令时添加 CACHE 选项来实现;除此之外,还有其它多种方式可以定义缓存变量,譬如前面给大家介绍的 cmake -D 选项是经常用来定义缓存变量的方法,cmake -DXXX,就表示创建了一个名为 XXX 的全局变量

7.3 属性

  属性大概可以分为多种:全局属性、目录属性(源码属性)、目标属性以及其它一些分类。具体见下链接:
https://cmake.org/cmake/help/v3.5/manual/cmake-properties.7.html

  1. 目录属性
      目录属性其实就是CMakeLists.txt源码的属性
    在这里插入图片描述
属性作用
CACHE_VARIABLES当前目录中可用的缓存变量列表。
CLEAN_NO_CUSTOM如果设置为true以告诉Makefile Generators在make clean操作期间不要删除此目录的自定义命令的输出文件。
INCLUDE_DIRECTORIES此属性是目录的头文件搜索路径列表, 其实就是include_directories() 命令所添加的目录
LINK_DIRECTORIES此属性是目录的库文件搜索路径列表,其实就是 link_directories()命令所添加的目录
MACROS当期目录中可用的宏命令列表。
PARENT_DIRECTORY加载当前子目录的源目录
VARIABLES当前目录中定义的变量列表。只读属性、不可修改!
  1. 目标属性
      目标属性可通过get_target_propertyset_target_property命令获取或设置。
    在这里插入图片描述
属性作用
BINARY_DIR只读属性,定义目标的目录中 CMAKE_CURRENT_BINARY_DIR 变量的值。
SOURCE_DIR只读属性,定义目标的目录中 CMAKE_CURRENT_SOURCE_DIR 变量的值。
INCLUDE_DIRECTORIES目标的头文件搜索路径列表,target_include_directories()命令会将目录添加到 INCLUDE_DIRECTORIES列表中,INCLUDE_DIRECTORIES 会拷贝目录属性中的INCLUDE_DIRECTORIES 属性作为初始值。
INTERFACE_INCLUDE_DIRECTORIEStarget_include_directories()命令使用 PUBLIC 和 INTERFACE 关键字的值填充此属性。
INTERFACE_LINK_LIBRARIEStarget_link_libraries()命令使用 PUBLIC 和 INTERFACE 关键字的值填充此属性。
LIBRARY_OUTPUT_DIRECTORY、LIBRARY_OUTPUT_NAME、LINK_LIBRARIES目标的链接依赖库列表。
OUTPUT_NAME目标文件的输出名称。
TYPE目标的类型, 它 将 是 STATIC_LIBRARY 、 MODULE_LIBRARY 、 SHARED_LIBRARY 、INTERFACE_LIBRARY、EXECUTABLE 之一或内部目标类型之一。

八、补充

8.1 数学运算math

  cmake提供了一个命令用于实现数学运算功能,这个命令就是math,如下所示:

math(EXPR <output variable> <math expression>)

  math 命令中,第一个参数是一个固定的关键字EXPR;第二个参数是一个返回参数,将数学运算结果存放在这个变量中;而第三个参数则是一个数学运算表达式,支持的运算符包括:+(加)、-(减)、*(乘)、/(除)、%(求余)、|(按位或)、&(按位与)、^(按位异或)、~(按位取反)、<<(左移)、>>(右移)以及这些运算符的组合运算,它们的含义与 C 语言中相同。

math(EXPR out_var "100 + 100")	# 计算100 + 100
message("${out_var}")
  • 28
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

别问,问就是全会

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值