当前位置: 首页 > 工具软件 > Vala_CMake > 使用案例 >

CMake基本语法,cmake常用命令,CMake常用变量

宋稳
2023-12-01

1.CMake基本语法

(1)注释的语法:

  • 在CMake中注释使用#字符开始到此行结束。

(2)CMake命令:

  • 命令不区分大小写(参数会区分大小写),命令由命令、参数列表组成,参数之间使用空格进行分隔。
  • 使用一对双引号包括的字符串认为是一个参数。
  • 命令可以是一个内置命令(例如:project,add_executable等),也可以是一个用户定义的宏(macro)或者函数(function)。

(3)数据类型:

  • CMake的基本数据类型是字符串,一组字符串在一起称为列表(list)
  • 例如:
# 通过 set 命令构建一个 listVAR
set(VAR a b c)
  • 使用语法 ${VariableName} 来访问名字为 VariableName 的变量的值(变量名区分大小写)。
  • 需要注意的是,即使在字符串中也可以使用${VariableName} 来访问变量的值:
set(VAR a b c)
# 输出 VAR = a;b;c
message("VAR = ${VAR}")
  • 使用语法 $ENV{VariableName} 来访问环境变量的值(ENV{VariableName} 则表示环境变量本身)
# 输出环境变量 PATH 的值
message($ENV{PATH})

(4)条件控制和循环结构

  • 条件控制命令为 if 命令
if(expression)
    #...
elseif(expression2)
    #...
else()
    #...
endif()
  • eg:
if (variable)
  # 当 variable 不为 空值,1,TRUE、ON时为真

if (NOT variable)
  # 当 variable 为 空值,0,FALSE,OFF 或者 NOTFOUND 时为真

if (variable1 AND variable2)
  # 当 variable1, variable1 同时不为 空值,0,FALSE,OFF 或者 NOTFOUND 时为真

if (variable1 OR variable2)
  # 当 variable1, variable1 有一个不为 空值,0,FALSE,OFF 或者 NOTFOUND 时为真

if (COMMAND command-name)
  # 当 command-name 是可调用的命令时为真

if (DEFINED variable)
  # 当 variable 已经被设置了值时为真

if (EXISTS file-name)
if (EXISTS directory-name)
  # 当指定的文件或者目录时为真

if (IS_DIRECTORY name)
if (IS_ABSOLUTE name)
  # 当 name 是目录或者是绝对路径是为真

if (name1 IS_NEWER_THAN name2)
  # 当 name1 文件的修改时间比 name2 文件的修改时间要新时为真

if (variable MATCHES regex)
if (string MATCHES regex)
  # 当给定的变量或者字符串与给定的正则表达式相匹配时为真
  • 对于 if(string) 来说:
    (1)如果 string 为(不区分大小写)1、ON、YES、TRUE、Y、非 0 的数则表示真
    (2)如果 string 为(不区分大小写)0、OFF、NO、FALSE、N、IGNORE、空字符串、以 -NOTFOUND 结尾的字符串则表示假
    (3)如果 string 不符合上面两种情况,则 string 被认为是一个变量的名字。
    变量的值为第二条所述的各值则表示假,否则表示真。
# 此策略(Policy)在 CMake2.8.0 才被引入
# 因此这里需要指定最低 CMake 版本为 2.8
cmake_minimum_required(VERSION 2.8)
set(YES 0)

# 输出 True
if(YES)
   message(True)
else()
   message(False)
endif()

# 输出 False
if(${YES})
   message(True)
else()
   message(False)
endif()
  • 表达式中可以包含操作符,操作符包括:
    (1)一元操作符,例如:EXISTS、COMMAND、DEFINED 等
    (2)二元操作符,例如:EQUAL、LESS、GREATER、STRLESS、STRGREATER 等
    (3)NOT(非操作符)
    (4)AND(与操作符)、OR(或操作符)
    (5)操作符优先级:一元操作符 > 二元操作符 > NOT > AND、OR
  • 常用操作符介绍:
if(NOT expression)
//为真的前提是 expression 为假

if(expr1 AND expr2)
//为真的前提是 expr1 和 expr2 都为真

if(expr1 OR expr2)
//为真的前提是 expr1 或者 expr2 为真

if(COMMAND command-name)
//为真的前提是存在 command-name 命令、宏或函数且能够被调用

if(EXISTS name)
//为真的前提是存在 name 的文件或者目录(应该使用绝对路径)

if(file1 IS_NEWER_THAN file2)
//为真的前提是 file1 比 file2 新或者 file1、file2 中有一个文件不存在(应该使用绝对路径)

if(IS_DIRECTORY directory-name)
//为真的前提是 directory-name 表示的是一个目录(应该使用绝对路径)

if(variable|string MATCHES regex)
//为真的前提是变量值或者字符串匹配 regex 正则表达式

if(variable|string LESS variable|string)
if(variable|string GREATER variable|string)
if(variable|string EQUAL variable|string)
//为真的前提是变量值或者字符串为有效的数字且满足小于(大于、等于)的条件

if(variable|string STRLESS variable|string)
if(variable|string STRGREATER variable|string)
if(variable|string STREQUAL variable|string)
//为真的前提是变量值或者字符串以字典序满足小于(大于、等于)的条件

if(DEFINED variable)
//为真的前提是 variable 表示的变量被定义了。
  • foreach 循环范例:
set(VAR a b c)
foreach(f ${VAR})
    message(${f})
endforeach()
  • while 循环范例:
set(VAR 5)
while(${VAR} GREATER 0)
   message(${VAR})
   math(EXPR VAR "${VAR} - 1")
endwhile()

(5)函数和宏定义

  • 函数会为变量创建一个局部作用域,而宏则使用全局作用域。
在一个 macro 或者 function 定义后,就可以被任何在它定义之后处理的 CMakeLists 文件中使用。

在macro 和 function 中有一些预定义的标准参数,ARGV0, ARGV1 分别表示 macro 和 function 传入的第一个参数和第二个参数。

ARGV 是 macro 和 function 所有参数的一个列表,ARGN 是表示传入 macro 和 function 的在正式定义参数之后传入的不定形式的参数的列表。
  • function
 CMake 中的 function 与 C 或者是 C++ 中的函数很像,可以传递参数到 function 中当作变量使用。
 
function 中,一些例如 ARGC, ARGV, ARGN, ARGV0, ARGV1 的标准变量已经被定义。
在 function 中是一个新的变量作用域, 与使用 add_subdirectory 命令进入子目录产生的新变量作用域一样,所有在调用 function 时已定义的变量在 function 中仍然被定义,但是对变量的修改以及定义新的变量只在这个 function 中起作用,函数返回后这些变量就被移除了。

定义 function 时,第一个参数是 function 的名称,剩余的其他参数都会传递到 function 中当变量使用。

function (name argv1 argv2)
  # do something here
  set (${argv1} "val" PARENT_SCOPE)
endfunction ()
在 function 中,使用带 PARENT_SCOPE 选项的 set 命令可以修改父作用域的中变量的值。

  • eg:定义一个可在CMake脚本其他位置调用的函数。
定义一个函数名为<name>,参数名为arg1 arg2 arg3(…)。
function(<name>[arg1 [arg2 [arg3 ...]]])
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
endfunction(<name>)

 函数体内的命令直到函数被调用的时候才会去执行。
 其中ARGC变量表示传递给函数的参数个数。 
 ARGV0, ARGV1, ARGV2代表传递给函数的实际参数。
ARGN代表超出最后一个预期参数的参数列表,例如,函数原型声明时,只接受一个参数,那么调用函数时传递给函数的参数列表中,从第二个参数(如果有的话)开始就会保存到ARGN。
  • 测试:
cmake_minimum_required(VERSION 2.8)
project(ArgumentExpansion)



function (argument_tester arg)
    message(STATUS "ARGN: ${ARGN}")
    message(STATUS "ARGC: ${ARGC}")
    message(STATUS "ARGV: ${ARGV}")
    message(STATUS "ARGV0: ${ARGV0}")

    list(LENGTH ARGV  argv_len)
    message(STATUS "length of ARGV: ${argv_len}")
    set(i 0)
    while( i LESS ${argv_len})
         list(GET ARGV ${i} argv_value)
         message(STATUS "argv${i}: ${argv_value}")
         math(EXPR i "${i} + 1")
    endwhile()


endfunction ()

argument_tester(arg0 arg1 arg2 arg3)
  • 结果:
结果:
-- ARGN: arg1;arg2;arg3
-- ARGC: 4
-- ARGV: arg0;arg1;arg2;arg3
-- ARGV0: arg0
-- ARGV1: arg1
-- length of ARGV: 4
-- argv0: arg0
-- argv1: arg1
-- argv2: arg2
-- argv3: arg3
  • macro
macro 的定义和调用方式与 function 一样,但与 function 最主要的不同是 macro 不会产生新的变量作用域。 

macro 中的参数不是当作变量,而是当作在执行前进行替换的字符串。

这与 C 和 C++ 中, 函数与宏定义的区别是一样的。

macro 的第一个参数是创建的 macro 的名称,剩余的其他参数是 macro 的中的参数。

macro (name argv1 argv2)
  # do something here
endmacro (name)

macro 也支持使用参数列表来进行宏替换,参数可以使用 ARGC, ARGV0, ARGV1 等来进行引用, 其含义与 function 中的一样。
  • 范例:
# 定义一个宏 hello
macro(hello MESSAGE)
   message(${MESSAGE})
endmacro()

# 调用宏 hello
hello("hello world")

# 定义一个函数 hello
function(hello MESSAGE)
   message(${MESSAGE})
endfunction()
  • 函数和宏可以通过命令 return() 返回,但是函数和宏的返回值必须通过参数传递出去。
  • 例如:
cmake_minimum_required(VERSION 2.8)
function(get_func RESULT)
    #RESULT 的值为实参的值,因此需要使用 ${RESULT}
    #这里使用 PARENT_SCOPE 是因为函数会构建一个局部作用域
    # set(${RESULT} "hello get_func")不会有输出
   set(${RESULT} "Hello Function" PARENT_SCOPE)
endfunction()

macro(get_macro RESULT)
   set(${RESULT} "Hello Macro")
endmacro()

get_func(V1)
# 输出 Hello Function
message(${V1})

get_macro(V2)
# 输出 Hello Macro
message(${V2})

字符串的一些问题

  • 字符串可跨行且支持转移字符
  • 例如:
set(VAR "hello
world")

# 输出结果为:
# ${VAR} = hello
# world
message("\${VAR} = ${VAR}")

2.cmake常用命令

常用命令:

  • 其他命令请查询:链接

(1)project 命令

  命令语法:project(<projectname> [languageName1 languageName2 … ] )

  命令简述:用于指定项目的名称

  使用范例:project(Main)

(2)cmake_minimum_required命令


  命令语法:cmake_minimum_required(VERSION major[.minor[.patch[.tweak]]][FATAL_ERROR])

  命令简述:用于指定需要的 CMake 的最低版本

  使用范例:cmake_minimum_required(VERSION 2.8)

(3)aux_source_directory命令

  命令语法:aux_source_directory(<dir> <variable>)

  命令简述:用于将 dir 目录下的所有源文件的名字保存在变量 variable 中

  使用范例:aux_source_directory(. DIR_SRCS)

(4)add_executable 命令

  命令语法:add_executable(<name> [WIN32] [MACOSX_BUNDLE][EXCLUDE_FROM_ALL] source1 source2 … sourceN)

  命令简述:用于指定从一组源文件 source1 source2 … sourceN 编译出一个可执行文件且命名为 name

  使用范例:add_executable(Main ${DIR_SRCS})

(5)add_library 命令

  命令语法:add_library([STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1source2 … sourceN)

  命令简述:用于指定从一组源文件 source1 source2 … sourceN 编译出一个库文件且命名为 name(默认为静态库)

  使用范例:add_library(Lib ${DIR_SRCS})

(6)add_dependencies 命令

  命令语法:add_dependencies(target-name depend-target1 depend-target2 …)

  命令简述:用于指定某个目标(可执行文件或者库文件)依赖于其他的目标。
  这里的目标必须是 add_executable、add_library、add_custom_target 命令创建的目标

(7)add_subdirectory 命令

  命令语法:add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

  命令简述:用于添加一个需要进行构建的子目录

  使用范例:add_subdirectory(Lib)

(8)target_link_libraries命令

  命令语法:target_link_libraries(<target> [item1 [item2 […]]][[debug|optimized|general] ] …)

  命令简述:用于指定 target 需要链接 item1 item2 …。这里 target 必须已经被创建,链接的 item 可以是已经存在的 target(依赖关系会自动添加)

  使用范例:target_link_libraries(Main Lib)

(9)set 命令

  命令语法:set(<variable> <value> [[CACHE <type><docstring> [FORCE]] | PARENT_SCOPE])

  命令简述:用于设定变量 variable 的值为 value。如果指定了 CACHE 变量,将被放入 Cache(缓存)中。

  使用范例:set(ProjectName Main)

(10)unset 命令

  命令语法:unset(<variable> [CACHE])

  命令简述:用于移除变量 variable。如果指定了 CACHE 变量将被从 Cache 中移除。

  使用范例:unset(VAR CACHE)

(11)message 命令


  命令语法:message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] “message todisplay”…)

  命令简述:用于输出信息

  使用范例:message(“Hello World”)

(12)include_directories 命令

  命令语法:include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)

  命令简述:用于设定目录,这些设定的目录将被编译器用来查找 include 文件

  使用范例:include_directories(${PROJECT_SOURCE_DIR}/lib)

(13)find_path 命令

	命令语法:find_path(<VAR> name1 [path1 path2 …])

  命令简述:用于查找包含文件name1的路径,如果找到则将路径保存在VAR中(此路径为一个绝对路径),
  如果没有找到,则结果为 <VAR>-NOTFOUND。
  默认的情况下,VAR 会被保存在 Cache 中,这时候我们需要清除 VAR 才可以进行下一次查询(使用 unset 命令)。

  使用范例:
  find_path(LUA_INCLUDE_PATH lua.h ${LUA_INCLUDE_FIND_PATH})

  if(NOT LUA_INCLUDE_PATH)
     message(SEND_ERROR "Header file lua.h not found")
  endif()

(14)find_library 命令

  命令语法:find_library(<VAR> name1 [path1 path2 …])

  命令简述:用于查找库文件 name1 的路径,如果找到则将路径保存在 VAR 中(此路径为一个绝对路径),
  如果没有找到则结果为 <VAR>-NOTFOUND。
  一个类似的命令 link_directories 已经不太建议使用了

(15)add_definitions 命令

  命令语法:add_definitions(-DFOO -DBAR …)

  命令简述:用于添加编译器命令行标志(选项),通常的情况下我们使用其来添加预处理器定义

  使用范例:add_definitions(-D_UNICODE -DUNICODE)

(16)execute_process 命令

命令语法:

execute_process(COMMAND <cmd1>[args1...]]

                  [COMMAND <cmd2>[args2...] [...]]

                  [WORKING_DIRECTORY<directory>]

                  [TIMEOUT <seconds>]

                  [RESULT_VARIABLE<variable>]

                  [OUTPUT_VARIABLE<variable>]

                  [ERROR_VARIABLE<variable>]

                  [INPUT_FILE <file>]

                  [OUTPUT_FILE <file>]

                  [ERROR_FILE <file>]

                  [OUTPUT_QUIET]

                  [ERROR_QUIET]

                 [OUTPUT_STRIP_TRAILING_WHITESPACE]

                 [ERROR_STRIP_TRAILING_WHITESPACE])

  命令简述:用于执行一个或者多个外部命令。
  每一个命令的标准输出通过管道转为下一个命令的标准输入。
  WORKING_DIRECTORY 用于指定外部命令的工作目录,RESULT_VARIABLE 用于指定一个变量保存外部命令执行的结果,
  这个结果可能是最后一个执行的外部命令的退出码或者是一个描述错误条件的字符串,
  OUTPUT_VARIABLE 或者 ERROR_VARIABLE 用于指定一个变量保存标准输出或者标准错误,OUTPUT_QUIET 或者 ERROR_QUIET 用于忽略标准输出和标准错误。

  使用范例:execute_process(COMMAND ls)

(18)file 命令

  命令简述:此命令提供了丰富的文件和目录的相关操作(这里仅说一下比较常用的)

  使用范例:

  # 目录的遍历

  # GLOB 用于产生一个文件(目录)路径列表并保存在variable 中

  # 文件路径列表中的每个文件的文件名都能匹配globbing expressions(非正则表达式,但是类似)

  # 如果指定了 RELATIVE 路径,那么返回的文件路径列表中的路径为相对于 RELATIVE 的路径

  # file(GLOB variable [RELATIVE path][globbing expressions]...)

 

  # 获取当前目录下的所有的文件(目录)的路径并保存到 ALL_FILE_PATH 变量中

  file(GLOB ALL_FILE_PATH ./*)
  

  # 获取当前目录下的 .h 文件的文件名并保存到ALL_H_FILE 变量中

  # 这里的变量CMAKE_CURRENT_LIST_DIR 表示正在处理的 CMakeLists.txt 文件的所在的目录的绝对路径(2.8.3 以及以后版本才支持)

  file(GLOB ALL_H_FILE RELATIVE${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/*.h)

3.CMake常用变量

UNIX 如果为真,表示为 UNIX-like 的系统,包括 AppleOS X 和 CygWin

WIN32 如果为真,表示为 Windows 系统,包括 CygWin

APPLE 如果为真,表示为 Apple 系统

CMAKE_SIZEOF_VOID_P 表示 void* 的大小(例如为 4 或者 8),可以使用其来判断当前构建为 32 位还是 64 位

CMAKE_CURRENT_LIST_DIR 表示正在处理的CMakeLists.txt 文件的所在的目录的绝对路径(2.8.3 以及以后版本才支持)

CMAKE_ARCHIVE_OUTPUT_DIRECTORY 用于设置 ARCHIVE 目标的输出路径

CMAKE_LIBRARY_OUTPUT_DIRECTORY 用于设置 LIBRARY 目标的输出路径

CMAKE_RUNTIME_OUTPUT_DIRECTORY 用于设置 RUNTIME 目标的输出路径

4.使用模块

CMake 通过使用模块的方式来支持代码重用。

  • 模块是一段在一个文件中的 CMake 命令, 它们可以通过 include 命令包含进其他的 CMakeLists 文件中。
  • 一个模块的位置可以通过绝对路径来指定,也可以让 CMake 自己查找。CMake 会在 CMAKE_MODULE_PATH 指定的文件里查找模块。如果没有找到 CMake 就会在自己的模块子目录下查找。

模块可以主要分为以下的三类:

  • (1) Find Modlues
    这些模块决定了软件元素的位置,如头文件和库
    CMake 包含很多的 Find Modlues,Find Modlues 用来定位软件元素的的位置,如果没有找到,它们会提供一个缓存记录使得用户可以设置它们的需要的值。
      find_path 命令用来定位头文件的位置,命令的第一个参数是用来存储结果的变量名称,第二个参数是需要查找的头文件名称,剩余的参数是用来寻找头文件的路径:
find_path (VARIABLE_NAME includefile path1 path2)

当头文件没有找到时,VARIABLE_NAME 变量会被设置为 VARIABLE_NAME-NOTFOUND。
  • find_library 命令用来寻找真正的库文件,这个命令执行了额外的检查来寻找一个合适的的库名称,如在 linux 系统中会在名称前添加 lib,在结尾添加 .so。find_library 命令的调用方式与 find_path 命令类似:
find_path (VARIABLE_NAME libraryfile path1 path2)
  • 在 CMake 中寻找模块的基本文件结构如下:
find_path (MODULE_INCLUDE_DIR header.h path1 path2)
find_library (MODULE_LIBRARY library path1 path2)

if(MODULE_LIBRARY)
  if(MODULE_INCLUDE_DIR)
    set (MODULE_FOUND "YES")
  endif()
endif()

(2) System Introspection Modules

  • 这些模块提供了目标平台或编译器的相关信息,如 float 的长度,支持 ANSI C++ 流等。它们的模块名称通常有 Test 或者 Check 的前缀。为了产生正确的编译结果,大多数的 System Introspection Modules 参与到代码的编译过程中,它们的源代码通常有和模块一样的名称,并以 .c 或 .cxx 后缀结尾。

(3) Utility Modlues

  • Utility Modlues 模块提供了额外的功能,如支持一个 CMake 工程依赖另一个的的情形。CMake 包含了一些 Utility Modlues 用来帮助 CMake 的使用。CMakeExportBuildSetting 和 CMakeImportBuildSetting 提供了工具用来区分两个用相同的编译器和关键标志进行编译的 C++ 项目。CMakePrintSystemInformation 模块用来打印 CMake 的关键设置来帮助调试。

5.add_custom_command和add_custom_target的使用

add_custom_command是在CMake构建时使用的,如果add_custom_command的输出没有成为其他Target的依赖的话,那么在编译期,是不会运行的。

所以一般需要配合生成target的函数去使用,目的是让其作为其他Target的依赖

add_executable()
add_custom_target()
 类似资料: