1、介绍
cmake是一个跨平台的安装、编译工具,可以用简单的语句来描述所有平台的安装、编译过程,他能够输出各种各样的makefile或者project文件。与 CMake 所对应的文件名为 CMakeLists.txt,使用cmake命令可以根据CMakeLists.txt文件生成当前环境下的Makefile文件。
Cmake 并不直接建构出最终的软件,而是产生标准的建构档(例如在 Unix 生成Makefile文件,在 Windows Visual C++ 环境下生成projects/workspaces文件),然后再用一般的建构方式使用
cmake全名为:cross platform make,又称为交叉编译,是kitware 公司以及一些开源开发者在开发几个工具套件(VTK)的过程中衍生品,最终形成体系,成为一个独立的开放源代码项目。项目的诞生时间是 2001 年
cmake主要支持C、C++、JAVA,其他语言不建议使用
2、特点
(1)开放源代码
(2)跨平台,并可生成 native 编译配置文件,在 Linux/Unix 平台,生成 makefile,在macOS下,可以生成 xcode,在 Windows 平台,可以生成 MSVC 的工程文件
(3)能够管理大型项目
(4)简化编译构建过程和编译过程。Cmake 的工具链非常简单:cmake+make
(5)效率高
(6)可扩展,可以为 cmake 编写特定功能的模块,扩充 cmake 功能
(7)编程风格的灵活性,主要体现在以下几点:
1)多个参数可以使用空格、分号隔开,但是要注意保持编程的风格的一致性
举例:ADD_EXECUTABLE(hello main.c func.c) 、 ADD_EXECUTABLE(hello main.c;func.c)
2)指令不区分大小写,但是建议指令用大写形式
3)设置变量时双引号的使用
当变量内部有空格时必须使用双引号,若没有可以不使用
举例:SET(SRC_LIST "m ain.c"),必须使用双引号
MESSAGE(STATUS “This is BINARY dir” ${PROJECT_BINARY_DIR})
MESSAGE(STATUS “This is BINARY dir ${PROJECT_BINARY_DIR}”),两个都可以
3、与其它make工具的对比
较为常见的make工具主要有GNU make、qt qmake、微软 nmake三种
(1)GNU make,简称make,对应Makefile文件,Makefile文件的编译与当前的系统有关,所以在设计到扩平台编译时较为繁琐
(2)微软的nmake,仅适用于windows平台,nmake在安装visual studio时会一起被安装
使用vs可以自动帮用户管理源文件的编译,很少单独使用nmake命令
(3)qt的qmake,qmake是QT的附加工具之一,使用它能够构建出Makefile、vs工程文件(vcxproj文件),即使源代码不是用QT编写的,如果仅仅使用 qt 编程,没有必要使用 cmake,因为 qmake 管理 Qt 工程的专业性和自动化程度比 cmake 要高很多
4、cmake基本命令介绍
cmake的命令不区别字母大小写,但是一般将cmake命令大写,要求每个要编译的目录内都必须有cmakelists.txt文件
(1)PROJECT(projectname [C] [CXX] [JAVA] ),设置工程的名字,后边可以设置工程所支持的语言,支持的语言可以省略
这个指令隐式的定义了两个 cmake 变量:<projectname>_BINARY_DIR 以及<projectname>_SOURCE_DIR,在这里就是HELLO_BINARY_DIR、HELLO_SOURCE_DIR
同时 cmake 系统也帮助我们预定义了 PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR变量,他们的值分别跟 HELLO_BINARY_DIR 与 HELLO_SOURCE_DIR 一致,建议使用PROJECT_BINARY_DIR 、PROJECT_SOURCE_DIR,这样即使修改了工程的名字,也不用修改此处的代码
(2)SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]]) 设置变量
举例说明:
SET(SRC_LIST main.c)、SET(SRC_LIST "main.c")
当上边中的字符串中不能有空格时,也可以不加引号,但是当中间有空格时候必须加上双引号
分别为: 设置一般变量(Set Normal Variable), 设置缓存变量(Set Cache Entry),设置环境变量(Set Environment Variable)
SET有三种用法,分别为: 设置一般变量(Set Normal Variable), 设置缓存变量(Set Cache Entry),设置环境变量(Set Environment Variable)(参考:https://www.cnblogs.com/gaox97329498/p/10986476.html)
// 1. 设置一般变量
(Set Normal Variable) set(<variable> <value>... [PARENT_SCOPE])
PARENT_SCOPE:配置该选项后,表示该变量在父级作用域上有效, 在当前作用域上是无效的
// 2. 设置缓存变量(Set Cache Entry)
set(<variable> <value>... CACHE <type> <docstring> [FORCE])
设置变量并缓存到 CMakeCache.txt
默认不会覆盖已缓存(已存在于 CMakeCache.txt )的变量;
type 指定变量的类型,
述字符串 <docstring>: 单行文字,用于 CMAKE-GUI 的时提示用户
FORCE 用于是否强制更新缓存里面的值,配置后,每次都会强制更新 CMakeCache.txt 里面的值
// 3. 设置环境变量(Set Environment Variable)
set(ENV{<variable>} [<value>])
(3)${PROJECT_BINARY_DIR},引用PROJECT_BINARY_DIR变量的值,注意在IF语句中,直接使用变量名字PROJECT_BINARY_DIR即可
(4)MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR | WARNING] ${HELLO_BINARY_DIR}),输出状态信息
SEND_ERROR,产生错误,生成过程被跳过
SATUS,输出前缀为—的信息
FATAL_ERROR,立即终止所有 cmake 过程
WARNING,警告信息
(5)ADD_EXECUTABLE(demo ${SRC_LIST})
定义了这个工程会生成一个文件名为 demo 的可执行文件,相关的源文件是 SRC_LIST 中定义的源文件列表
也可以直接写成: ADD_EXECUTABLE(hello main.c)
(6)引用变量的方式:${SRC_LIST},但是在 IF 控制语句中是直接使用变量名
(7)ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个指令用于向当前工程添加存放源文件的子目录(内有cmakelists文件的目录),并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除,比如,工程的 example,可能就需要工程构建完成后,再进入 example 目录单独进行构建
(8)ADD_LIBRARY(demo [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN),构建库文件
SHARED:构建动态共享库.so
STATIC:构建静态库.a
MODULE,在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待
注意:生成的文件名字为libdemo.so或libdemo.a,而不是demo.so或demo.a
(9)INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是使用参数AFTER追加到当前的头文件搜索路径的后面,使用参数BEFORE可以将此路径加到已有路径的前边
(9)LINK_DIRECTORIES(directory1 directory2 ...)
添加非标准的共享库搜索路径
(10)TARGET_LINK_LIBRARIES(target library1 <debug | optimized> library2 ...)
添加需要链接的共享库,其中target是PROJECT设置的项目名字
(11)INSTALL指令包含了目标二进制、动态库、静态库以及文件、目录、脚本等文件安装,下面分写介绍
INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
] [...])
上边是目标文件的安装,参数中的 TARGETS 后面跟的就是我们通过 ADD_EXECUTABLE 或者 ADD_LIBRARY 定义的目标文件,可能是可执行二进制、动态库、静态库,其中ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME特指可执行目标二进制
参数DESTINATION 定义了安装的路径,代表CMAKE_INSTALL_PREFIX变量的值,在编译时用cmake -DCMAKE_INSTALL_PREFIX=
这种方式指定DESTINATION的值
INSTALL(FILES files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
可用于安装一般文件,并可以指定访问权限,文件名是此指令所在路径下的相对路径。如果默认不定义权限 PERMISSIONS,安装后的权限为:OWNER_WRITE, OWNER_READ, GROUP_READ,和 WORLD_READ,即 644 权限。
INSTALL(PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
非目标文件的可执行程序安装,比如脚本,跟上面的 FILES 指令使用方法一样,唯一的不同是安装后权限为:OWNER_EXECUTE, GROUP_EXECUTE, 和 WORLD_EXECUTE,即 755 权限
INSTALL(DIRECTORY dirs... DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])
上边是目录的安装,DIRECTORY 后面是 source 文件相对于 INSTALL 命令所在目录的相对路径,在写dirs参数值时务必注意:abc 和 abc/有很大的区别,不加/是将整个目录拷贝过去,加上/是将目录下的内存拷贝过去
PATTERN 用于使用正则表达式进行过滤,PERMISSIONS 用于指定 PATTERN 过滤后的文件权限。
(12)SET_TARGET_PROPERTIES(target1 target2 ... PROPERTIES prop1 value1 prop2 value2 ...)
主要用来设置目标文件的一些属性
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1) 设置生成库的版本信息,VERSION 指代动态库版本,SOVERSION 指代 API 版本
(13)GET_TARGET_PROPERTY(VAR target property)
获取目标文件的指定属性
(14)OPTION(<option_variable> "help string describing option" [initial value])
为变量设置默认值,而且这个变量可以在代码中使用这个默认值可以在使用cmake进行更改,例如:
cmake -D<option_variable>=OFF .
在CMakeList.txt文件中写
option(BUILD_SHARED "build shared library" ON)这种,就是定义了默认值,如果不在执行cmake时制定BUILD_SHARED 值,则它的值就是ON
参考:https://www.jianshu.com/p/035bc18f8f62、https://www.cnblogs.com/the-capricornus/p/4717566.html
请注意:这个option命令和你本地是否存在编译缓存的关系很大。 所以,如果你有关于 option 的改变,那么请务必清理 CMakeCache.txt 和 CMakeFiles 文件夹
(14)CONFIGURE_FILE:将一份文件拷贝到另一个位置并修改它的内容,语法如下:
configure_file(<input> <output>
[COPYONLY] [ESCAPE_QUOTES] [@ONLY]
[NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
拷贝一个 <input>(输入文件) 文件到 <output> (输出文件),并且替换输入文件中被 @VAR@ 或者 ${VAR} 引用的变量值。该变量将被替换成当前的变量值(注:CMake中的变量值)或者空串当变量未定义。
参考:https://www.cnblogs.com/the-capricornus/p/4717566.html
COPYONLY
Copy the file without replacing any variable references or other content. This option may not be used with NEWLINE_STYLE.
只拷贝文件,不进行任何的变量替换。这个选项在指定了 NEWLINE_STYLE 选项时不能使用(无效)。
ESCAPE_QUOTES
Escape any substituted quotes with backslashes (C-style).
躲过任何的反斜杠(C风格)转义。
注:躲避转义,比如你有个变量在CMake中是这样的
set(FOO_STRING "\"foo\"")
那么在没有 ESCAPE_QUOTES 选项的状态下,通过变量替换将变为 ""foo"",如果指定了 ESCAPE_QUOTES 选项,变量将不变。
@ONLY
Restrict variable replacement to references of the form @VAR@. This is useful for configuring scripts that use ${VAR} syntax.
限制变量替换,让其只替换被 @VAR@ 引用的变量(那么 ${VAR} 格式的变量将不会被替换)。这在配置 ${VAR} 语法的脚本时是非常有用的。
NEWLINE_STYLE <style>
Specify the newline style for the output file. Specify UNIX or LF for \n newlines, or specify DOS, WIN32, or CRLF for \r\n newlines. This option may not be used with COPYONLY.
指定输出文件中的新行格式。UNIX 和 LF 的新行是 \n ,DOS 和 WIN32 和 CRLF 的新行格式是 \r\n 。 这个选项在指定了 COPYONLY 选项时不能使用(无效)。
拷贝一个 <input>(输入文件) 文件到 <output> (输出文件),并且替换输入文件中被 @VAR@ 或者 ${VAR} 引用的变量值,该变量将被替换成当前的变量值(注:CMake中的变量值)或者空串当变量未定义。
参考:https://www.cnblogs.com/the-capricornus/p/4717566.html
举例:
Consider a source tree containing a foo.h.in file:
#cmakedefine FOO_ENABLE
#cmakedefine FOO_STRING "@FOO_STRING@"
An adjacent CMakeLists.txt may use configure_file to configure the header:
option(FOO_ENABLE "Enable Foo" ON)
if(FOO_ENABLE)
set(FOO_STRING "foo")
endif()
configure_file(foo.h.in foo.h @ONLY)
This creates a foo.h in the build directory corresponding to this source directory. If the FOO_ENABLE option is on, the configured file will contain:
#define FOO_ENABLE
#define FOO_STRING "foo"
Otherwise it will contain:
/* #undef FOO_ENABLE */
/* #undef FOO_STRING */
One may then use the include_directories() command to specify the output directory as an include directory:
include_directories(${CMAKE_CURRENT_BINARY_DIR})
so that sources may include the header as #include <foo.h>.
(15)设置编译选项
有两种方法:(参考https://blog.csdn.net/10km/article/details/51731959)
一是使用 ADD_COMPILE_OPTIONS 添加编译选项,需要注意的是会改变所有的编译器的编译设置,比如:
IF(CMAKE_COMPILER_IS_GNUCXX)
ADD_COMPILE_OPTIONS(-std=c++11 -O2 -Wall)
ENDIF(CMAKE_COMPILER_IS_GNUCXX)
若是gcc编译器则则加入 C++11 标准,并且将编译器优化级别设定为2,打开所有警告信息的提示
(C++编译选项参考:https://blog.csdn.net/X_And_Y/article/details/81301555)
二是使用 SET 命令设置 CMAKE_CXX_FLAGS 、CMAKE_C_FLAGS 的值,好处是仅仅会修改C、C++编译器的选项
IF(CMAKE_COMPILER_IS_GNUCXX)
SET(CMAKE_CXX_FLAGS "-std=c++11 -O2 -Wall")
ENDIF(CMAKE_COMPILER_IS_GNUCXX)
(16)判断语句IF
IF(expression)
COMMAND1(.....)
COMMAND2(.....)
ELSE(expression)
COMMAND1(.....)
COMMAND2(.....)
ENDIF(expression)
凡是出现 IF 的地方必须以 ENDIF 结尾,但是当里边有 ELSEIF 时可以省略 ENDIF,IF 内表达式使用方法如下:
逻辑判断:
IF(var)
IF(NOT var )
IF(var1 AND var2)
IF(var1 AND var2)
IF(COMMAND cmd),当给定的 cmd 确实是命令并可以调用是为真
IF(EXISTS dir)或者 IF(EXISTS file),当目录名或者文件名存在时为真
IF(file1 IS_NEWER_THAN file2),当 file1 比 file2 新,或者 file1/file2 其中有一个不存在时为真,文件名需使用完整路径
IF(IS_DIRECTORY dirname),当 dirname 是目录时,为真
字符串匹配:
IF(variable MATCHES regex)、IF(string MATCHES regex),当给定的变量或者字符串能够匹配正则表达式 regex 时为真
与数字比较:
IF(variable LESS number)
IF(variable GREATER number)
IF(variable EQUAL number)
variable 也可以换成字符串
与字符串比较:
IF(string STRLESS string)
IF(string STRGREATER string)
IF(string STREQUAL string)
string 也可以换成数字
一个小例子:
IF(WIN32)
MESSAGE(STATUS “This is windows.”)
ELSE(WIN32)
MESSAGE(STATUS “This is not windows”)
ENDIF(WIN32)
上述代码用来控制在不同的平台进行不同的控制,但是,阅读起来却并不是那么舒服,但是ELSE(WIN32)之类的语句很容易引起歧义,使用 CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS 变量可以控制 IF、ELSE 语句的书写方式,执行:
SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
这样就可以直接用
IF(WIN32)
MESSAGE(STATUS “This is windows.”)
ELSE()
MESSAGE(STATUS “This is not windows”)
ENDIF()
当使用 ELSEIF 如下:
IF(WIN32)
#do something related to WIN32
ELSEIF(UNIX)
#do something related to UNIX
ENDIF(WIN32)
(17)WHILE 循环
WHILE(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
ENDWHILE(condition)
注意有 ENDWHILE
(18)FOREACH 循环
使用 FOREACH 要注意有 ENDFOREACH,FOREACH 使用分三种形式:
1)列表
FOREACH(loop_var arg1 arg2 ...)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)
举例:
AUX_SOURCE_DIRECTORY(. SRC_LIST)
FOREACH(F ${SRC_LIST})
MESSAGE(${F})
ENDFOREACH(F)
2)范围
FOREACH(loop_var RANGE total)
ENDFOREACH(loop_var)
从 0 到 total 以1为步进
举例:
FOREACH(VAR RANGE 10)
MESSAGE(${VAR})
ENDFOREACH(VAR)
输出0、1、2、3、...10
3)范围和步进
FOREACH(loop_var RANGE start stop [step])
ENDFOREACH(loop_var)
从 start 开始到 stop 结束,以 step 为步进
举例:
FOREACH(A RANGE 5 15 3)
MESSAGE(${A})
ENDFOREACH(A)
输出5、8、11、14
5、cmake中常用的变量
(1)PROJECT_BINARY_DIR、CMAKE_BINARY_DIR、<projectname>_BINARY_DIR,如果是 in source 编译,指得就是工程顶层目录,即makefilelist.txt所在目录,如果是 out-of-source 编译,指的是工程编译发生的目录,即在执行cmake ..命令时所在的目录
(2)PROJECT_SOURCE_DIR、CMAKE_SOURCE_DIR、<projectname>_SOURCE_DIR,这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录,即makefilelist.txt所在目录
(3)CMAKE_CURRENT_SOURCE_DIR,指的是当前处理的 CMakeLists.txt 所在的路径
(4)CMAKE_CURRRENT_BINARY_DIR
如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-ofsource 编译,他指的是编译当前的target 目录
使用我们上面提到的 ADD_SUBDIRECTORY(src bin)可以更改这个变量的值(因为此命令是添加了编译选项),但是使用 SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径
(5)CMAKE_CURRENT_LIST_FILE,返回这个变量所在 CMakeLists.txt 的完整路径
(6)CMAKE_CURRENT_LIST_LINE,返回这个变量所在的行
(7)CMAKE_MODULE_PATH,这个变量用来定义自己的 cmake 模块所在的路径。
如果工程比较复杂,有可能会自己编写一些 cmake 模块,这些 cmake 模块是随你的工程发布的,为了让 cmake 在处理CMakeLists.txt 时找到这些模块,你需要通过 SET 指令,将自己的 cmake 模块路径设置一下。
比如:SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),这时候你就可以通过 INCLUDE 指令来调用自己的模块了
(8)EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH,分别用来重新定义最终结果的存放目录
(9)PROJECT_NAME,返回通过 PROJECT 指令定义的项目名称
(10)版本相关变量:
CMAKE_MAJOR_VERSION,CMAKE 主版本号,比如 2.4.6 中的 2
CMAKE_MINOR_VERSION,CMAKE 次版本号,比如 2.4.6 中的 4
CMAKE_PATCH_VERSION,CMAKE 补丁等级,比如 2.4.6 中的 6
(11)CMAKE_SYSTEM_NAME,不包含版本的系统名,比如 Linux
UNIX,在所有的类 UNIX 平台为 TRUE
WIN32,在所有的 win32 平台为 TRUE
(12)CMAKE_C_FLAGS
设置 C 编译选项,也可以通过指令 ADD_DEFINITIONS()添加
(13)CMAKE_CXX_FLAGS
设置 C++编译选项,也可以通过指令 ADD_DEFINITIONS()添加
(14)$ENV{NAME},cmake的环境变量
(15)BUILD_SHARED_LIBS
这个开关用来控制默认的库编译方式,如果不进行设置,使用 ADD_LIBRARY 并没有指定库类型的情况下,默认编译生成的库都是静态库, SET(BUILD_SHARED_LIBS ON)后,默认生成的为动态库
6、cmake使用实例
(1)Demo1,单个源文件
假设有一个源文件main.cc,该程序内有power函数,用途是计算一个数的指数幂,则编译该文件的CMakeLists.txt为:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
PROJECT(Demo1)
# 指定生成目标
ADD_EXECUTABLE(Demo main.cc)
编译可以分为外部编译(in-source build)、内部编译(out-of-source build),下面分开来介绍两者使用方式:
内部编译:
在工程的顶层目录下执行:cmake,生成Makefile,之后make,就生成了Demo1可执行文件
外部编译:
在工程顶层目录下:mkdir build,cd build,cmake ..,make,生成Demo1可执行文件
使用外部编译一个最大的好处是,对于原有的工程没有任何影响,所有动作全部发生在编译目录,所以cmake 强烈推荐外部构建
(2)Demo2,同一目录,多个源文件
假设将Demo1中的power函数单独写进MathFunction.h、MathFunction.cc中,main.cc调用里边的power函数,则CMakeLists.txt:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
PROJECT(Demo2)
# 指定生成目标
ADD_EXECUTABLE(Demo main.cc MathFunctions.cc)
两个源文件的名字用空格分隔,或者也可以这样写:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
PROJECT(Demo2)
# 指定生成目标
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
ADD_EXECUTABLE(Demo ${DIR_SRCS})
AUX_SOURCE_DIRECTORY语法为:
AUX_SOURCE_DIRECTORY(<dir> <variable>)
AUX_SOURCE_DIRECTORY可以查找指定目录下的所有源文件,然后将结果存进指定变量名,当目录下源文件较多时更加方便快捷
(3)Demo3,多个目录,多个源文件

这种情况,必须在Demo3目录与math目录下分别有一个CMakeLists.txt文件,并且在本目录将会把math目录下的文件编译成静态库有main.cc中的函数调用
Demo3目录下的CMakeLists.txt为:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
PROJECT(Demo3)
# 查找当前目录下的所有源文件,并将名称保存到 DIR_SRCS 变量
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
# 添加 math 子目录
ADD_SUBDIRECTORY(math)
# 指定生成目标
ADD_EXECUTABLE(Demo main.cc)
# 添加链接库
TARGET_LINK_LIBRARIES(Demo MathFunctions)
math目录中的CMakeLists.txt文件为:
# 查找当前目录下的所有源文件,并将名称保存到 DIR_LIB_SRCS 变量
AUX_SOURCE_DIRECTORT(. DIR_LIB_SRCS)
# 生成链接库
ADD_LIBRARY(MathFunctions ${DIR_LIB_SRCS})
(4)Demo4,自定义编译选项

CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。
Demo4的文件结构如上所示,可以将 MathFunctions 库设为一个可选的库,如果该选项为 ON ,就使用该库定义的数学函数来进行运算,否则就调用标准库中的数学函数库
Demo4根目录下CMakeLists.txt:
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
PROJECT (Demo4)
# 加入一个配置头文件,用于处理 CMake 对源码的设置
CONFIGURE_FILE(
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
# 是否使用自己的 MathFunctions 库
OPTION(USE_MYMATH
"Use provided math implementation" ON)
# 是否加入 MathFunctions 库
if (USE_MYMATH)
INCLUDE_DIRECTORIES("${PROJECT_SOURCE_DIR}/math")
ADD_SUBDIRECTORT(math)
SET(EXTRA_LIBS MathFunctions)
ENDIF(USE_MYMATH)
# 查找当前目录下的所有源文件,并将名称保存到 DIR_SRCS 变量
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
# 指定生成目标
ADD_EXECUTABLE(Demo ${DIR_SRCS})
TARGET_LINK_LIBRARIES(Demo ${EXTRA_LIBS})
CONFIGURE_FILE命令用于加入一个配置头文件 config.h ,这个文件由 CMake 从 config.h.in 生成,config.h.in 内容如下:
#cmakedefine USE_MYMATH
config.h 内容如下:
#define USE_MYMATH
CONFIGURE_FILE可以根据 USE_MYMATH 的值将 config.h.in 内的 #cmakedefine USE_MYMATH 替换为 #define USE_MYMATH 或 /* #undef USE_MYMATH */,而 USE_MYMATH 的值首先可以在编译时使用 cmake -DUSE_MYMATH=OFF 指定USE_MYMATH的值为OFF,上述 CMakeLists.txt 中的 OPTION 是为USE_MYMATH 指定默认值为 ON,当执行 cmake 时不指定 USE_MYMATH 值,则为 ON
main函数中就可以根据 config.h 中是否 #define USE_MYMATH 决定使用自定义库还是标准库,通过这样的机制可以通过预定义一些参数和变量来控制代码的生成
(5)Demo5:安装
CMake 可以指定安装规则,能够通过在产生 Makefile 后使用 make install 来执行。在以前的 GNU Makefile 里,你可能需要为此编写 install 的伪目标和相应的规则,但在 CMake 里,这样的工作同样只需要简单的调用几条命令
在Demo5 下 CMakeLists.txt 内添加:
# 指定安装路径
install (TARGETS Demo DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/config.h" DESTINATION include)
在 math 目录下 CMakeLists.txt 内添加:
# 指定 MathFunctions 库的安装路径
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)
通过上面的定制,DESTINATION 默认为 /usr/local,所以在默认情况下,生成的 Demo 文件和 MathFunctions 函数库 libMathFunctions.o 文件将会被复制到 /usr/local/bin 中,而 MathFunctions.h 和生成的 config.h 文件则会被复制到 /usr/local/include 中
若想改变安装的文件位置,可以使用如下命令:
cmake -DCMAKE_INSTALL_PREFIX=/usr ..,通过这条命令 DESTINATION 的值被定义为了 /usr,MathFunctions 函数库libMathFunctions.o 文件将会被复制到 /usr/bin 中,而 MathFunctions.h 和生成的 config.h 文件则会被复制到 /usr/include 中
(6)Demo6:编译选项相关的设置
CMake详解与实践

510

被折叠的 条评论
为什么被折叠?



