【CMake官网】:掌握CMake

1、变量

CMake 变量名称区分大小写,且只能包含字母数字字符和下划线
CMAKE_ 变量【存储均为字符串】是CMake 自定义变量,命名应避开

set 可设变量值,第一参数是变量名,其余参数是值
多个参数被打包到 分号分隔的列表 中,并作为字符串存储在变量中

set(Foo "")      	# 1 quoted arg -> value is ""
set(Foo a)       	# 1 unquoted arg -> value is "a"
set(Foo "a b c") 	# 1 quoted arg -> value is "a b c"
set(Foo a b c)   	# 3 unquoted args -> value is "a;b;c"

变量 VAR 可通过 ${VAR} 引用
若变量未设值,则引用替换为空字符串,否则为变量值
替换是在扩展未引用的参数之前执行的,因此 含分号的变量值 被拆分为0~n个参数

set(Foo a b c)    	# 3 unquoted args -> value is "a;b;c"
command(${Foo})   	# unquoted arg replaced by a;b;c
                  	# and expands to three arguments
command("${Foo}") 	# quoted arg value is "a;b;c"


set(Foo "")       	# 1 quoted arg -> value is empty string
command(${Foo})   	# unquoted arg replaced by empty string
                  	# and expands to zero arguments
command("${Foo}") 	# quoted arg value is empty string

CMake 可直接访问 环境变量、Windows注册表值
环境变量:$ ENV{VAR}
注册表项: [HKEY_CURRENT_USER\Software\path1\path2;key] 【路径是从注册表树和键构建】

2、变量范围

CMake 中变量作用域略有不同
1、设变量时,对当前及任何子目录的 CMakeLists文件或函数,乃至include包含的文件均可见
2、当处理新子目录或调新函数时,建新变量范围,并使其内所有变量值初始化
3、在子作用域中,对新变量或现有变量更改,均不影响父作用域

// 不影响父作用域:
function(foo)
	message(${test}) 	# test is 1 here
	set(test 2)
  	message(${test}) 	# test is 2 here, but only in this scope
endfunction()

set(test 1)
foo()
message(${test}) 	 	# test will still be 1 here

在子作用域中,让变量影响父作用域:PARENT_SCOPE

function(foo)
  	message(${test}) 	# test is 1 here
  	set(test 2 PARENT_SCOPE)
  	message(${test}) 	# test still 1 in this scope
endfunction()

set(test 1)
foo()
message(${test}) 	 	# test will now be 2 here

变量是按 set 执行顺序定义的

# FOO is undefined

set(FOO 1)
# FOO is now set to 1

set(FOO 0)
# FOO is now set to 0

变量范围:

set(foo 1)

# process the dir1 subdirectory
add_subdirectory(dir1)		//foo

# include and process the commands in file1.cmake
include(file1.cmake)		//foo

set(bar 2)
# process the dir2 subdirectory
add_subdirectory(dir2)		//foo、bar

# include and process the commands in file2.cmake
include(file2.cmake)		//foo、bar

3、命令

虽命令名称不区分大小写,但command命名最佳,其和 ( 在同一行,内容就可跨多行
命令参数以空格分隔且区分大小写,"开始"结尾则只表示一参数

值中含 " 须用 \ 转义,可考虑对需要转义的参数可用方括号
值还可用分号来自动扩展参数

command("")          # 1 quoted argument
command("a b c")     # 1 quoted argument
command("a;b;c")     # 1 quoted argument
command("a" "b" "c") # 3 quoted arguments
command(a b c)       # 3 unquoted arguments
command(a;b;c)       # 1 unquoted argument expands to 3

4、基本命令

set 设变量,unset 取消变量
string、list、separate_arguments 提供对字符串和列表的基本操作
add_executable、add_librarycommands 定义要构建的可执行文件和库,以及包含它们的源文件

Visual Studio 项目,显示源文件的同时,若要显示头文件,需将它们添加到可执行文件或库的源文件列表中

5、流量控制

流控制结构:
1、条件语句【if】
2、循环结构【foreach、while】
3、程序定义【macro、function】

6、条件语句

条件语句【if】:

if(FOO)
  	# do something here
else()
  	# do something else
endif()

elseif 顺序测试多条件:

if(MSVC80)
  	# do something here
elseif(MSVC90)
  	# do something else
elseif(APPLE)
  	# do something else
endif()

7、循环构造

循环结构【foreach】:

foreach(tfile		//变量名
        TestAnisotropicDiffusion2D	//值列表
        TestButterworthLowPass
        TestButterworthHighPass
        TestCityBlockDistance
        TestConvolve
        )
	add_test(${tfile}-image ${VTK_EXECUTABLE}	//循环主题
		${VTK_SOURCE_DIR}/Tests/rtImageTest.tcl
    	${VTK_SOURCE_DIR}/Tests/${tfile}.tcl
    	-D ${VTK_DATA_ROOT}
    	-V Baseline/Imaging/${tfile}.png
    	-A ${VTK_SOURCE_DIR}/Wrapping/Tcl
    	)
endforeach()

每次循环 tfile 顺序变更值,直到处理完所有值
foreach 可嵌套,还可用循环变量构造变量名:

if(${${tfile}}_TEST_RESULT} MATCHES FAILED)
  	message("Test ${tfile} failed.")
endif()

循环结构【while】:

#####################################################
# run paraview and ctest test dashboards for 6 hours
#
while(${CTEST_ELAPSED_TIME} LESS 36000)
  	set(START_TIME ${CTEST_ELAPSED_TIME})
  	ctest_run_script("dash1_ParaView_vs71continuous.cmake")
  	ctest_run_script("dash1_cmake_vs71continuous.cmake")
endwhile()

8、过程定义

macro、function 支持分散 CMakeLists 中的重复性任务
调函数将建新变量范围,对变量修改仅存于函数内,出函数将释放

function 第一参数是函数名,后续参数均是函数形参

function(DetermineTime _time)	//_time 用于传递返回变量的名称
  	# pass the result up to whatever invoked this
  	set(${_time} "1:23:45" PARENT_SCOPE)	// 影响父作用域
endfunction()

# now use the function we just defined
DetermineTime(current_time)

if(DEFINED current_time)
  	message(STATUS "The time is now: ${current_time}")
endif()

宏定义和调用方与函数相同
主要区别在于宏不推送和弹出新变量范围,且宏的参数不被视为变量,而是在执行之前就被替换为字符串

第一个参数是宏名,后续参数均是宏形参

# define a simple macro
macro(assert TEST COMMENT)
  	if(NOT ${TEST})
    	message("Assertion failed: ${COMMENT}")
  	endif()
endmacro()

# use the macro
find_library(FOO_LIB foo /usr/local/lib)
assert(${FOO_LIB} "Unable to find library foo")

macro 还支持 可变参数列表,变量参数可用 ARGC 和 ARGV0、ARGV1 等来引用,而非形参
混合形参和可变参数:

# define a macro that takes at least two arguments
# (the formal arguments) plus an optional third argument
macro(assert TEST COMMENT)
  	if(NOT ${TEST})
    	message("Assertion failed: ${COMMENT}")

    	# if called with three arguments then also write the
    	# message to a file specified as the third argument
    	if(${ARGC} MATCHES 3)
      		file(APPEND ${ARGV2} "Assertion failed: ${COMMENT}")
    	endif()

  	endif()
endmacro()

# use the macro
find_library(FOO_LIB foo /usr/local/lib)
assert(${FOO_LIB} "Unable to find library foo")

若要将参数作为列表处理,可用 ARGV 和 ARGN
ARGV【与 ARGV0、ARGV1 等相反】是宏的所有参数的列表,而 ARGN 是形参之后的所有参数的列表

return 从函数、目录或文件返回
宏不同于函数,在原地展开,因此无法处理返回

9、正则表达式

if、string,可用正则表达式作参数【指定正则有几种不同约定】,如搜索字符串
用基于 Texas Instruments 的开源正则表达式类 可解析 正则表达式

10、高级命令

add_dependencies 可在两个目标之间创建依赖关系【至少一目标是自定义目标】
include_regular_expression 可控制 用于跟踪源代码依赖关系的正则表达式【如限制处理某些包含文件】

以前缀 foo 开头,指定正则 ^foo.*$ 以将依赖项检查 限制为仅对项目的文件检查