参考地址 CMake入门笔记
什么是CMake
CMake
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。只是 CMake 的组态档取名为 CMakeLists.txt。Cmake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然后再依一般的建构方式使用。这使得熟悉某个集成开发环境(IDE)的开发者可以用标准的方式建构他的软件,这种可以使用各平台的原生建构系统的能力是 CMake 和 SCons 等其他类似系统的区别之处。
Make
make工具是一个简化编译工作程序,有了它我们可以进行所谓的“自动化编译”,极大地提高了软件开发的效率。make工具相当于一个Shell,通过解释Makefile的中的命令进行工作。大多数IDE都有这个工具,比如:Visual C++的nmake,Linux下GNU的make。
make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。
直白点说就是,CMake用来生成makefile或者是工程文件,make是用来解释makefile的。
以Linux平台下使用gcc编译为例,我们想要编译源文件可以直接在命令行中执行gcc test.c -o test之类的命令,可是文件多了,工程复杂,而且文件不时的还会变,每次这样输多麻烦。
这个时候make就有了用武之地,写个makefile,以后编译就直接make解释下makefile,根据makefile来执行编译命令。这样在linux上是很好,在windows上或者其他平台上怎么办呢?
这时候就到cmake出马了,构建工程的时候,不在写makefile,而写CMakeLists.txt。需要编译的时候,先用Cmake解释CMakeLists.txt。如果在Windows平台上,设置编译器是VS,CMake就生成一个VS的工程,然后用VS来运行编译项目。如果是在Linux平台上,编译器是gcc,CMake就生成makefile,然后用make来解释makefile编译项目。
如图,分别为原始目录、在Linux下选择gcc为编译工具生成的工程文件和在WIndows下选用Visual Studio为编译器生成的工程文件:
cmake执行后会在cmake执行时所在的目录生成许多文件,根据cmake执行目录是否与源文件同目录,将构建方式分为两种:
out-of-source build,构建输出文件与源文件放到不同目录中;
in-source build :构建输出文件与源文件放到相同目录中。
一般选择out-of-source方式,可以在项目目录下建立一个build文件,然后进入build下,执行cmake ../,使生成的文件都在build文件夹下,以便后期清理。
示例
为了编译与演示方便,示例全部在Linux下进行。
Hello, CMake!
CMake最简单的实例,目录结构如下:
main.c文件:
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("Hello, CMake!\r\n");
return 0;
}
1
2
3
4
5
6
7
8
CMakeLists.txt文件
# 版本限定
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
# 项目名称
PROJECT(Test1)
# 添加源文件列表变量
SET(SRC_LIST main.cpp)
# 打印编译目录和项目目录路径
MESSAGE(STATUS "This is SOURCE dir " ${PROJECT_SOURCE_DIR})
# 生成可执行文件
ADD_EXECUTABLE(HelloCMake ${SRC_LIST})
1
2
3
4
5
6
7
8
9
10
然后进入项目目录,依次执行(需要确保linux下安装了gcc、make等):
mkdir build
cd build
cmake ../
1
2
3
此时,build文件夹内容如下:
。然后键入make命令,即解析了Makefile文件,build目录下的内容变为:
可以看到,生成了一个HelloCMake的可执行文件,键入./HelloCMake可以运行HelloCMake文件。
bash执行输出过程如下:
同一目录多个源文件
在实际使用中,很少会出现这么简单的情况,往往在一个文件夹中会存在多个源文件要求编译,目录如下,这时候只需要在SET后面加上需要编译的源文件就可以了。
CMakeLists.txt文件:
# 版本限定
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
# 项目名称
PROJECT(Test2)
# 添加源文件列表变量
SET(SRC_LIST Stupid.cpp main.cpp)
# 打印编译目录和项目目录路径
MESSAGE(STATUS "This is BINARY dir " ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir " ${PROJECT_SOURCE_DIR})
# 生成可执行文件
ADD_EXECUTABLE(Test2 ${SRC_LIST})
1
2
3
4
5
6
7
8
9
10
11
上述方式虽然可行,但是如果源文件过多,CMakeLists.txt文件编写起来也是一件很吃力的事情,这时候,可以用aux_source_directory来解决这个问题。aux_source_directory可以遍历指定文件夹的下源文件,将它们统一复制给一个变量,然后进行编译。
CMakeLists.txt文件2:
# 版本限定
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
# 项目名称
project(Test3)
# 遍历当前目录所有的源文件,赋值给test3src
aux_source_directory(. test3src)
message(STATUS "src file : " ${test3src})
# 生成可执行文件
add_executable(Test3 ${test3src})
1
2
3
4
5
6
7
8
9
10
不同目录下的多个源文件
为了使源码更为清晰,源码往往会根据不同的功能,将代码放在不同的文件夹下,结构如图:
这时候CmakeLists.txt文件又该如何编写呢?答案如下:
# 版本限定
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
# 项目名称
project(Test3)
# 遍历当前目录所有的源文件,赋值给test3src
aux_source_directory(. main)
aux_source_directory(./stupid stupid)
set(test4src ${main} ${stupid})
message(STATUS "src file : " ${test4src})
# 生成可执行文件
add_executable(Test4 ${test4src})
1
2
3
4
5
6
7
8
9
10
11
12
13
编译动态库与静态库
在前面的示例中,我们都是直接生成了可执行文件,而更多的时候,我们需要生成的不是可执行文件,而是一个可以让复用的库文件,这时候只需要将add_executable(Name ${srcTemp})修改为add_library(Name ${srcTemp})就可以了。add_library可以在中间增加一个参数表示动态库或静态库,默认无参数为静态库。
# 版本限定
cmake_minimum_required(VERSION 2.8)
# 遍历当前目录所有的源文件,赋值给STUPID_SRC
aux_source_directory(. STUPID_SRC)
# 编译STUPID_SRC为静态库
add_library(stupid STATIC ${STUPID_SRC})
# 编译STUPID_SRC为动态库
# add_library(stupid SHARED ${STUPID_SRC})
1
2
3
4
5
6
7
8
9
库文件和头文件导出到指定位置
编译出的库文件是要使用的,而库文件的使用一般需要库文件和头文件。虽然可以编译出库后自己整理库文件和头文件,但是终归是一件麻烦事,尤其是工程比较庞大的时候。这时候,我们可以使用INSTALL来将库文件和头文件导出到指定位置。
# 版本限定
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(OUTPUT ${CMAKE_CURRENT_BINARY_DIR})
AUX_SOURCE_DIRECTORY(. STUPID_SRC)
# 设置库文件输出目录
# SET(LIBRARY_OUTPUT_PATH ${LIB_DIRS})
# 创建动态库
ADD_LIBRARY(stupid SHARED ${STUPID_SRC})
# 导出动态库
INSTALL(TARGETS stupid DESTINATION ${OUTPUT}/libs)
# 导出头文件
INSTALL(DIRECTORY . DESTINATION ${OUTPUT}/include/stupid
FILES_MATCHING PATTERN "*.h")
1
2
3
4
5
6
7
8
9
10
11
12
13
然后进入项目目录/build/ 目录下,依次执行:
cmake ../
make
make install
1
2
3
即可在build下生成以下结构:
include为头文件目录,其中包含了stupid/stupid.h文件。libs为库文件目录,其中包含了libstupid.so文件。
使用动态库/静态库
在上一个示例中,我们生成并导出了动态库,现在来使用它。复制出上个示例导出的libs及include文件夹,形成以下结构:
CMakeLists.txt文件内容如下:
# 版本限定
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
# 项目名称
PROJECT(Test8)
# 遍历当前目录所有的源文件,赋值给test3src
AUX_SOURCE_DIRECTORY(. main)
MESSAGE(STATUS "src file : " ${main})
# 指定库文件搜索目录,必须为绝对路径
LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/libs)
# 生成可执行文件
ADD_EXECUTABLE(Test8 ${main})
# 指定头文件目录
INCLUDE_DIRECTORIES(./include)
# 指定目标需要连接的库
TARGET_LINK_LIBRARIES(Test8 stupid)