C++ CMake入门和进阶(二):CMake语法

CMake也是有语法的,这里总结一些。CMake系列学习个人笔记:

  • C++ CMake入门和进阶(一):使用CMake编译项目

文章目录

  • 输出 message
  • 分支 if/elseif/else/endif
  • 列表 list
  • 循环 foreach/while/break/continue
  • 函数 function
  • 文件 file

输出 message

message是CMake中重要的输出功能。

General messages
  message([<mode>] "message text" ...)

在mode模式下,常用FATAL_ERRORSTATUSWARNING等关键字。

分支 if/elseif/else/endif

分支的应用十分广泛,可以用于判断值、比较大小、判断文件是否存在、版本比较、新旧比较等等。语法格式如下:

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>)

列表 list

很像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/while/break/continue

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

函数声明必须在使用之前。格式如下:

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

文件 file

文件操作也是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

你可能感兴趣的:(cmake,cmake)