CMake也是有语法的,这里总结一些。CMake系列学习个人笔记:
message是CMake中重要的输出功能。
General messages
message([<mode>] "message text" ...)
在mode模式下,常用FATAL_ERROR
、STATUS
、WARNING
等关键字。
分支的应用十分广泛,可以用于判断值、比较大小、判断文件是否存在、版本比较、新旧比较等等。语法格式如下:
if(<condition>)
<commands>
elseif(<condition>) # optional block, can be repeated
<commands>
else() # optional block
<commands>
endif()
例子:
if(NOT <condition>)
if(<cond1> AND <cond2>)
if(<cond1> OR <cond2>)
if((condition) AND (condition OR (condition))) # 优先级
if(TARGET target-name) # 判断是否是内部已建立的目标
if(DEFINED <name>|CACHE{<name>}|ENV{<name>}) # 判断指定类型的变量是否声明
if(<variable|string> IN_LIST <variable>) # 判断变量是否在列表中
if(EXISTS path-to-file-or-directory) # 路径或文件名存在,必须是绝对完整路径,链接文件指向的目录也支持检测
if(file1 IS_NEWER_THAN file2)
if(IS_DIRECTORY path-to-directory) # 判断绝对路径是否是文件夹
if(<variable|string> LESS|GREATER|EQUAL|LESS_EQUAL|GREATER_EQUAL| \
STRLESS|STRGREATER|STREQUAL|STRLESS_EQUAL|STRGREATER_EQUAL <variable|string>)
很像python的语法,支持插入、查找、删除、读取、排序、反转。
Reading
list(LENGTH <list> <out-var>)
list(GET <list> <element index> [<index> ...] <out-var>)
list(JOIN <list> <glue> <out-var>)
list(SUBLIST <list> <begin> <length> <out-var>)
Search
list(FIND <list> <value> <out-var>)
Modification
list(APPEND <list> [<element>...])
list(FILTER <list> {INCLUDE | EXCLUDE} REGEX <regex>)
list(INSERT <list> <index> [<element>...])
list(POP_BACK <list> [<out-var>...])
list(POP_FRONT <list> [<out-var>...])
list(PREPEND <list> [<element>...])
list(REMOVE_ITEM <list> <value>...)
list(REMOVE_AT <list> <index>...)
list(REMOVE_DUPLICATES <list>)
list(TRANSFORM <list> <ACTION> [...])
Ordering
list(REVERSE <list>)
list(SORT <list> [...])
例子:
# list eample
set(test_list 0 2 3 1 4 5 6)
list(LENGTH test_list output_length)
message("The length of test_list is ${output_length}")
list(GET test_list 5 5th_val)
message("5th_val is ${5th_val}")
list(SORT test_list ORDER DESCENDING)
# foreach test
message("\nlist sort result:")
foreach(x IN LISTS test_list)
message(STATUS "x = ${x}")
endforeach()
结果:
The length of test_list is 7
5th_val is 5
list sort result:
-- x = 6
-- x = 5
-- x = 4
-- x = 3
-- x = 2
-- x = 1
-- x = 0
foreach即可以用来循环计数又可以遍历列表:
foreach(<loop_var> <items>)
<commands>
endforeach()
foreach(<loop_var> RANGE <stop>)
foreach(<loop_var> RANGE <start> <stop> [<step>])
foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])
while(<condition>) # condition的规则和if一致
<commands> # 可以使用continue和break继续或者终止循环
endwhile()
例子:
# exp1 注意循环计数是0 1 2,包括2
message("\nforeach RABGE test result:")
foreach(i RANGE 0 2)
message(STATUS "i = ${i}")
endforeach()
# exp2 搜索路径下是否有带有关键字的文件
set(file_groups output.txt output.log output.error)
foreach(file_name IN LISTS file_groups)
if(EXISTS "${CMAKE_SOURCE_DIR}/${file_name}")
message("output file exists, file is in ${CMAKE_SOURCE_DIR}/${file_name}")
endif()
endforeach()
# exp3 3.17引入的新的多列表循环,按较长的循环
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()
# exp4 while循环
set(condition OFF)
while(NOT condition)
message("This is a while loop")
set(condition ON)
endwhile()
结果:
foreach RABGE test result:
-- i = 0
-- i = 1
-- i = 2
output file exists, file is in /home/sjl/learn/cmake-examples/grammer/output.log
-- num_0=one, num_1=satu
-- 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=
This is a while loop
函数声明必须在使用之前。格式如下:
function(<name> [<arg1> ...])
<commands>
endfunction()
例子,判断条件并设定值,如果依赖条件成立设置为ON,如果不成立设置为OFF(FORCE设置),这里的函数功能和官方cmake_dependent_option()函数用法一致,先看一下这个函数的用法:
cmake_dependent_option(<option> "" <value> <depends> <force>)
# If USE_BAR is true and USE_ZOT is false, this provides an option called USE_FOO that defaults to ON.
# Otherwise, it sets USE_FOO to OFF and hides the option from the user. If the status of USE_BAR or
# USE_ZOT ever changes, any value for the USE_FOO option is saved so that when the option is re-enabled
# it retains its old value.
cmake_dependent_option(USE_FOO "Use Foo" ON
"USE_BAR;NOT USE_ZOT" OFF)
这里如果depends支持,则设置值为value(CACHE设置),如果不支持则设置为force,这是强制设置。我们来实现一下这个函数:
include(CMakeDependentOption) # Modern CMake的头文件
set(CONDITION_A ON)
function(cmake_dependent_option_test target_val info val depends force_val)
if(${depends})
# 这里是CACHE设置,但是cmake_dependent_option函数在下次条件支持时可以Over write缓存的值,而这里的写法不能over write
# 这里不能使用FORCE因为会修改输入的值,而cmake_dependent_option即便条件满足也不会覆盖输入的值,只会覆盖缓存的值
set(${target_val} ${val} CACHE BOOL ${info})
else()
set(${target_val} ${force_val} CACHE BOOL ${info} FORCE)
endif()
endfunction()
cmake_dependent_option(CONFIG_A "config A" ON "CONDITION_A" OFF)
message("CONFIG_A is ${CONFIG_A}")
cmake_dependent_option_test(CONFIG_B "config B" ON CONDITION_A OFF)
message("CONFIG_B is ${CONFIG_B}")
set(CONDITION_A OFF)
cmake_dependent_option(CONFIG_A "config A" ON "CONDITION_A" OFF)
message("CONFIG_A is ${CONFIG_A}")
cmake_dependent_option_test(CONFIG_B "config B" ON CONDITION_A OFF)
message("CONFIG_B is ${CONFIG_B}")
测试结果:
rm -rf *
cmake ..
CONFIG_A is ON
CONFIG_B is ON
CONFIG_A is OFF
CONFIG_B is OFF
cmake..
CONFIG_A is ON
CONFIG_B is OFF # 第二次没有覆盖,手动实现的函数不是很完善
CONFIG_A is OFF
CONFIG_B is OFF
文件操作也是CMake重要的功能:
Reading
file(READ <filename> <out-var> [...]) # 从文件读取变量
file(STRINGS <filename> <out-var> [...]) # 从文件读取字符串
file(<HASH> <filename> <out-var>) # 计算hash值
file(TIMESTAMP <filename> <out-var> [...]) # 获取文件的建立时间戳
file(GET_RUNTIME_DEPENDENCIES [...]) # 获取可执行文件的运行依赖,在安装时调用以检测是否可以安装
Writing
file({WRITE | APPEND} <filename> <content>...) # write创建或覆盖,append追加
file({TOUCH | TOUCH_NOCREATE} [<file>...]) # 创建一个空文件
file(GENERATE OUTPUT <output-file> [...]) # 按照规则创建文件
file(CONFIGURE OUTPUT <output-file> CONTENT <content> [...]) # 按照规则配置文件
Filesystem
file({GLOB | GLOB_RECURSE} <out-var> [...] [<globbing-expr>...]) # 全局搜索匹配表达式的文件列表并存储到变量中,也可以是相对地址模式
file(MAKE_DIRECTORY [<dir>...]) # 创建文件夹
file({REMOVE | REMOVE_RECURSE } [<files>...]) # 删除文件、文件夹
file(RENAME <oldname> <newname> [...])
file(COPY_FILE <oldname> <newname> [...])
file({COPY | INSTALL} <file>... DESTINATION <dir> [...])
file(SIZE <filename> <out-var>)
file(READ_SYMLINK <linkname> <out-var>)
file(CREATE_LINK <original> <linkname> [...])
file(CHMOD <files>... <directories>... PERMISSIONS <permissions>... [...])
file(CHMOD_RECURSE <files>... <directories>... PERMISSIONS <permissions>... [...])
Path Conversion
file(REAL_PATH <path> <out-var> [BASE_DIRECTORY <dir>] [EXPAND_TILDE])
file(RELATIVE_PATH <out-var> <directory> <file>)
file({TO_CMAKE_PATH | TO_NATIVE_PATH} <path> <out-var>)
Transfer
file(DOWNLOAD <url> [<file>] [...]) # 下载
file(UPLOAD <file> <url> [...]) # 上传
Locking
file(LOCK <path> [...])
Archiving
file(ARCHIVE_CREATE OUTPUT <archive> PATHS <paths>... [...])
file(ARCHIVE_EXTRACT INPUT <archive> [...])
例子
if(EXISTS ${CMAKE_SOURCE_DIR}/env.txt)
file(READ ${CMAKE_SOURCE_DIR}/env.txt LOCAL_PATH)
message("LOCAL_PATH is ${LOCAL_PATH}")
file(STRINGS ${CMAKE_SOURCE_DIR}/env.txt FILE_STRING)
message("FILE_STRING is ${FILE_STRING}")
file(SHA256 ${CMAKE_SOURCE_DIR}/env.txt hash_of_file)
if(hash_of_file EQUAL 1df40cff003062f8c942fcbd16391585a41d4f94c4554f4fac2f2f5297910880)
message("hash compute success")
else()
message("hash compute error")
endif()
endif()
file(STRINGS ${CMAKE_SOURCE_DIR}/fruit.txt FRUIT_LIST)
message("FRUIT_LIST is ${FRUIT_LIST}")
file(TIMESTAMP ${CMAKE_SOURCE_DIR}/fruit.txt CREATE_TIME UTC)
message("The create time of fruit.txt is ${CREATE_TIME}")
file(GLOB all_txt_file RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/*.txt)
message("all_txt_file is ${all_txt_file}")
file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/tmp)
file(REMOVE_RECURSE ${CMAKE_SOURCE_DIR}/tmp)
数学 math
CMake支持表达式,符号定义与C语言一致:
math(EXPR val "5 * ( 10 + 13)" OUTPUT_FORMAT DECIMAL) # val is 115
math(EXPR val "0x001 << 8" OUTPUT_FORMAT HEXADECIMAL) # val is 0x100