Cmake编程

变量

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的值 在其父级范围中如下:

in命令

命令由命令名、左括号、空格组成 分隔的参数和结束括号。每个命令都是在 它在CMakeLists文件中出现的顺序。见 cmake-commands手册完整列表 CMake命令。

CMake不再对命令名区分大小写,因此您可以在哪里看到 command,你可以使用COMMANDCommand。它被认为是 使用小写命令的最佳实践。 所有空白(空格、换行符、 制表符)被忽略,除了分隔参数。因此,命令可以跨越 多行,只要命令名和左括号都是开的 同一条线。

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、 ARGVARGNARGV0ARGV1等。是 定义。函数调用具有动态范围。 在一个函数中,你 在一个新的变量作用域中;这就像你掉进 使用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命令还支持定义接受变量的宏 参数列表。如果要定义一个 具有可选参数或多个签名。变量参数可以 使用ARGCARGV0ARGV1等引用,反而 形式参数。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.如果要将参数作为列表处理,请使用 ARGVARGN变量。ARGV(与ARGV0相反) ARGV1,等等)是宏的所有参数的列表,而 ARGN是形式化后的所有参数列表 争论在宏中,您可以使用foreach命令执行以下操作: 根据需要迭代ARGVARGN

return命令从函数、目录或文件返回。注记 宏与函数不同,它是就地展开的,因此不能 手柄return。

你可能感兴趣的:(C,java,eclipse)