1、CMake简介
CMake管理一个程序项目,实际上就是在源码根目录中建立一个CMakeLists.txt文件作为根节点,再通过add_subdirectory(subdir)包含其下级源码子目录,而下级源码目录中的CMakeLists.txt进一步通过add_subdirectory(subdir)包含其下级源码目录,最终把所有源码都串接在一起,同时所有的CMakeLists.txt文件也都串接在一起。
另外,每一个CMakeLists.txt都要求通过include_directories来为当前文件夹下的源码设置头文件搜索路径。对于多个源码子目录(多个CMakeLists.txt)的项目,任意指定其中一个源码目配置为编译成可执行文件,其他源码目录配置为编译成静态lib,并在任意一个CMakeLists.txt中,通过target_link_libraries将可执行文件跟所有的库(包括静态或动态库)连接在一起。
CMake vs Autotools:
首先,拿他们对比本身没有太多的意义,他们都是产生makefile文件的工程编译管理工具。CMake产生的晚,解决了很多autotools工具的问题。
autotools是一个工具集具有强大的灵活性,但是因为步骤太多,配置繁琐,产生了很多的替代方案,cmake是其中最优秀的之一。
早期没有那么多选项,大量的开源项目都是采用autotools管理工程。
2、CMakeLists.txt
项目的每个源码目录下都要配置一个CMakeLists.txt文件,头文件目录下不需要。
如果项目根目录下没有源码,但是有多个源码子目录,那么项目根目录下也需要配置一个CMakeLists.txt,以便将所有源码子目录串接起来。
CMakeLists.txt常用指令以及变量:
- 指令 project(project_name)
设置项目名称。这条指令一定要放在最顶层的CMakeLists.txt中,千万不要在下级CMakeLists.txt中再次使用project指令,否则会导致PROJECT_SOURCE_DIR变量错乱。
- 变量 PROJECT_SOURCE_DIR
指向最顶层CMakeLists.txt所在目录。
- 指令 cmake_minimum_required
cmake最低版本需求,不加入此行会受到警告信息。
只在根节点CMakeLists.txt中设置即可。
- 指令 include_directories (include_path)
设置当前目录下的源码在编译时,对头文件的搜索路径,多个路径用空格隔开。
每个CMakeLists.txt都需要设置当前目录下源码对头文件的搜索路径。
- 指令 link_directories (lib_path)
设置当前目录下的源码在编译时,对库文件的搜索路径,多个路径用空格隔开。
官方不推荐这条指令,取代方案是find_library。
例如:
find_library(libpath_jsoncpp libjsoncpp.so ${PROJECT_SOURCE_DIR}/lib)
首先从系统库路径搜索libjsoncpp.so,然后从自定义指定路径搜索,一旦匹配成功即返回路径到变量libpath_jsoncpp。
- 指令 set(des src)
变量复制,目标变量不存在则自动创建。
例如:
set(lib_path "hello") //赋值变量lib_path为字符串"hello"
message("lib_path=${lib_path}") 输出变量内容
3、实例1
flat项目,所有源码都在同一个文件夹下。
demo文件夹下,有三个文件,main.c, test.c, test.h:
demo/
|– main.c
|– test.c
|– test.h
在demo文件夹下建立CMakeLists.txt文件,编辑内容如下:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ cmake_minimum_required (VERSION 2.6) #cmake最低版本需求,不加入此行会受到警告信息
┃ project(hello) #设置项目名(不是指最终编译出的可执行文件名)
┃ aux_source_directory( . src_list) #把当前目录(.)下所有源代码文件和头文件路径赋值给变量src_list;
┃ set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin ) #设置应用程序输出路径
┃ add_executable(demo ${src_list}) #将目标源码编译成可执行文件demo
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
在demo目录下执行cmake <根节点CMakeLists.txt路径>,就会将在当前目录下生成Makefile以及其他相关配置文件,然后在执行make命令即可调用Makefile进行编译。
为了避免在代码目录下生成一大堆目标文件,可以在项目根目录下创建一个子目录,如build,然后进入子目录,并运行cmake ..命令,哲理的..表示根节点CMakeLists.txt路径在上级目录。
执行完cmake ..,所有的配置信息文件包括Makefile都会生成在当前目录(build)下,然后再执行make命令,就会在build目录下生成目标文件。若要清理目标文件,直接删除build文件夹即可。
注:
- EXECUTABLE_OUTPUT_PATH为CMake的系统变量,用于设定应用程序的数据路径;
- PROJECT_SOURCE_DIR为包含project(项目名)的最近一个CMakeLists.txt文件所在的文件夹,通常project(...)只要在根CMakeLists.txt文件中定义一次就可以了,所以PROJECT_SOURCE_DIR通常指向项目根目录。
4、实例2
层次化结构:
demo/
┣main.c
┣include/
┣test1.h
┣test2.h
┣src/
┣test1.c
┣test2.c
在demo文件夹下建立CMakeLists.txt文件,编辑内容如下:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ cmake_minimum_required (VERSION 2.6) #cmake最低版本需求,不加入此行会受到警告信息
┃ project(hello) #设置项目名(不是指最终编译出的可执行文件名)
┃
┃ aux_source_directory( . src_list) #把当前目录(.)下所有源代码文件和头文件路径赋值给变量src_list;
┃ include_directories (include /usr/include) #设置头文件搜索路径,多个路径用空格隔开,
┃ #若有相对路径则是基于当前CMakeLists.txt所在目录;
┃ add_subdirectory(src) #添加其他源码子目录
┃ set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin ) #设置应用程序输出路径
┃ add_executable(demo ${src_list}) #将目标源码编译成可执行文件demo
┃ link_directories (${PROJECT_SOURCE_DIR}/lib) #设置库文件搜索路径
┃ target_link_libraries (demo libpthread.so libsqlite3.so libcurl.so libjsoncpp.so) #设置添加到目标程序中去的库(静态或动态)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
在src文件夹下建立CMakeLists.txt文件,编辑内容如下:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃aux_source_directory(. dir_sub_src) #把当前目录(.)下所有源代码文件和头文件加入变量dir_sub_src
┃include_directories (../include) #为当前目录下的源码文件设置头文件搜索路径,相对路径基于当前CMakeLists.txt所在目录。
┃add_library (testlib ${dir_sub_src}) #把当前目录源码编译成静态库testlib
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
5、实例3
层次化结构(多个源码子目录,但项目根目录下没源码文件的情况):
demo/
┣include/
┣test1.h
┣test2.h
┣app/
┣main.c
┣src/
┣test1.c
┣test2.c
在demo文件夹下建立CMakeLists.txt根节点文件,编辑内容如下:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ cmake_minimum_required (VERSION 2.6) #cmake最低版本需求,不加入此行会受到警告信息
┃ project(hello) #设置项目名(不是指最终编译出的可执行文件名)
┃ add_subdirectory(app) #添加源码子目录app(只允许添加子目录,而且一次只能添加一个子目录)
┃ add_subdirectory(src) #添加源码子目录src(只允许添加子目录,而且一次只能添加一个子目录)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
在demo/app文件夹下建立CMakeLists.txt根节点文件,编辑内容如下:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ aux_source_directory(. src_list) #把当前目录(.)下所有源代码文件和头文件路径赋值给变量src_list;
┃ include_directories (../include) #设置头文件搜索路径,多个路径用空格隔开
┃ set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin ) #设置应用程序输出路径
┃ add_executable(demo ${src_list}) #将目标源码编译成可执行文件demo
┃ link_directories (${PROJECT_SOURCE_DIR}/lib) #设置库文件搜索路径
┃ target_link_libraries (demo testlib libpthread.so) #设置添加到目标程序中去的库(静态或动态)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
注:
在调用add_executable指定输出可执行文件时,要在同一个CMakeLists.txt文件中通过set (EXECUTABLE_OUTPUT_PATH)设置应用程序输出路径。在调用target_link_libraries连接库文件时,要在同一个CMakeLists.txt文件中通过link_directoriesadd设置库文件搜索路径。
在demo/src文件夹下建立CMakeLists.txt文件,编辑内容如下:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃aux_source_directory(. dir_sub_src) #把当前目录(.)下所有源代码文件和头文件路径赋值给变量dir_sub_src
┃include_directories (../include) #为当前目录下的源码文件设置头文件搜索路径,相对路径基于当前CMakeLists.txt所在目录。
┃add_library (testlib ${dir_sub_src}) #把当前目录源码编译成静态库testlib
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
对于一些全局公共配置,只需要在根节点CMakeLists.txt中配置即可,例如下面这些公共配置:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ cmake_minimum_required (VERSION 2.6) #cmake最低版本需求,不加入此行会受到警告信息
┃ project(hello) #设置项目名(不是指最终编译出的可执行文件名)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛