cmake
cmake 初探
什么是CMAKE
- CMake是一种跨平台编译工具。
- 首先编写CMakeLists.txt文件,然后通过cmake命令将CMakeLists.txt文件转化为对应平台可执行的make文件,最后用make命令编译源码生成可执行程序或库文件,从而避免不同平台Makefile格式的问题。
C/C++的编译过程
-
编译过程实际上就是将一种语言(通常为高级语言)翻译为成另一种语言(通常为低级语言)。 C/C++程序编译的主要流程:源代码 → 预处理器 → 编译器 → 汇编器 → 链接器 → 可执行程序。
-
预处理: C/C++中,在编译器对源文件进行编译之前,先要对其进行预处理。
- 预处理器提供了一组预编译处理指令和预处理操作符,形式上以#开头,实际上并不属于C/C++中的语句,不能被编译程序翻译,需要在真正编译之前做一个预处理,最后输出一个“.i”文件。
-
编译: 编译就是把C/C++代码转换成汇编代码。
- 编译程序需要通过词法分析和语法分析,在确认所有的指令都符合语法规则没有语法错误之后,把代码转换成汇编语言,生成汇编代码。
-
汇编: 汇编就是将上一步输出的汇编代码翻译成目标机器指令的过程,生成的目标文件是与源程序等效的机器语言。
-
链接: 链接就是将汇编生成的目标文件、系统库的目标文件、库文件链接起来,生成可以在某一特定平台运行的可执行程序。
CMake 常用变量
CMAKE_BINARY_DIR
、PROJECT_BINARY_DIR
、<projectname>_BINARY_DIR
- 这三个变量指代的内容是一致的, 如果是 in source 编译,指得就是工程顶层目录,
- 如果是 out-of-source 编译,指的是工程编译发生的目录。
- PROJECT_BINARY_DIR 跟其他指令稍有区别,现在,你可以理解为他们是一致的。
CMAKE_SOURCE_DIR
、PROJECT_SOURCE_DIR
、<projectname>_SOURCE_DIR
- 这三个变量指代的内容是一致的,不论采用何种编译方式, 都是工程顶层目录。
- 在 in source 编译时,他跟
CMAKE_BINARY_DIR
等变量一致。
CMAKE_CURRENT_SOURCE_DIR
- 指的是当前处理的 CMakeLists.txt 所在的路径,比如上面我们提到的 src 子目录。
CMAKE_CURRRENT_BINARY_DIR
- 如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-of-source 编译,他指的是 target 编译目录。
- 使用我们上面提到的
ADD_SUBDIRECTORY(src bin)
可以更改这个变量的值。 - 使用
SET(EXECUTABLE_OUTPUT_PATH <新路径>)
并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径。
CMAKE_MODULE_PATH
- 这个变量用来定义自己的 cmake 模块所在的路径。
- Eg:
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
,这时候你就可以通过 INCLUDE 指令来调用自己的模块了。
EXECUTABLE_OUTPUT_PATH
和LIBRARY_OUTPUT_PATH
- 分别用来重新定义最终结果的存放目录,前面我们已经提到了这两个变量。
如何编写cmake
-
cmake_minimum_required(VERSION 3.16):用于指定cmake最低版本需求; project(projectName) :用于指定项目名称; include_directories() :用于引入头文件搜索路径; aux_source_directory(src dir_srcs):用于包含源文件目录; set(TEST_MATH) :用于设置环境变量,编译用到的源文件全部都要放到这里; add_executable(${PROJECT_NAME} ${TEST_MATH}):用于添加要编译的可执行文件; target_link_libraries(${PROJECT_NAME} m):用于添加可执行文件所需要的库;
-
新建变量,存放源文件:
set (SRC_LIST ./main.c ./func.c ./func1.c)
-
不同目录下多个源文件
- 添加指定头文件的搜索路
include_directories (func1 func2)
- 搜索指定路径下的所有源文件,并保存在变量中
aux_source_directory (func1 SRC_LIST1)
- 添加指定头文件的搜索路
-
向当前工程添加存放源文件的子目录
add_subdirectory (src)
-
将生成的可执行文件存放到工程根目录下的bin目录下
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
-
对库的链接
- 将指定目录下的指定库文件存到变量
FUNC_LIB
中find_library(FUNC_LIB func HINTS ${PROJECT_SOURCE_DIR}/func/lib)
find_library
:在指定目录下查找指定库,并把库的绝对路径存放到变量里,参数一是变量名,参数二是库名,参数三是HINTS,参数四是路径。- 使用
find_library
的好处是在执行cmake ..
时就会去查找库是否存在,这样可以提前发现错误,不用等到链接时。
- 将指定目录下的指定库文件存到变量
添加编译选项:
- 有时编译程序时想添加一些编译选项,如-Wall,-std=c++11等,就可以使用add_compile_options来进行操作。
add_compile_options(-std=c++11 -Wall)
- 添加C++ 11标准支持,并让编译器对代码提出尽可能多的警告
添加控制选项
option命令: 参数1为option的名字,参数2为字符串,描述option的功能,参数3为option的值,ON或OFF,也可以不写默认为OFF。
第一:CMakeList
中条件编译
-
option(MYDEBUG "enable debug compilation" OFF)
-
if (MYDEBUG) add_executable(main2 main2.c) else() message(STATUS "Currently is not in debug mode") endif()
-
-
cmake .. -DMYDEBUG=ON
命令 控制
第二:代码中条件编译
-
CMakeList
中添加-
option(WWW1 "print one message" OFF) # 这儿`OFF、False` `ON、True` 都可以 if (WWW1) add_compile_options(-DWWW1) endif()
-
cmake中加这个,目的就是代码中可以使用该变量
-
-
*.h
或*.cpp
中 案例:-
#ifdef WWW1 printf("--- define WWW1\n"); #else printf("---- not define WWW1\n"); #endif
-
Cmake 进阶
//TODO