CMake指令放入一个名为 CMakeLists.txt 的文件中,CMake命令是不区分大小写的,所以常用的做法是使用小写,参数使用大写。
每个CMakeLists.txt都必须包含cmake_minimum_required作为第一行,设置项目工程依赖CMake的最低版本。如下代码:
cmake_minimum_required(VERSION 3.1)
指定CMake版本为3.1
每个CMakeLists.txt都必须包含cmake_minimum_required作为第一行,设置项目工程依赖CMake的最低版本。
cmake_minimum_required(VERSION 3.7...3.21)
指定CMake版本为3.7到3.21,这意味着这个工程最低可以支持3.7版本,但是也最高在 3.21版本上测试成功过。实际只会设置为3.7版本的特性,因为这些版本处理这个工程没有什么差异。注意:数字3.7...3.21不能有空格。
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
设置CMake所需的最低版本为3.7,如果使用的CMake版本低于该版本,则会发出致命错误。
CMake版本特性,cmake_minimum_required和cmake_policy搭配使用。
cmake_minimum_required(VERSION 3.7)
if(${CMAKE_VERSION} VERSION_LESS 3.21)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.21)
endif()
如果CMake的版本低于3.21,if 块条件为真,CMake 将会被设置为当前版本。如果CMake版本是3.21或者更高,if 块条件为假,CMake会设置3.21版本。
设置项目名称。
project(MyProject VERSION 1.0
DESCRIPTION "Very nice project"
LANGUAGES CXX)
MyProject是.sln文件的名称,如下图所示。
这里的字符串是带引号的,因此内容中可以带有空格。项目名称是这里的第一个参数。所有的关键字参数都可选的。VERSION 设置了一系列变量,例如 MyProject_VERSION和PROJECT_VERSION。
语言可以是 C,CXX,Fortran,ASM,CUDA(CMake3.8+),CSharp(3.8+),SWIFT(CMake3.15+ experimental),默认是CXX。在 CMake 3.9,可以通过DESCRIPTION 关键词来添加项目的描述。
add_executable指令是生成可执行文件。
add_executable(one two.cpp three.h)
one 既是生成的可执行文件的名称,也是创建的 CMake 目标(target)的名称。紧接着的是源文件的列表,你想列多少个都可以。CMake很聪明,它根据拓展名只编译源文件。在大多数情况下,头文件将会被忽略;列出他们的唯一原因是为了让他们在 IDE 中被展示出来,目标文件在许多 IDE 中被显示为文件夹。
cmake_minimum_required(VERSION 3.7...3.21)
if(${CMAKE_VERSION} VERSION_LESS 3.21)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.21)
endif()
project(Test01 VERSION 1.0
DESCRIPTION "Very nice project"
LANGUAGES CXX)
add_executable(${PROJECT_NAME} main.cpp test01.h test01.cpp)
生成一个库。
add_library(one STATIC two.cpp three.h)
你可以选择库的类型,可以是 STATIC、SHARED或者MODULE。如果你不选择它,CMake 将会通过 BUILD_SHARED_LIBS 的值来选择构建 STATIC 还是 SHARED 类型的库。
在下面的章节中你将会看到,你经常需要生成一个虚构的目标,也就是说,一个不需要编译的目标。例如,只有一个头文件的库。这被叫做 INTERFACE 库,这是另一种选择,和上面唯一的区别是后面不能有文件名。
你也可以用一个现有的库做一个 ALIAS 库,这只是给已有的目标起一个别名。这么做的一个好处是,你可以制作名称中带有::的库。
如果add_library不添加STATIC、SHARED和MODULE任何参数,默认只生成lib文件,如果添加STATIC也只生成lib文件,如果添加SHARED,则只生成dll文件。
如果我们想同时生成lib和dll文件时,就需要给add_library设置SHARED参数,并设置导出符号__declspec(dllimport) 。
添加宏定义
add_definitions(-D宏名称)
-D关键词不能少,且后面紧接着时宏名称,没有空格。
给项目工程加入引用代码目录,也就是引用头文件所在目录。
target_include_directories(one PUBLIC include)
target_include_directories为目标添加了一个目录。PUBLIC 对于一个二进制目标没有什么含义;
但对于库来说,它让CMake知道,任何链接到这个目标的目标也必须包含这个目录。
其他选项还有 PRIVATE(只影响当前目标,不影响依赖),以及 INTERFACE(只影响依赖)。
注意:命令target_include_directories必须放在add_library或者add_executable之后,否则CMake构建工程时报类似“Cannot specify link libraries for target “test“ which is not built by this project.”的错误。
给项目工程加入引用代码目录,也就是引用头文件所在目录,但是include_directories命令包含其子目录。
给项目工程添加库目录。
注意:link_directories必须放在add_executable指令前。
给项目工程添加库目录。
注意:target_link_directories必须放在add_executable指令后。
使用语法如下:
target_link_directories(${PROJECT_NAME} PUBLIC "../tecio/lib")
连接引用库,也就是链接库。
add_library(another STATIC another.cpp another.h)
target_link_libraries(another PUBLIC one)
target_link_libraries可能是CMake中最有用也最令人迷惑的命令。它指定一个目标,并且在给出目标的情况下添加一个依赖关系。如果不存在名称为one的目标,那他会添加一个链接到你路径中one库(这也是命令叫
target_link_libraries 的原因)。或者你可以给定一个库的完整路径,或者是链接器标志。最后再说一个有些迷惑性的知识:),经典的CMake允许你省略 PUBLIC
关键字,但是你在目标链中省略与不省略混用,那么CMake会报出错误。
只要记得在任何使用目标的地方都指定关键字,那么就不会有问题。
编译特性
target_compile_features(calclib PUBLIC cxx_std_11)
目标calclib使用c++11特性。
本地变量、缓存变量和环境变量。
声明一个本地变量MY_VARIABLE
set(MY_VARIABLE "value")
取消常规变量MY_VARIABLE
unset(MY_VARIABLE)
变量名通常全部用大写,变量值跟在其后。你可以通过 ${} 来解析一个变量,例如 ${MY_VARIABLE}。CMake 有作用域的概念,在声明一个变量后,你只可以它的作用域内访问这个变量。如果你将一个函数或一个文件放到一个子目录中,这个变量将不再被定义。你可以通过在变量声明末尾添加 PARENT_SCOPE 来将它的作用域置定为当前的上一级作用域。
set(MY_VARIABLE "value" PARENT_SCOPE)
MY_VARIABLE本地变量在父作用域中。
set(MY_LIST "one" "two")
set(MY_LIST "one;two")
列表就是简单地包含一系列变量,可以通过;分隔变量,这和空格的作用是一样的。
有一些和list(进行协同的命令,separate_arguments可以把一个以空格分隔的字符串分割成一个列表。需要注意的是,在CMake中如果一个值没有空格,那么加和不加引号的效果是一样的。这使你可以在处理知道不可能含有空格的值时不加引号。
当一个变量用${}括起来的时候,空格的解析规则和上述相同。对于路径来说要特别小心,路径很有可能会包含空格,因此你应该总是将解析变量得到的值用引号括起来,也就是应该这样"${MY_PATH}" 。
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/bin)
上面命令,必须在add_subdirectory的前面。
CMake中项目(project指令)和目标(add_library和add_executable指令),与Visual Studio中是不一样的,CMake 项目相当于Visual Studio解决方案,而CMake目标相当于Visual Studio一个一个的不同类型的项目。Visual Studio一个解决方案,可以包含多个项目。
我们非常关心解决方案的属性和项目的属性,包括 名称、版本、语言、路径、描述和网站。
如果用add_subdirectory命令,在子目录的CMakeLists.txt文件中,也有project命令,那么构建后,都会生成项目
我们用include()命令也可以加载 子目录中的CMakeLists.txt文件,例如:
include(sub/CMakeLists.txt)
虽然include()命令与 add_subdirectory()命令,都会加载CMakeLists.txt文件,但是include()命令是将子目录中CMakeLists.txt文件内容导入到父目录的源文件中,而 add_subdirectory() 命令是将子目录中 CMakeLists.txt 添加到构建中。
所以,有一个比较明显的区别,都执行project命令,如果使用include()那么项目会被覆盖,如果使用add_subdirectory()那么会创建子项目。
在编译完lib库后,需要手动拷贝lib文件、头文件给别人,做二次开发,这样相当麻烦。可以使用install指令,将生成的lib文件等拷贝到指定的位置。
配置库文件、头文件和执行文件到install的目录下,cmake中的install根目录为CMAKE_INSTALL_PREFIX变量的路径,如果我们要设置配置路径可以使用set命令设置CMAKE_INSTALL_PREFIX变量的值来改变路径。一般默认情况CMAKE_INSTALL_PREFIX变量的值为,在UNIX系统中为:/usr/local,在windows系统中为:c:/Program Files/${PROJECT_NAME}
打包项目指令如下:
# 设置打包目录
set(CMAKE_INSTALL_PREFIX ${PROJECT_BINARY_DIR})
# 配置可执行文件到安装路径 CMAKE_INSTALL_PREFIX的bin中
install(TARGETS glew DESTINATION glew-sdk/lib)
# 配置程序的头文件到安装路径 CMAKE_INSTALL_PREFIX的include文件中
install(FILES "include/GL/glew.h"
DESTINATION glew-sdk/include)
使用方法1
target_link_libraries ( app
debug ${Boost_FILESYSTEM_LIBRARY_DEBUG}
optimized ${Boost_FILESYSTEM_LIBRARY_RELEASE} )
使用方法2
set( MyFavLib_LIBRARIES
debug debug/module1 optimized release/module1
optimized debug/module2 optimized release/module2 )
target_link_libraries( app ${MyFavLib_LIBRARIES} )
注意,方法2中,module1和module2的lib后缀名要省略,vs会自动补全lib后缀名。
使用方法
if(NOT DEFINED CMAKE_DEBUG_POSTFIX)
set(CMAKE_DEBUG_POSTFIX "_debug")
endif()
if(NOT DEFINED CMAKE_RELEASE_POSTFIX)
set(CMAKE_RELEASE_POSTFIX "_release")
endif()
注意,该指令必须放在add_library之前,如下图所示。
include(${CMAKE_CURRENT_DIR}/../share.cmake)
(1) 设置exe输出目录
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
在Win + VS环境下,会自动在你所设置的目录后面扩展一层
(2) 设置lib输出目录
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../lib)
在Win + VS环境下,会自动在你所设置的目录后面扩展一层
(3) 设置debug和release输出目录
用法1
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/../bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/../bin)
上面两条语句分别设置了Debug版本和Release版本可执行文件的输出目录, 一旦设置上面的属性,在任何环境下生成的可执行文件都将直接放在你所设置的目录.
用法2
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/../lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/../lib)
上面两条语句分别设置了Debug版本和Release版本库文件的输出目录, 一旦设置上面的属性,在任何环境下生成的库文件都将直接放在你所设置的目录.
用法1
set(CMAKE_DEBUG_POSTFIX "_d")
set(CMAKE_RELEASE_POSTFIX "_r")
上面两条语句分别设置了Debug版本和Release版本下库文件的后缀名
用法2
set_target_properties(${TARGET_NAME} PROPERTIES DEBUG_POSTFIX "_d")
set_target_properties(${TARGET_NAME} PROPERTIES RELEASE_POSTFIX "_r")
上面两条语句分别设置了Debug版本和Release版本下可执行文件的后缀名
语法格式
message([<mode>] "message text" ...)
mode 的值包括 FATAL_ERROR、WARNING、AUTHOR_WARNING、STATUS、VERBOSE等。主要使用其中的 2 个——FATAL_ERROR、STATUS。
FATAL_ERROR:产生 CMake Error,会停止编译系统的构建过程;
STATUS:最常用的命令,常用于查看变量值,类似于编程语言中的 DEBUG 级别信息。
"message text"为显示在终端的内容。
使用set函数 + ENV指令
set(ENV{变量名} 值)
注意:
(1)读环境变量时,需要加上$;写环境变量时,不需要加$;
(2)cmake文件内定义的环境变量仅用于cmake编译过程,不能用于目标程序。
例子如下:
message("myenvvar: $ENV{myenvvar}")
# 定义环境变量myenvvar
set(ENV{myenvvar} "123")
message("myenvvar: $ENV{myenvvar}")
CMake中的option用于控制编译流程,相当于C语言中的宏条件编译。
options基本格式如下:
option(<variable> "<help_text>" [value])
variable:定义选项名称
help_text:说明选项的含义
value:定义选项默认状态,一般是OFF或者ON,除去ON之外,其他所有值都为认为是OFF。
if(expression)
#...
elseif(expression2)
#...
else()
#...
endif()
对于 if(string) 来说:
(1)如果 string 为(不区分大小写)1、ON、YES、TRUE、Y、非 0 的数则表示真
(2)如果 string 为(不区分大小写)0、OFF、NO、FALSE、N、IGNORE、空字符串、以 -NOTFOUND 结尾的字符串则表示假
(3)如果 string 不符合上面两种情况,则 string 被认为是一个变量的名字。变量的值为第二条所述的各值则表示假,否则表示真。
表达式中可以包含操作符,操作符包括:
(1) 一元操作符,例如:EXISTS、COMMAND、DEFINED 等
(2) 二元操作符,例如:EQUAL、LESS、GREATER、STRLESS、STREQUAL、STRGREATER 等
(3) NOT(非操作符)
(4) AND(与操作符)、OR(或操作符)
(5) 操作符优先级:一元操作符 > 二元操作符 > NOT > AND > OR
使用例子1
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)
# 当给定的变量或者字符串与给定的正则表达式相匹配时为真
使用例子2
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 表示的变量被定义了。
set(VAR a b c)
foreach(f ${VAR})
message(${f})
endforeach()
set(VAR 5)
while(${VAR} GREATER 0)
message(${VAR})
math(EXPR VAR "${VAR} - 1")
endwhile()
list (subcommand <list> [args...])
subcommand为具体的列表操作子命令,例如读取、查找、修改、排序等;
<list>为待操作的列表变量;
[args...]为对列表变量操作需要使用的参数表,不同的子命令对应的参数也不一致。
list命令即对列表的一系列操作,cmake中的列表变量是用分号;分隔的一组字符串,创建列表可以使用set命令(参考set命令),例如:set (var a b c d)创建了一个列表 "a;b;c;d",而set (var "a b c d")则是只创建了一个变量"a c c d"。list命令的具体格式根据子命令不同会有所区别。
对列表的操作分为读取、查找、修改、排序等4个大类,下面按照这四个大类逐一对列表的子命令进行介绍。
LENGTH:子命令LENGTH用于读取列表长度
list (LENGTH <list> <output variable>)
<output variable>为新创建的变量,用于存储列表的长度。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d) # 创建列表变量"a;b;c;d"
list (LENGTH list_test length)
message (">>> LENGTH: ${length}")
输出:>>> LENGTH: 4
GET:子命令GET用于读取列表中指定索引的的元素,可以指定多个索引。
list (GET <list> <element index> [<element index> ...] <output variable>)
<element index>为列表元素的索引,从0开始编号,索引0的元素为列表中的第一个元素;索引也可以是负数,-1表示列表的最后一个元素,-2表示列表倒数第二个元素,以此类推。注意:当索引(不管是正还是负)超过列表的长度,运行会报错(list index: XX out of range)。
<output variable>为新创建的变量,存储指定索引元素的返回结果,也是一个列表。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d) # 创建列表变量"a;b;c;d"
list (GET list_test 0 1 -1 -2 list_new)
message (">>> GET: ${list_new}")
输出:>>> GET: a;b;d;c
JOIN:子命令JOIN用于将列表中的元素用连接字符串连接起来组成一个字符串,注意,此时返回的结果已经不是一个列表。
list (JOIN <list> <glue> <output variable>)
将列表中的元素用<glue>链接起来,组成一个字符串后,返回给<output variable>变量。对于不属于列表的多个字符串的连接操作,可以使用string()命令的连接操作。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d) # 创建列表变量"a;b;c;d"
list (JOIN list_test -G- list_new)
message ("<<< JOIN: ${list_new}")
输出:<<< JOIN: a-G-b-G-c-G-d
SUBLIST:子命令SUBLIST用于获取列表中的一部分(子列表)。
list (SUBLIST <list> <begin> <length> <output variable>)
返回列表<list>中,从索引<begin>开始,长度为<length>的子列表。如果长度<length>为0,返回的时空列表。如果长度<length>为-1或列表的长度小于<begin>+<length>,那么将列表中从<begin>索引开始的剩余元素返回。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d) # 创建列表变量"a;b;c;d"
list (SUBLIST list_test 1 2 list_new)
message (">>> SUBLIST: ${list_new}")
list (SUBLIST list_test 1 0 list_new)
message (">>> SUBLIST: ${list_new}")
list (SUBLIST list_test 1 -1 list_new)
message (">>> SUBLIST: ${list_new}")
list (SUBLIST list_test 1 8 list_new)
message (">>> SUBLIST: ${list_new}")
输出:
>>> SUBLIST: b;c
>>> SUBLIST:
>>> SUBLIST: b;c;d
>>> SUBLIST: b;c;d
FIND:子命令FIND用于查找列表是否存在指定的元素。
list (FIND <list> <value> <output variable>)
如果列表<list>中存在<value>,那么返回<value>在列表中的索引,如果未找到则返回-1。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d) # 创建列表变量"a;b;c;d"
list (FIND list_test d list_index)
message (">>> FIND 'd': ${list_index}")
list (FIND list_test e list_index)
message (">>> FIND 'e': ${list_index}")
输出:
>>> FIND 'd': 3
>>> FIND 'e': -1
列表的修改子命令可能会改变原列表的值。
APPEND:子命令APPEND用于将元素追加到列表。
list (APPEND <list> [<element> ...])
此命令会改变原列表的值。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d) # 创建列表变量"a;b;c;d"
list (APPEND list_test 1 2 3 4)
message (">>> APPEND: ${list_test}")
输出:>>> APPEND: a;b;c;d;1;2;3;4
FILTER:子命令FILTER用于根据正则表达式包含或排除列表中的元素。
list (FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
根据模式的匹配结果,将元素添加(INCLUDE选项)到列表或者从列表中排除(EXCLUDE选项)。此命令会改变原来列表的值。模式REGEX表明会对列表进行正则表达式匹配。(从官方文档目前未找到其他模式)
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d 1 2 3 4) # 创建列表变量"a;b;c;d;1;2;3;4"
message (">>> the LIST is: ${list_test}")
list (FILTER list_test INCLUDE REGEX [a-z])
message (">>> FILTER: ${list_test}")
list (FILTER list_test EXCLUDE REGEX [a-z])
message (">>> FILTER: ${list_test}")
输出:
>>>the LIST is: a;b;c;d;1;2;3;4
>>>FILTER: a;b;c;d
>>>FILTER:
INSERT:子命令INSERT用于在指定位置将元素(一个或多个)插入到列表中。
list (INSERT <list> <element_index> <element> [<element> ...])
<element_index>为列表指定的位置,如果元素的位置超出列表的范围,会报错。此命令会改变原来列表的值
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d) # 创建列表变量"a;b;c;d"
list (INSERT list_test 0 8 8 8 8)
message (">>> INSERT: ${list_test}")
list (INSERT list_test -1 9 9 9 9)
message (">>> INSERT: ${list_test}")
list (LENGTH list_test lenght)
list (INSERT list_test ${length} 0)
message (">>> INSERT: ${list_test}")
输出:
>>> INSERT: 8;8;8;8;a;b;c;d
>>> INSERT: 8;8;8;8;a;b;c;9;9;9;9;d
>>> INSERT: 8;8;8;8;a;b;c;9;9;9;9;d;0
POP_BACK:子命令POP_BACK用于将列表中最后元素移除。
list (POP_BACK <list> [<out-var>...])
<out-var>如果未指定输出变量,则仅仅是将原列表的最后一个元素移除。如果指定了输出变量,则会将最后一个元素移入到该变量,并将元素从原列表中移除。如果指定了多个输出变量,则依次将原列的最后一个元素移入到输出变量中,如果输出变量个数大于列表的长度,那么超出部分的输出变量未定义。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d) # 创建列表变量"a;b;c;d"
list (POP_BACK list_test)
message (">>> POP_BACK: ${list_test}")
list (POP_BACK list_test outvar1 outvar2 outvar3 outvar4)
message (">>> POP_BACK: ${outvar1}、${outvar2}、${outvar3}、${outvar4}")
输出:
>>> POP_BACK: a;b;c
>>> POP_BACK: c、b、a、
POP_FRONT:子命令POP_FRONT用于将列表中第一个元素移除。
list (POP_FRONT <list> [<out-var>...])
<out-var>如果未指定输出变量,则仅仅是将原列表的第一个元素移除。如果指定了输出变量,则会将第一个元素移入到该变量,并将元素从原列表中移除。如果指定了多个输出变量,则依次将原列的第一个元素移入到输出变量中,如果输出变量个数大于列表的长度,那么超出部分的输出变量未定义。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d) # 创建列表变量"a;b;c;d"
list (POP_FRONT list_test)
message (">>> POP_FRONT: ${list_test}")
list (POP_FRONT list_test outvar1 outvar2 outvar3 outvar4)
message (">>> POP_FRONT: ${outvar1}、${outvar2}、${outvar3}、${outvar4}")
输出:
>>> POP_FRONT: b;c;d
>>> POP_FRONT: b、c、d、
PREPEND:子命令PREPEND用于将元素插入到列表的0索引位置。
list (PREPEND <list> [<element> ...])
如果待插入的元素是多个,则相当于把多个元素整体“平移”到原列表0索引的位置。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d) # 创建列表变量"a;b;c;d"
list (PREPEND list_test z)
message (">>> PREPEND: ${list_test}")
list (PREPEND list_test p q r s t)
message (">>> POP_FRONT: ${list_test}")
输出:
>>> PREPEND: z;a;b;c;d
>>> PREPEND: p;q;r;s;t;z;a;b;c;d
REMOVE_ITEM:子命令REMOVE_ITEM用于将指定的元素从列表中移除。
list (REMOVE_ITEM <list> <value> [<value> ...])
注意:指定的是元素的值,当指定的值在列表中存在重复的时候,会删除所有重复的值。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a a b c c d) # 创建列表变量"a;a;b;c;c;d"
list (REMOVE_ITEM list_test a)
message (">>> REMOVE_ITEM: ${list_test}")
list (REMOVE_ITEM list_test b e)
message (">>> REMOVE_ITEM: ${list_test}")
输出:
>>> REMOVE_ITEM: b;c;c;d
>>> REMOVE_ITEM: c;c;d
REMOVE_AT:子命令REMOVE_AT用于将指定索引的元素从列表中移除。
list (REMOVE_AT <list> <index> [<index> ...])
注意:指定的是元素的索引,当指定的索引不存在的时候,会提示错误;如果指定的索引存在重复,则只会执行一次删除动作。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d e f) # 创建列表变量"a;b;c;d;e;f"
list (REMOVE_AT list_test 0 -1)
message (">>> REMOVE_AT: ${list_test}")
list (REMOVE_AT list_test 1 1 1 1)
message (">>> REMOVE_AT: ${list_test}")
输出:
>>> REMOVE_AT: b;c;d;e
>>> REMOVE_AT: b;d;e
REMOVE_DUPLICATES:子命令REMOVE_DUPLICATES用于移除列表中的重复元素。
list (REMOVE_DUPLICATES <list>)
如果没有重复元素,原列表不会做更改。如果原列表所有元素都是一样的,则会保留一个;如果有多个重复元素,则保留第一个。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a a a b b b c c c d d d) # 创建列表变量"a;a;a;b;b;b;c;c;c;d;d;d;"
list (REMOVE_DUPLICATES list_test)
message (">>> REMOVE_DUPLICATES: ${list_test}")
set (list_test a a a a)
list (REMOVE_DUPLICATES list_test)
message (">>> REMOVE_DUPLICATES: ${list_test}")
set (list_test a b c a d a e a f) # 多个元素重复,只保留第一个
list (REMOVE_DUPLICATES list_test)
message (">>> REMOVE_DUPLICATES: ${list_test}")
输出:
>>> REMOVE_DUPLICATES: a;b;c;d
>>> REMOVE_DUPLICATES: a
>>> REMOVE_DUPLICATES: a;b;c;d;e;f
TRANSFORM:子命令TRANSFORM用于将指定的动作运用到所有或者部分指定的元素,结果可以存到原列表中,或存到指定输出新的变量中。
list (TRANSFORM <list> <ACTION> [<SELECTOR>]
[OUTPUT_VARIABLE <output variable>])
选项ACTION用于指定应用到列表元素的动作,动作必须在如下中选择一个:APPEND/PREPEND/TOUPPER/TOUPPER/STRIP/GENEX_STRIP/REPLACE;选项SELECTOR用于指定哪些列表元素会被选择,并执行动作,SELECTOR只能从如下中选择一个:AT/FOR/REGEX;选项OUTPUT_VARIABLE用于指定新的输出变量。 TRANSFORM命令不会改变列表中元素的个数。
ACTION选项的解析如下:
APPEND,PREPEND:在列表的每个元素后/前插入指定的值。
list (TRANSFORM <list> <APPEND|PREPEND> <value> [OUTPUT_VARIABLE <output variable>])
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d)
list (TRANSFORM list_test APPEND B OUTPUT_VARIABLE list_test_out)
list (TRANSFORM list_test APPEND B)
message (">>> TRANSFORM-APPEND: ${list_test} --- ${list_test_out}")
set (list_test a b c d)
list (TRANSFORM list_test PREPEND F OUTPUT_VARIABLE list_test_out)
list (TRANSFORM list_test PREPEND F)
message (">>> TRANSFORM-PREPEND: ${list_test} --- ${list_test_out}")
输出:
>>> TRANSFORM-APPEND: aB;bB;cB;dB --- aB;bB;cB;dB
>>> TRANSFORM-PREPEND: Fa;Fb;Fc;Fd --- Fa;Fb;Fc;Fd
TOUPPER, TOLOWER:将列表的每一个元素转换成大写/小写。
list(TRANSFORM <list> <TOLOWER|TOUPPER> [OUTPUT_VARIABLE <output variable>])
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a bb c d)
list (TRANSFORM list_test TOUPPER OUTPUT_VARIABLE list_test_out)
list (TRANSFORM list_test TOUPPER)
message (">>> TRANSFORM-TOUPPER: ${list_test} --- ${list_test_out}")
set (list_test A B C DD)
list (TRANSFORM list_test TOLOWER OUTPUT_VARIABLE list_test_out)
list (TRANSFORM list_test TOLOWER)
message (">>> TRANSFORM-TOLOWER: ${list_test} --- ${list_test_out}")
输出:
>>> TRANSFORM-TOUPPER: A;BB;C;D --- A;BB;C;D
>>> TRANSFORM-TOLOWER: a;b;c;dd --- a;b;c;dd
STRIP:去除列表中每一个元素的头尾空格。
list(TRANSFORM <list> STRIP [OUTPUT_VARIABLE <output variable>])
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test " a b " " bb " "c e" d)
message (">>> TRANSFORM list: ${list_test}")
list (TRANSFORM list_test STRIP OUTPUT_VARIABLE list_test_out)
list (TRANSFORM list_test STRIP)
message (">>> TRANSFORM-STRIP: ${list_test} --- ${list_test_out}")
输出:
>>> TRANSFORM list: a b ; bb ;c e;d
>>> TRANSFORM-STRIP: a b;bb;c e;d --- a b;bb;c e;d
GENEX_STRIP:裁剪列表中任何为产生表达式的元素(产生表达式请参考此处)从下面的例子可以看出,虽然列表中是产生表达式的元素被清除了,但是实际列表的长度没有变化,产生表达式的位置被空元素占据了(TRANSFROM命令不会改变列表元素的个数)。
list(TRANSFORM <list> GENEX_STRIP [OUTPUT_VARIABLE <output variable>])
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d $ $)
list (LENGTH list_test len)
message (">>> TRANSFORM list: ${list_test}, len=${len}")
list (TRANSFORM list_test GENEX_STRIP OUTPUT_VARIABLE list_test_out)
list (TRANSFORM list_test GENEX_STRIP)
list (LENGTH list_test len_new)
message (">>> TRANSFORM-STRIP: ${list_test} --- ${list_test_out}, new len = ${len_new}")
输出:
>>> TRANSFORM list: a;b;c;d;$
>>> TRANSFORM-STRIP: a;b;c;d;; --- a;b;c;d;;, new len = 6
REPLACE:尽可能多的查找(用正则表达式<regular_expression>匹配)并替换(用<replace_expression>替换)列表中符合条件的元素。
list(TRANSFORM <list> REPLACE <regular_expression> <replace_expression> [OUTPUT_VARIABLE<output variable>])
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test aa bb cc dd 1a b2 33 44)
message (">>> TRANSFORM-list: ${list_test}")
list (TRANSFORM list_test REPLACE "[a-c]" 9)
message (">>> TRANSFORM-REPLACE: ${list_test}")
输出:
>>> TRANSFORM-list: aa;bb;cc;dd;1a;b2;33;44
>>> TRANSFORM-REPLACE: 99;99;99;dd;19;92;33;44
SELECTOR选项的解析如下:
AT:指定元素的索引(可以是多个)。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d)
message (">>> TRANSFORM-list: ${list_test}")
list (TRANSFORM list_test APPEND Q AT 1 2 OUTPUT_VARIABLE list_test_out)
list (TRANSFORM list_test APPEND Q AT 1 2)
message (">>> TRANSFORM-AT: ${list_test} --- ${list_test_out}")
输出:
>>> TRANSFORM-list: a;b;c;d
>>> TRANSFORM-REPLACE: a;bQ;cQ;d --- a;bQ;cQ;d
FOR:指定元素的范围和迭代的步长。
list(TRANSFORM <list> <ACTION> FOR <start> <stop> [<step>] [OUTPUT_VARIABLE <output variable>])
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d e f g h i j k)
message (">>> TRANSFORM-list: ${list_test}")
list (TRANSFORM list_test APPEND Q FOR 0 -1 2 OUTPUT_VARIABLE list_test_out)
list (TRANSFORM list_test APPEND Q FOR 0 -1 2)
message (">>> TRANSFORM-FOR: ${list_test} --- ${list_test_out}")
输出:
>>> TRANSFORM-list: a;b;c;d;e;f;g;h;i;j;k
>>> TRANSFORM-REPLACE: aQ;b;cQ;d;eQ;f;gQ;h;iQ;j;kQ --- aQ;b;cQ;d;eQ;f;gQ;h;iQ;j;kQ
REGEX:指定正则表达式,符合正则表达式的元素才会被处理。/p>
list(TRANSFORM <list> <ACTION> REGEX <regular_expression>[OUTPUT_VARIABLE <output variable>])
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test a b c d e f g h i j k)
message (">>> TRANSFORM-list: ${list_test}")
list (TRANSFORM list_test APPEND Q REGEX "[a-f]" OUTPUT_VARIABLE list_test_out)
list (TRANSFORM list_test APPEND Q REGEX "[a-f]")
message (">>> TRANSFORM-REGEX: ${list_test} --- ${list_test_out}")
输出:
>>> TRANSFORM-list: a;b;c;d;e;f;g;h;i;j;k
>>> TRANSFORM-REGEX: aQ;bQ;cQ;dQ;eQ;fQ;g;h;i;j;k --- aQ;bQ;cQ;dQ;eQ;fQ;g;h;i;j;k
REVERSE:子命令REVERSE用于将整个列表反转。
list (REVERSE <list>)
反转列表。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test aa bb cc dd)
message (">>> list: ${list_test}")
list (REVERSE list_test)
message (">>> REVERSE: ${list_test}")
输出:
>>> list: aa;bb;cc;dd
>>> REVERSE: dd;cc;bb;aa
SORT:子命令SORT用于对列表进行排序。
list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
对列表进行排序。
COMPARE:指定排序方法。有如下几种值可选:1)STRING:按照字母顺序进行排序,为默认的排序方法;2)FILE_BASENAME:如果是一系列路径名,会使用basename进行排序;3)NATURAL:使用自然数顺序排序。
CASE:指明是否大小写敏感。有如下几种值可选:1)SENSITIVE:按照大小写敏感的方式进行排序,为默认值;2)INSENSITIVE:按照大小写不敏感方式进行排序。
ORDER:指明排序的顺序。有如下几种值可选:1)ASCENDING:按照升序排列,为默认值;2)DESCENDING:按照降序排列。
# CMakeLists.txt
cmake_minimum_required (VERSION 3.12.2)
project (list_cmd_test)
set (list_test 3 1 1.1 10.1 3.4 9)
list (SORT list_test) # 以字母顺序,按照大小写敏感方式,升序排列
message (">>> SORT STRING-SENSITIVE-ASCENDING: ${list_test}")
list (SORT list_test COMPARE NATURAL ORDER DESCENDING) # 以自然顺序,降序排列
message (">>> SORT STRING-SENSITIVE-DESCENDING: ${list_test}")
输出:
>>> SORT STRING-SENSITIVE-ASCENDING: 1;1.1;10.1;3;3.4;9
>>> SORT STRING-SENSITIVE-DESCENDING: 10.1;9;3.4;3;1.1;1
mark_as_advanced 将CMake 的缓存变量标记为高级。
mark_as_advanced([CLEAR|FORCE] VAR VAR2 VAR...)
将缓存的变量标记为高级变量。其中,高级变量指的是那些在CMake GUI中,只有当“显示高级选项”被打开时才会被显示的变量。如果CLEAR是第一个选项,参数中的高级变量将变回非高级变量。如果FORCE是第一个选项,参数中的变量会被提升为高级变量。如果两者都未出现,新的变量会被标记为高级变量;如果这个变量已经是高级/非高级状态的话,它将会维持原状。
该命令在脚本中无效。
add_compile_options是设置编译选项指令
(1) 设置utf-8编码
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
(2) 设置使用c++11标准
add_compile_options(-std=c++11)
有时候,一个项目中,文件很多,我们想让文件看起来整洁一下,就需要将文件进行分组
file(GLOB LOG "log/*.h" "log/*.cpp")
file(GLOB LOGWIN "log/logan-win/*.h" "log/logan-win/*.cpp")
file(GLOB CLOGAN "log/logan-win/clogan/*.h" "log/logan-win/clogan/*.cpp")
file(GLOB MBEDTLS_INCLUDE "log/logan-win/mbedtls/include/mbedtls/*.h" )
file(GLOB MBEDTLS_LIB "log/logan-win/mbedtls/library/*.h" "log/logan-win/mbedtls/library/*.cpp")
set(group group1.h group1.cpp main.cpp)
source_group(group FILES ${group})
source_group(group/log FILES ${LOG})
source_group(group/log/logan-win FILES ${LOGWIN})
source_group(group/log/logan-win/clogan FILES ${CLOGAN})
source_group(group/log/logan-win/mbedtls/include FILES ${MBEDTLS_INCLUDE})
source_group(group/log/logan-win/mbedtls/library FILES ${MBEDTLS_LIB})
add_executable(${PROJECT_NAME}
${group} ${LOG} ${LOGWIN} ${CLOGAN} ${MBEDTLS_INCLUDE} ${MBEDTLS_LIB})
在使用CMake后,手工维护filter是不现实的。CMake也提供了可以生成filter的机制,就是source_group()命令。结合source_group(), file(), string()等命令,我们可以让实现CMake自动按目录结构生成filter。
(1) 在比较顶层的CMakeLists.txt中定义该宏
定义宏代码:
macro(source_group_by_dir source_files)
if(MSVC)
set(sgbd_cur_dir ${CMAKE_CURRENT_SOURCE_DIR})
foreach(sgbd_file ${${source_files}})
string(REGEX REPLACE ${sgbd_cur_dir}/\(.*\) \\1 sgbd_fpath ${sgbd_file})
string(REGEX REPLACE "\(.*\)/.*" \\1 sgbd_group_name ${sgbd_fpath})
string(COMPARE EQUAL ${sgbd_fpath} ${sgbd_group_name} sgbd_nogroup)
string(REPLACE "/" "\\" sgbd_group_name ${sgbd_group_name})
if(sgbd_nogroup)
set(sgbd_group_name "\\")
endif(sgbd_nogroup)
source_group(${sgbd_group_name} FILES ${sgbd_file})
endforeach(sgbd_file)
endif(MSVC)
endmacro(source_group_by_dir)
(2) 使用宏
在添加工程(add_library或者add_executable)的CMakeLists.txt文件中调用该宏,调用如下:
source_group_by_dir(all_files)
其中all_files是保存了所有文件名的变量。注意,这里用的是变量名,而没有引用其值。
一般这个文件列表可以用file()或者aux_source_directory()来得到。例如对于C++工程,通常是这样的:
file(GLOB_RECURSE project_headers *.h)
file(GLOB_RECURSE project_cpps *.cpp)
set(all_files ${project_headers} ${project_cpps})
source_group_by_dir(all_files)
有时候,一个项目中,可能有很多工程,工程多了后,就会显得杂乱无章,此时我们需要对工程进行分组
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "4") # 分组名称为4
变量 | 作用 |
---|---|
${CMAKE_CURRENT_SOURCE_DIR} | 当前CMakeLists.txt文件所在目录 |
${CMAKE_CURRENT_BINARY_DIR} | 当前CMakeLists.txt构建输出所在的目录 |
${CMAKE_SOURCE_DIR} | 顶级CMakeLists.txt文件所在路径 |
${CMAKE_BINARY_DIR} | 顶级CMakeLists.txt构建输出所在的目录 |
${PROJECT_SOURCE_DIR} | 项目源码顶层目录 |
${PROJECT_BINARY_DIR} | 项目编译输出目录 |
${PROJECT_NAME} | 项目名称 |
${CMAKE_VERSION} | CMake的版本,由 MAJOR、MINOR、PATCH、TWEAK组成 |
${CMAKE_MAJOR_VERSION} | CMake的主要版本,假设CMake的版本是3.21,那么3就表示主要版本,21表示次要版本 |
${CMAKE_MINOR_VERSION} | CMake的次要版本 |
${CMAKE_PATCH_VERSION} | CMake的补丁版本 |
${CMAKE_TWEAK_VERSION} | CMake的微调版本 |