CMakeLists文件使用变量的方式与任何编程语言非常相似。CMake 变量名区分大小写,只能包含字母数字 字符和下划线。
CMake自动定义了许多有用的变量,它们是 在cmake-variables
手册中讨论。 这些变量以CMAKE_
开始。避免这种命名约定(并且, 理想情况下,建立您自己)。
所有CMake变量都在内部存储为字符串,尽管它们可能 有时被解释为其他类型。
使用set命令设置变量值。以最简单的形式, 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
是变量名。如果命名变量 则引用将替换为空字符串; 否则,它被变量的值替换。更换是 在展开未加引号的参数之前执行,因此变量 包含分号的值将拆分为零个或多个参数 原始未引用参数的位置。例如:
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中的变量的作用域与大多数变量稍有不同 语言。设置变量时,它对当前的 CMakeLists文件或函数以及任何子目录的CMakeLists文件, 调用的任何函数或宏,以及 使用include命令。当新的子目录 处理(或调用函数)时,将创建一个新的变量范围,并 用调用中所有变量的当前值初始化 范围。在子范围中创建的任何新变量或所做的更改 对现有变量进行修改,不会影响父作用域。考虑一下 以下示例:
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
在某些情况下,您可能需要一个函数或子目录将 变量在其父级作用域中。CMake有一种方法可以返回 函数的值,并且可以通过使用 PARENT_SCOPE
选项和set命令。我们可以修改 前面的示例,使得函数foo
改变test的值 在其父级范围中如下:
命令由命令名、左括号、空格组成 分隔的参数和结束括号。每个命令都是在 它在CMakeLists文件中出现的顺序。见 cmake-commands手册完整列表 CMake命令。
CMake不再对命令名区分大小写,因此您可以在哪里看到 command
,你可以使用COMMAND
或Command
。它被认为是 使用小写命令的最佳实践。 所有空白(空格、换行符、 制表符)被忽略,除了分隔参数。因此,命令可以跨越 多行,只要命令名和左括号都是开的 同一条线。
CMake命令参数以空格分隔且区分大小写。命令 参数可以被引用或未被引用。 引用的论点开始和结束 在双引号(“)中,并且总是恰好表示一个参数。任何双人间 包含在值中的引号必须用反斜杠转义。考虑 对需要转义的参数使用方括号参数,请参见 cmake-language手册。未引用的论点 以除双引号以外的任何字符开始(后面的双引号是 文字),并通过以下方式自动展开为零个或多个参数 在值内用分号分隔。 例如:
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
正如我们前面看到的,set和unset命令 显式设置或取消设置变量。string、list和 separate_arguments命令提供字符串和列表的基本操作。
add_executable和add_library命令是主要的 用于定义要生成的可执行文件和库的命令,以及 哪些源文件包括它们。对于VisualStudio项目, 源文件将像往常一样显示在IDE中,但任何头文件 项目使用将不会。要显示头文件,只需 将它们添加到可执行文件或库的源文件列表中; 这可以对所有发生器进行。任何不使用的发电机 头文件(例如基于Makefile的生成器)将 只是忽略它们。
CMake语言提供了三种流控制结构来帮助组织 您的CMakeLists文件并保持它们的可维护性。
条件语句(例如if)
环化构建体(例如foreach和while)
程序定义(例如macro和function)
首先,我们来看看if命令。在许多方面, CMake中的if
命令就像任何命令中的if命令 其他语言。它计算其表达式并使用它执行代码 在其主体中,或者可选地在else
子句中的代码。为了 例如:
if(FOO)
# do something here
else()
# do something else
endif()
CMake还支持elseif以帮助顺序测试多个 条件。例如:
if(MSVC80)
# do something here
elseif(MSVC90)
# do something else
elseif(APPLE)
# do something else
endif()
foreach和while命令允许您处理 按顺序发生的重复性任务。break命令中断 foreach或while循环之外 结束
foreach命令使您能够执行组 对列表的成员重复执行CMake命令。考虑一下 以下示例改编自VTK
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()
foreach
命令的第一个参数是 每次迭代时都会取不同值的变量 循环;其余的参数是值的列表, 循环在本例中,foreach
循环的主体只有一个 CMake命令,add_test。在foreach
的主体中,每个 引用循环变量(本例中为tfile
)的时间将 替换为列表中的当前值。在第一 迭代,出现的${tfile}
将替换为 TestAnisotropicDiffusion2D
.在下一次迭代中,${tfile}
将被替换为#9。TestButterworthLowPass循环 将继续循环,直到所有参数都处理完。
值得一提的是,foreach循环可以嵌套,并且 循环变量在任何其他变量之前被替换 膨胀这意味着在foreach
循环的主体中,您可以 使用循环变量构造变量名。在下面的代码中, 循环变量tfile
被展开,然后与 _TEST_RESULT
.然后将新的变量名展开并测试为 看看是否匹配FAILED
。
if(${${tfile}}_TEST_RESULT} MATCHES FAILED)
message("Test ${tfile} failed.")
endif()
while命令根据测试条件提供循环。的 while
命令中测试表达式的格式与 它是用于if命令,如前所述。考虑一下 下面的例子,由CTest使用。请注意,CTest将更新 CTEST_ELAPSED_TIME
内部
##################################################### # 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()
macro和function命令支持重复性任务 可能分散在您的CMakeLists文件中。一旦宏或 函数定义后,它可以被任何CMakeLists文件使用 其定义。
CMake中的函数非常类似于C或C++中的函数。你可以 将参数传递给它,它们就变成了 功能同样,一些标准变量,如ARGC
、 ARGV
、ARGN
、ARGV0
、ARGV1
等。是 定义。函数调用具有动态范围。 在一个函数中,你 在一个新的变量作用域中;这就像你掉进 使用add_subdirectory
命令创建子目录,并在一个新的 变量作用域所有在函数 被调用时仍保持定义状态,但对变量或new的任何更改 变量只存在于函数中。当函数返回时, 这些变数就会消失。更简单地说:当您调用 函数,则推送一个新的变量作用域;当它回来的时候 变量范围弹出。
function命令定义了一个新函数。 第一个论点 是要定义的函数的名称;所有附加参数为 函数的形式参数。
function(DetermineTime _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()
请注意,在此示例中,_time
用于传递 返回变量调用set命令的值为 _time
,这将是current_time
。set 命令使用PARENT_SCOPE
选项在 调用方的作用域而不是本地作用域。
宏的定义和调用方式与函数相同。的 主要的区别是宏不会推送和弹出新变量 作用域,并且宏的参数不被视为变量 而是作为在执行之前替换的串。这很像 在C或C++中,宏和函数之间的区别。第一个 参数是要创建的宏的名称;所有附加参数 是宏的形式参数。
# 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")
上面的简单示例创建了一个名为assert
的宏。宏 定义为两个参数;第一个是要测试的值, 第二个是测试失败时打印出来的注释。的主体 宏是一个简单的if命令和message命令 在里面当endmacro命令被 找到了只需使用宏的名称就可以调用宏,就好像它是 命令在上面的示例中,如果没有找到FOO_LIB
,则 将显示指示错误状况的消息。
macro命令还支持定义接受变量的宏 参数列表。如果要定义一个 具有可选参数或多个签名。变量参数可以 使用ARGC
和ARGV0
、ARGV1
等引用,反而 形式参数。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")
在本例中,两个必需的参数是TEST
和 COMMENT
.这些必需的参数可以通过名称引用,如 在这个例子中,或者参考ARGV0
和 ARGV1
.如果要将参数作为列表处理,请使用 ARGV
和ARGN
变量。ARGV
(与ARGV0
相反) ARGV1
,等等)是宏的所有参数的列表,而 ARGN
是形式化后的所有参数列表 争论在宏中,您可以使用foreach命令执行以下操作: 根据需要迭代ARGV
或ARGN
。
return命令从函数、目录或文件返回。注记 宏与函数不同,它是就地展开的,因此不能 手柄return。