Cmake学习笔记
cmake
是 makefile
的上层工具,它们的目的正是为了产生可移植的makefile,并简化自己动手写makefile时的巨大工作量.
c/c++ 项目工程部署如上:
src
: 源码工程目录ext
: 第三方依赖库文件与头文件CMakeLists.txt
: cmake 构建配置文件
简单案例
源文件编写 : src/main.cc
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
编写CMAKE 配置文件 CMakeLists.txt
# cmake 最低版本需求
cmake_minimum_required(VERSION 3.13)
# 工程名称
project(cmake_study)
# 设置
set(CMAKE_CXX_STANDARD 11)
# 编译源码生成目标
add_executable(cmake_study src/main.cc)
cmake 命令便按照 CMakeLists
配置文件运行构建Makefile
文件
$ mkdir build
$ cd build/
$ cmake ..
为了不让编译产生的中间文件污染我们的工程,我们可以创建一个 build
目录进入执行 cmake
构建工具. 如果没有错误, 执行成功后会在 build
目录下产生 Makefile
文件。
然后我们执行 make
命令就可以编译我们的项目了。
-rw-r--r-- 1 root root 13591 Jul 20 12:09 CMakeCache.txt
drwxr-xr-x 14 root root 448 Jul 20 12:09 CMakeFiles
-rw-r--r-- 1 root root 5034 Jul 20 12:09 Makefile
-rw-r--r-- 1 root root 1508 Jul 20 12:09 cmake_install.cmake
-rwxr-xr-x 1 root root 9104 Jul 20 12:09 cmake_study
$ ./cmake_study
Hello, World!
以上就是大致的 cmake 构建运行过程。
从上面的过程可以看出,cmake 的重点在配置 CMakeLists.txt
文件。
CMakeLists 详解
CMakeLists 变量篇
我们可以使用 SET(set)
来定义变量
语法 :SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
指令功能 : 用来显式的定义变量
例子 : SET (SRC_LST main.c other.c)
说明: 用变量代替值,例子中定义 SRC_LST
代替后面的字符串。
我们可以使用 ${NAME}
来获取变量的名称。
CMake 常用指令
- ADD_DEFINITIONS
语法 : ADD_DEFINITIONS(-DENABLE_DEBUG -DABC)
向 C/C++编译器添加 -D
定义. 如果你的代码中定义了#ifdef ENABLE_DEBUG #endif
,这个代码块就会生效。
- ADD_DEPENDENCIES
语法: ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...)
定义 target 依赖的其他 target, 确保在编译本 target 之前,其他的 target 已经被构建。
- ADD_EXECUTABLE
语法 : ADD_EXECUTABLE(<name> [source1] [source2 ...])
利用源码文件生成目标可执行程序。
- ADD_LIBRARY
语法 : ADD_LIBRARY(<name> [STATIC | SHARED | MODULE] [source1] [source2 ...])
根据源码文件生成目标库。
STATIC
,SHARED
或者 MODULE
可以指定要创建的库的类型。 STATIC库是链接其他目标时使用的目标文件的存档。 SHARED库是动态链接的,并在运行时加载
- ADD_SUBDIRECTORY
语法 : ADD_SUBDIRECTORY(NAME)
添加一个文件夹进行编译,该文件夹下的 CMakeLists.txt 负责编译该文件夹下的源码. NAME是想对于调用add_subdirectory的CMakeListst.txt的相对路径.
- ENABLE_TESTING
语法: ENABLE_TESTING()
.
控制 Makefile 是否构建 test 目标,涉及工程所有目录。 一般情况这个指令放在工程的主CMakeLists.txt 中.
- ADD_TEST
语法 : ADD_TEST(testname Exename arg1 arg2 ...)
testname
是自定义的 test 名称,Exename
可以是构建的目标文件也可以是外部脚本等等。 后面连接传递给可执行文件的参数。 如果没有在同一个 CMakeLists.txt 中打开ENABLE_TESTING()
指令, 任何 ADD_TEST
都是无效的。
- AUX_SOURCE_DIRECTORY
语法 : AUX_SOURCE_DIRECTORY(dir VARIABLE)
作用是发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表。因为目前 cmake 还不能自动发现新添加的源文件。
比如 :
AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(main ${SRC_LIST})
- CMAKE_MINIMUM_REQUIRED
语法 : CMAKE_MINIMUM_REQUIRED
定义 cmake 的最低兼容版本 比如 CMAKE_MINIMUM_REQUIRED(VERSION 2.5 FATAL_ERROR)
如果 cmake 版本小与 2.5,则出现严重错误,整个过程中止。
- EXEC_PROGRAM
在 CMakeLists.txt 处理过程中执行命令,并不会在生成的 Makefile 中执行。 具体语法为:
EXEC_PROGRAM(Executable [directory in which to run]
[ARGS <arguments to executable>]
[OUTPUT_VARIABLE <var>]
[RETURN_VALUE <var>])
用于在指定的目录运行某个程序,通过 ARGS 添加参数,如果要获取输出和返回值,可通过OUTPUT_VARIABLE 和 RETURN_VALUE 分别定义两个变量.
这个指令可以帮助你在 CMakeLists.txt 处理过程中支持任何命令,比如根据系统情况去修改代码文件等等。
- FILE 指令
文件操作指令
语法:
FILE(WRITE filename "message to write"... )
FILE(APPEND filename "message to write"... )
FILE(READ filename variable)
FILE(GLOB variable [RELATIVE path] [globbing expression_r_rs]...)
FILE(GLOB_RECURSE variable [RELATIVE path] [globbing expression_r_rs]...)
FILE(REMOVE [directory]...)
FILE(REMOVE_RECURSE [directory]...)
FILE(MAKE_DIRECTORY [directory]...)
FILE(RELATIVE_PATH variable directory file)
FILE(TO_CMAKE_PATH path result)
FILE(TO_NATIVE_PATH path result)
CMake 控制指令
- IF 指令
if(<condition>)
<commands>
elseif(<condition>) # optional block, can be repeated
<commands>
else() # optional block
<commands>
endif()
#####
IF(var),如果变量不是:空,0,N, NO, OFF, FALSE, NOTFOUND 或<var>_NOTFOUND 时,表达式为真。
IF(NOT var ),与上述条件相反。
IF(var1 AND var2),当两个变量都为真是为真。
IF(var1 OR 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)
- FOREACH 指令
语法:
foreach(<loop_var> <items>)
<commands>
endforeach()
其中<items>
是以分号或空格分隔的项目列表。记录foreach匹配和匹配之间的所有命令endforeach而不调用。 一旦endforeach评估,命令的记录列表中的每个项目调用一次<items>
。在每次迭代开始时,变量loop_var将设置为当前项的值。
- WHILE 指令
语法:
while(<condition>)
<commands>
endwhile()
)
其中`<items>`是以分号或空格分隔的项目列表。记录foreach匹配和匹配之间的所有命令endforeach而不调用。 一旦endforeach评估,命令的记录列表中的每个项目调用一次`<items>`。在每次迭代开始时,变量loop_var将设置为当前项的值。
- **WHILE 指令**
语法:
while()
endwhile()
while和匹配之间的所有命令 endwhile()被记录而不被调用。 一旦endwhile()如果被评估,则只要为`<condition>`真,就会调用记录的命令列表。