这次在公司强制熟悉了一波CMake,以前自己就算是宁肯写makefile也不想去引入cmake,总觉得越原始的东西可控性越高,强迫症晚期了,哈哈。没想到用了之后才觉得,这东西还真**方便,不用真SB了。Cmake在解决跨平台编译配置问题基本是首选(虽然还没试过),仅仅是看了别人写的CMakeList.txt就这样觉得,几句就可以生成不同编译器,不同操作系统,X86和X64的编译配置文件。解决头文件包含、链接库路径、库链接、编译参数、Target、编译目录 等问题变得非常简单了,结构层次也变得清晰明了,很大层度提高了编译流程的可控性。
使用CMake就是执行一个CMakeList.txt的文件,可以包含子目录迭代执行,规则就是在外部的一切在内部均生效,减少了内部重复工作。配合几个简单常用的命令,就可以生产Makefile等。自己也就只接触了一两天,总结得比较粗,以后有机会再完善啦。
常用命令:
project (HELLO) #指定项目名称,生成的项目的名称;
aux_source_directory(. SRC_LIST) :查找源文件 查找当前目录下所有的源文件并保存到SRC_LIST变量里
file(GLOB SRC_LIST "src/cmake*") :file命令同时支持目录递归查找 ,查找src目录下所有以cmake开头的文件并保存到CMAKE_FILES变量里
file(GLOB_RECURSE SRC_LIST "src/cmake*")
按照官方文档的说法,不建议使用file的GLOB指令来收集工程的源文件,原文解释如下
We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate.
大意就是,GLOB收集的源文件增加或删除,而CMakeLists.txt没有发生修改时,CMake不能识别这些文件。其实,当CMakeLists.txt使用aux_source_directory和file glob查找工程源文件时,如果添加或删除源文件,都需要重新运行CMake。
Talk:其实从项目结构来说,在项目最外层有一个CMakeList.txt用于全局包含头文件、指定链接库路径、设置全局变量等。其次在子目录中CMakeList.txt通常存在于一个模块的文件夹中,而这个子目录中的CMakeList.txt可以指定包含这个模块的所有源文件,但也并非代表所有源文件都要在此指定,其实在每个目录中都可以存在一个CMakeList.txt然后在外层add_subdirectory即可,这样在外层可以控制是否编译这个模块或者是否编译这个模块中的某一部分,结构层次显得非常清晰,可以看出CMake并不太适合去递归查找源文件,而应对每一级的流程具备高可控性。
include_directories:指定头文件的搜索路径,相当于指定gcc的-I参数
>> include_directories (${HELLO_SOURCE_DIR}/Hello) #增加Hello为include目录
link_directories:动态链接库或静态链接库的搜索路径,相当于gcc的-L参数
>> link_directories (${HELLO_BINARY_DIR}/Hello) #增加Hello为link目录
target_link_libraries:添加链接库,相同于指定-l参数
>> target_link_libraries(demo Hello) #将可执行文件与Hello连接成最终文件demo,这个地方是有顺序的,如果有多个SO库需引入,而A.so依赖B.so,那么A.so需写在B.so前,与gcc规则相同
add_subdirectory:包含子目录,包含后会执行这个目录下的CMakeList.txt
>> add_subdirectory (Hello)
add_library:
>> add_library(Hello STATIC hello.cxx) #将hello.cxx编译成静态库如libHello.a ,若需编译动态so,应将STATIC设置为SHARED
add_executable:编译可执行程序,指定编译,好像也可以添加.o文件
>> add_executable (helloDemo demo.cxx demo_b.cxx) #将cxx编译成可执行文件——
message( status|fatal_error, “message”):
set(variable value1 value2 value3 ...):
经常配合set命令使用的CMake变量,使用进行设置。可设置多个变量到variable,通过FOREACH(v ${variable
}) 进行遍历
常用变量:
CMAKE_BUILD_TYPE | 对应的c编译选项变量 | 对应的c++编译选项变量 |
---|---|---|
None | CMAKE_C_FLAGS | CMAKE_CXX_FLAGS |
Debug | CMAKE_C_FLAGS_DEBUG | CMAKE_CXX_FLAGS_DEBUG |
Release | CMAKE_C_FLAGS_RELEASE | CMAKE_CXX_FLAGS_RELEASE |
RelWithDebInfo | CMAKE_C_FLAGS_RELWITHDEBINFO | CMAKE_CXX_FLAGS_RELWITHDEBINFO |
MinSizeRel | CMAKE_C_FLAGS_MINSIZEREL | CMAKE_CXX_FLAGS_MINSIZEREL |
1,CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
这三个变量指代的内容是一致的,如果是 in source 编译,指得就是工程顶层目录,如果是 out-of-source 编译,指的是工程编译发生的目录。PROJECT_BINARY_DIR 跟其他指令稍有区别,现在,你可以理解为他们是一致的。
2,CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。
也就是在 in source 编译时,他跟 CMAKE_BINARY_DIR 等变量一致。
PROJECT_SOURCE_DIR 跟其他指令稍有区别,现在,你可以理解为他们是一致的。
3,CMAKE_CURRENT_SOURCE_DIR
指的是当前处理的 CMakeLists.txt 所在的路径
4,CMAKE_CURRRENT_BINARY_DIR
如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-of-source 编译,他指的是 target 编译目录。
使用我们上面提到的 ADD_SUBDIRECTORY(src bin)可以更改这个变量的值。
使用 SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径。
5,CMAKE_CURRENT_LIST_FILE
输出调用这个变量的 CMakeLists.txt 的完整路径
6,CMAKE_CURRENT_LIST_LINE
输出这个变量所在的行
7,EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH
分别用来重新定义最终结果的存放目录,前面我们已经提到了这两个变量。
8,PROJECT_NAME
返回通过 PROJECT 指令定义的项目名称。