组织结构
目录
目录结构:
.
├── CMakeLists.txt
├── sub_1
│ ├── CMakeLists.txt
│ ├── sub_1_sub_1
│ │ └── CMakeLists.txt
│ └── sub_1_sub_2
│ └── CMakeLists.txt
└── sub_2
└── CMakeLists.txt
上层目录通过add_subdirectory()添加子目录,如:
# CMakeLists.txt
add_subdirectory(sub_1)
add_subdirectory(sub_2)
# sub_1/CMakeLists.txt
add_subdirectory(sub_1_sub_1)
add_subdirectory(sub_1_sub_2)
脚本
用cmake语言编写并以.cmake结尾的文件。它可以用 cmake -P script.cmake
执行,它不会产生编译系统,不能定义任何目标。
不知道脚本是用来干什么的?
模块
<module>.cmake, 我很不理解,这不就是脚本文件吗?
在CMakeLists.txt中或是有cmake脚本中,都可以用include命令把module.cmake加载到当前环境中,类型c++的include语句。
通过设置CMAKE_MODULE_PATH 变量指模块的搜索路径。
语法
编码
3.2以上版本支持UTF8编码,这就够了!
还有一些其它元素的定义,直接看源文吧:https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#syntax
源文件
可以把它理解为c++中的.cpp文件
命令调用
可以把它理解为c++中的函数
command_name(arg1 arg2
arg3 ...)
参数之间不是用,
分隔而是‘space或\n’, 注意 space 可以是‘空格’或'\t'
其它的和c的函数没什么差别。
参数
括号参数
message(
[=[
[的第一个换行会被忽略(本应该有两个换行,但是只输出了一个)
这是多行叁数
多行参数中不可以引用变量 ${PROJECT_BINARY_DIR}
也没有转义\t(制表符不起作用),最后的换行不会被忽略
]=]
)
- [=[ 中的‘=’可以没有,也可以有多个
- 结尾的]=]必须与开头的[=[成对匹配
括号参数里的内容只能是字符串...
有引号的参数
message(
"
这是双引号 \"可以使用转换字符\"
也可以引和变量:${PROJECT_BINARY_DIR}
这个是续行符\
,接上一行
"
)
没有引号的参数
message(${PROJECT_BINARY_DIR}) # 引用变量
message(hello\tword) # 符串,这符串中不能包含'()#\',可以有转义序列
转义序列
escape_sequence ::= escape_identity | escape_encoded | escape_semicolon
escape_identity ::= '\' <match '[^A-Za-z0-9;]'># 编码转换比如要表示一个utf8的字符
escape_encoded ::= '\t' | '\r' | '\n'
escape_semicolon ::= '\;' # 不知道有什么作用
变量引用
${<variable>}
注释
单行注释
# This is a line comment.
message("First Argument\n" # This is a line comment :)
"Second Argument") # This is a line comment.
多行注释
#[=[
第一行
第二行
...
]=]
[=[xxxx]=]和括号参数的语法一样
控制结构
参考c/c++相信你很快就知道它们的含义有用法了
条件表达式语法
基本表达式
if(<常量>)
# [1 ON YES TRUE Y 非0的数字(包括浮点数)] -- true
# [ 0 OFF NO FALSE N IGNORE NOTFOUND 空串 xx-NOTFOUND] -- false
if(y) # 不区分大小写
message("show me")
endif()
if(<变量>)
set(MY_VAL OFF)
# MY_VAL = [ 0 OFF NO FALSE N IGNORE NOTFOUND 空串 xx-NOTFOUND] is false
# 其它为true (如果 MY_VAL没有定义也为false)
# 不支持 if(ENV{some_var}) 一直为false
if(MY_VAL)
message("show me")
endif()
if(<字符串>)
if("string") # 为false
endif()
# ON YES TRUE Y 为true
if("y")
message("show me")
endif()
逻辑表达式
if(NOT<condition>)
set(MY_VAL MY-NOTFOUND)
if(NOT MY_VAL)
message("show me")
endif()
if(<cond1> AND<cond2>)
set(MY_VAL MY-NOTFOUND)
if(NOT MY_VAL AND ON)
message("show me")
endif()
if(<cond1> OR<cond2>)
set(MY_VAL MY-NOTFOUND)
if(NOT MY_VAL OR OFF)
message("show me")
endif()
组合与嵌套
set(MY_VAL MY-NOTFOUND)
if(NOT (NOT (MY_VAL OR OFF) AND FALSE))
message("show me")
endif()
存在检测
if(COMMAND command-name)
如果命令名存在,返回true
if(COMMAND message)
message("show me")
endif()
if(TARGET target-name)
target-name是否存在
if(TARGET Tutorial)
message("show me")
endif()
if(DEFINED <name>|CACHE{<name>}|ENV{<name>})
变量是否有定义|缓存中是否有变量name|环境变量是否存在
if(DEFINED ENV{PATH})
message("show me")
endif()
需要注意:
- PATH 不能用小写,环境变量都要用大写??
- 宏参数不是变量
- if(DEFINED someName)不能直接判断是不有缓存变量someName,someName是缓存或非缓存变量它都返回true.
- if(DEFINED CACHE{someName})只能判断缓存中是否存在该变量
- 如果要两个(缓存和非缓存)都判断 : if(DEFINED someName AND NOT DEFINED CACHE{someName})
if(<variable|string> IN_LIST <variable>)
set(MY_LIST A B C D E)
set(V E)
if(V IN_LIST MY_LIST)
message("show me")
endif()
if("A" IN_LIST MY_LIST)
message("show me2")
endif()
文件操作
if(EXISTS path-to-file-or-directory)
文件或目录是否存在
set(p /home/luan/cmake_tutorial/cmake-3.24.1-tutorial-source/Step2/build)
if(EXISTS ${p})
message("show me")
endif()
# 可以使用相对路径
set(p Makefile)
if(EXISTS ${p})
message("show me")
endif()
if(file1 IS_NEWER_THAN file2)
file1 比file2 更新,如果两个文件的时间戳相同也返回true
if(myfile2 IS_NEWER_THAN myfile)
message("show me")
endif()
if(IS_DIRECTORY path-to-directory)
判断目录
if(IS_SYMLINK file-name)
给定的路径是否为软链接(或者快捷方式)
if(IS_ABSOLUTE path)
是否为绝对路径
比较
if(<variable|string> MATCHES regex)
字符串或变量的值是与给定的正则匹配
if(" my book name" MATCHES "^ my")
message("show me")
endif()
if(<variable|string> <OP><variable|string>)
OP可以取以下值:
- LESS
- GREATER
- EQUAL
- LESS_EQUAL
- GREATER_EQUAL
- STRLESS
- STRGREATER
- STREQUAL
- STRLESS_EQUAL
- STRGREATER_EQUAL
两大类,数值和字符串比较
# 指定 16进制也是可以的
if("100" EQUAL "0x64")
message("show me")
endif()
# 字符串比较,不会相同...
if("100" STREQUAL "0x64")
message("show me")
endif()
版本比较
if(<variable|string> <OP><variable|string>)
OP的取值:
- VERSION_LESS
- VERSION_GREATER
- VERSION_EQUAL
- VERSION_LESS_EQUAL
- VERSION_GREATER_EQUAL
if("1.0" VERSION_LESS "1.1")
message("show me")
endif()
路径的比较
if(<variable|string> PATH_EQUAL <variable|string>)
这个要求的版本比较高:New in version 3.24.
与字符 STREQUAL相比,它把多个路径分割符处理成1个
# tue
if ("/a/b//c" PATH_EQUAL "/a/b/c")
message("show me")
endif()
变量拓展
set(var1 OFF)
set(var2 "var1")
# if(${var2}) --> 先执行取值操作 --> if(var1)
if(${var2}) # 相当于 if(var1) 所以它是假的
message("true")
else()
message("false")
endif()
if(var2) # var2 = "var1" 不是false常数,所以它是返回真
message("true")
else()
message("false")
endif()
<variable|string>语法解析:
- 先测试参数是否是一个定义的变量,如果是先求值,否则直接应用原始值
- NOT OR AND 先测试参数是否布尔常量,如果不是则假定它是一个变量
条件块
if()/elseif()/else()/endif()
if(<condition>)
<commands>
elseif(<condition>) # optional block, can be repeated
<commands>
else() # optional block
<commands>
endif()
循环
foreach()/endforeach()
foreach(<loop_var> <items>)
set(VAR a b c d e fg)
foreach(item ${VAR})
message(${item})
endforeach()
message("\n")
foreach(item 1 2 3 4 5 6)
message(${item})
endforeach()
foreach(<loop_var> RANGE <stop>)
foreach(item RANGE 5) # 从 0 开始
message(${item})
endforeach()
foreach(<loop_var> RANGE <start> <stop> [<step>])
foreach(item RANGE 0 10 2) # start 0 stop 10 step 2
message(${item})
endforeach()
foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])
foreach(item IN ITEMS 1 2 3 4 5 6 7 8)
message(${item})
endforeach()
set(A 0;1)
set(B 2 3)
set(C "4 5")
set(D 6;7 8)
set(E "")
foreach(X IN LISTS A B C D E)
message(STATUS "X=${X}")
endforeach()
foreach(<loop_var>... IN ZIP_LISTS <lists>)
New in version 3.17(我的机子中,cmake版本是3.16,比较尴尬...)
list(APPEND English one two three four)
list(APPEND Bahasa satu dua tiga)
foreach(num IN ZIP_LISTS English Bahasa)
message(STATUS "num_0=${num_0}, num_1=${num_1}")
endforeach()
foreach(en ba IN ZIP_LISTS English Bahasa)
message(STATUS "en=${en}, ba=${ba}")
endforeach()
-- num_0=two, num_1=dua
-- num_0=three, num_1=tiga
-- num_0=four, num_1=
-- en=one, ba=satu
-- en=two, ba=dua
-- en=three, ba=tiga
-- en=four, ba=
和python的zip函数差不多一个功能...
while()/endwhile()
set(COUNT 0)
while(COUNT LESS 10)
math(EXPR COUNT "${COUNT} + 1" OUTPUT_FORMAT DECIMAL)
message(${COUNT})
endwhile()
突然间发现,cmake没有数学算的功能(它是通过math来实现的)...
break()/continue()
很遗憾,它们只能嵌套到while或foreach中
注定不能像c/c++的break或continue那样灵活...
数学运算
math(EXPR <variable> "<expression>" [OUTPUT_FORMAT <format>])
- variable 是要输出的变量(保存结果的变量)
- expression 数学运算表达式 如 5 + 2 ..., 引号表达式内可以引用变量的值 "${var} + 1" 也是可以的
set(COUNT 0)
while(COUNT LESS 10)
math(EXPR COUNT "${COUNT} + 1" OUTPUT_FORMAT DECIMAL)
message(${COUNT})
endwhile()
- format 可取值: DECIMAL , HEXADECIMAL
自定义指令
还没有用过,先把链接贴出来,如果有需要再来看...
macro()/endmacro()
https://cmake.org/cmake/help/latest/command/macro.html#command:macro
function()/endfunction()
https://cmake.org/cmake/help/latest/command/function.html#command:function
先把一些操作的声明贴上来,例过段时间再回来补上...
变量
set(<variable> <value>... [PARENT_SCOPE])
所有的变量类型都是字符串,但是有一些命令可以把字符解释为数值。
- set(varName value) -- 定义变量
- unset(varName) -- 取消变量的定义
- cmake中一些被保留的变量名(大小字不敏感)
1. CMAKE_
2. _CMAKE_
3. _开始的或者是cmake命令
变量的作用域
目录作用域
# CMakeLists.txt
set(MY_NAME hello)
...
# 报错了
# message(${YOU_NAME})
# MathFunctions/CMakeLists.txt
message(${MY_NAME}) # 可以输出
set(YOU_NAME word)
结论: 子目录可以引用父目录的变量,而父目录不能使用子目录中定义的变量
环境变量
引用语法:
$ENV{<variable>}
也可以用set/unset对它进行操作,对于环境变量应该是引用居多...
列表
通过set操作
set(srcs a.c b.c c.c) # sets "srcs" to "a.c;b.c;c.c"
set(x a "b;c") # sets "x" to "a;b;c", not "a;b\;c"
通过list操作
https://cmake.org/cmake/help/latest/command/list.html
读操作
list(LENGTH <list> <output variable>)
list(LENGTH <list> <output variable>)
list(GET <list> <element index>
[<element index> ...]
<output variable>)
list(JOIN <list> <glue> <output variable>)
list(SUBLIST <list> <begin> <length> <output variable>)
查找搜索
list(FIND <list> <value> <output variable>)
修改
list(APPEND <list> [<element> ...])
list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
list(INSERT <list> <element_index> <element> [<element> ...])
list(POP_BACK <list> [<out-var>...])
list(POP_FRONT <list> [<out-var>...])
list(PREPEND <list> [<element> ...])
list(REMOVE_ITEM <list> <value> [<value> ...])
list(REMOVE_AT <list> <index> [<index> ...])
list(REMOVE_DUPLICATES <list>)
list(TRANSFORM <list> <ACTION> [<SELECTOR>]
[OUTPUT_VARIABLE <output variable>])
list(TRANSFORM <list> <APPEND|PREPEND> <value> ...)
list(TRANSFORM <list> STRIP ...)
list(TRANSFORM <list> GENEX_STRIP ...)
list(TRANSFORM <list> REPLACE <regular_expression>
<replace_expression> ...)
list(TRANSFORM <list> <ACTION> AT <index> [<index> ...] ...)
list(TRANSFORM <list> <ACTION> FOR <start> <stop> [<step>] ...)
list(TRANSFORM <list> <ACTION> REGEX <regular_expression> ...)
排序
list(REVERSE <list>)
list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])