在linux下使⽤CMake构建应⽤程序
本⽂介绍了⼀个跨平台的⾃动化构建系统 CMake 在上的使⽤⽅法。 CMake 是⼀个⽐ automake 更加容易使⽤的⼯具,能够使程序员从复杂的编译连接过程中解脱出来。⽂中通过⼀些例⼦介绍使⽤ CMake 处理多源⽂件⽬录的⽅法、查并使⽤其他开发包的⽅法以及⽣成debug 版和 release 版程序的⽅法。
CMake 简介
CMake 是⼀个跨平台的⾃动化建构系统,它使⽤⼀个名为 的⽂件来描述构建过程,可以产⽣标准的构建⽂件,如 Unix 的Makefile 或Windows Visual C++ 的 projects/workspaces 。⽂件 需要⼿⼯编写,也可以通过编写脚本进⾏半⾃动的⽣成。CMake 提供了⽐ autoconfig 更简洁的语法。在 linux 平台下使⽤ CMake ⽣成 Makefile 并编译的流程如下:
1.编写 。
2.执⾏命令 “cmake PATH” 或者 “ccmake PATH” ⽣成 Makefile ( PATH 是 所在的⽬录 )。
3.使⽤ make 命令进⾏编译。
第⼀个⼯程
现假设我们的项⽬中只有⼀个源⽂件 main.cpp
清单 1 源⽂件 main.cpp
#include<iostream>
int main()
{
std::cout<<"Hello word!"<<std::endl;
return 0;
}
为了构建该项⽬,我们需要编写⽂件 并将其与 main.cpp 放在同⼀个⽬录下:
清单
1 PROJECT(main)
2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
3 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
4 ADD_EXECUTABLE(main ${DIR_SRCS})
< 的语法⽐较简单,由命令、注释和空格组成,其中命令是不区分⼤⼩写的,符号"#"后⾯的内容被认为是注释。命令由命令名称、⼩括号和参数组成,参数之间使⽤空格进⾏间隔。例如对于清单2的 ⽂件:第⼀⾏是⼀条命令,名称是 PROJECT ,参数是main ,该命令表⽰项⽬的名称是 main 。第⼆⾏的命令限定了 CMake 的版本。第三⾏使⽤命令 AUX_SOURCE_DIRECTORY 将当前⽬录中的源⽂件名称赋值给变量 DIR_SRCS 。 CMake ⼿册中对命令 AUX_SOURCE_DIRECTORY 的描述如下:
aux_source_directory(<dir> <variable>)
该命令会把参数 <dir> 中所有的源⽂件名称赋值给参数 <variable> 。第四⾏使⽤命令 ADD_EXECUTABLE 指⽰变量 DIR_SRCS 中的源⽂件需要编译成⼀个名称为 main 的可执⾏⽂件。
完成了⽂件 的编写后需要使⽤ cmake 或 ccmake 命令⽣成Makefile 。 ccmake 与命令 cmake 的不同之处在于 ccmake 提供了⼀个图形化的操作界⾯。cmake 命令的执⾏⽅式如下:
cmake [options] <path-to-source>
这⾥我们进⼊了 main.cpp 所在的⽬录后执⾏ “cmake .” 后就可以得到 Makefile 并使⽤ make 进⾏编译,如下图所⽰。
图 1. camke 的运⾏结果
camke 的运⾏结果
处理多源⽂件⽬录的⽅法
CMake 处理源代码分布在不同⽬录中的情况也⼗分简单。现假设我们的源代码分布情况如下:
图 2. 源代码分布情况
源代码分布情况
其中 src ⽬录下的⽂件要编译成⼀个链接库。
第⼀步,项⽬主⽬录中的
在⽬录 step2 中创建⽂件 。⽂件内容如下:
清单 3 ⽬录 step2 中的
1 PROJECT(main)
2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
3 ADD_SUBDIRECTORY( src )
4 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
5 ADD_EXECUTABLE(main ${DIR_SRCS}  )
6 TARGET_LINK_LIBRARIES( main Test )
相对于清单 2,该⽂件添加了下⾯的内容: 第三⾏,使⽤命令 ADD_SUBDIRECTORY 指明本项⽬包含⼀个⼦⽬录 src 。第六⾏,使⽤命令 TARGET_LINK_LIBRARIES 指明可执⾏⽂件 main 需要连接⼀个名为Test的链接库。
第⼆步,⼦⽬录中的
在⼦⽬录 src 中创建 。⽂件内容如下:
清单 4. ⽬录 src 中的
1 AUX_SOURCE_DIRECTORY(. DIR_TEST1_SRCS)
2 ADD_LIBRARY ( Test ${DIR_TEST1_SRCS})
在该⽂件中使⽤命令 ADD_LIBRARY 将 src ⽬录中的源⽂件编译为共享库。
第三步,执⾏ cmake
⾄此我们完成了项⽬中所有 ⽂件的编写,进⼊⽬录 step2 中依次执⾏命令 “cmake .” 和 “make” 得到结果如下:
图3. 处理多源⽂件⽬录时 cmake 的执⾏结果
处理多源⽂件⽬录时 cmake 的执⾏结果
在执⾏ cmake 的过程中,⾸先解析⽬录 step2 中的 ,当程序执⾏命令 ADD_SUBDIRECTORY( src ) 时进⼊⽬录 src 对其中的 进⾏解析。
在⼯程中查并使⽤其他程序库的⽅法
在开发软件的时候我们会⽤到⼀些函数库,这些函数库在不同的系统中安装的位置可能不同,编译的时候需要⾸先到这些软件包的头⽂件以及链接库所在的⽬录以便⽣成编译选项。例如⼀个需要使⽤博克利数据库项⽬,需要头⽂件db_cxx.h 和链接库 libdb_cxx.so ,现在该项⽬中有⼀个源代码⽂件 main.cpp ,放在项⽬的根⽬录中。
第⼀步,程序库说明⽂件
在项⽬的根⽬录中创建⽬录 cmake/modules/ ,在 cmake/modules/ 下创建⽂件 ake ,内容如下:
清单 5. ⽂件 ake
01 MESSAGE(STATUS "Using ")
02
03 FIND_PATH(
04  LIBDB_CXX_INCLUDE_DIR
05  db_cxx.h
06  /usr/include/
07  /usr/local/include/
08  )
09
10 FIND_LIBRARY(
11  LIBDB_CXX_LIBRARIES NAMES  db_cxx
12  PATHS /usr/lib/ /usr/local/lib/
13  )
⽂件 ake 的命名要符合规范: ake ,其中NAME 是函数库的名称。ake 的语法与 相同。这⾥使⽤了三个命令: MESSAGE , FIND_PATH 和 FIND_LIBRARY 。
命令 MESSAGE 会将参数的内容输出到终端。
命令 FIND_PATH 指明头⽂件查的路径,原型如下:
find_path(<VAR> name1 [path1 path2 ...]) 该命令在参数 path* 指⽰的⽬录中查⽂件 name1 并将查到的路径保存在变量 VAR 中。清单5第3-8⾏的意思是在 /usr/include/ 和 /usr/local/include/ 中查⽂件db_cxx.h ,并将 db_cxx.h 所在的路径保存在
LIBDB_CXX_INCLUDE_DIR 中。
命令 FIND_LIBRARY 同 FIND_PATH 类似,⽤于查链接库并将结果保存在变量中。清单5第10-13⾏的意思是在⽬录 /usr/lib/ 和
/usr/local/lib/ 中寻名称为 db_cxx 的链接库,并将结果保存在 LIBDB_CXX_LIBRARIES 。
第⼆步, 项⽬的根⽬录中的
在项⽬的根⽬录中创建 :
清单 6. 可以查链接库的
01 PROJECT(main)
02 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
03 SET(CMAKE_SOURCE_DIR .)
04 SET(CMAKE_MODULE_PATH ${CMAKE_ROOT}/Modules ${CMAKE_SOURCE_DIR}/cmake/modules)
05 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
06 ADD_EXECUTABLE(main ${DIR_SRCS})
07
08 FIND_PACKAGE( libdb_cxx REQUIRED)
09 MARK_AS_ADVANCED(
10 LIBDB_CXX_INCLUDE_DIR
11 LIBDB_CXX_LIBRARIES
12 )
13 IF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
14 MESSAGE(STATUS "Found libdb libraries")
15    INCLUDE_DIRECTORIES(${LIBDB_CXX_INCLUDE_DIR})
16    MESSAGE( ${LIBDB_CXX_LIBRARIES} )
17    TARGET_LINK_LIBRARIES(main ${LIBDB_CXX_LIBRARIES}
18 )
19 ENDIF (LIBDB_CXX_INCLUDE_DIR AND LIBDB_CXX_LIBRARIES)
在该⽂件中第4⾏表⽰到⽬录 ./cmake/modules 中查 ake ,8-19 ⾏表⽰查链接库和头⽂件的过程。第8⾏使⽤命令FIND_PACKAGE 进⾏查,这条命令执⾏后 CMake 会到变量 CMAKE_MODULE_PATH 指⽰的⽬录中查⽂件 ake 并执⾏。第13-19⾏是条件判断语句,表⽰如果 LIBDB_CXX_INCLUDE_DIR 和 LIBDB_CXX_LIBRARIES 都已经被赋值,则设置编译时到LIBDB_CXX_INCLUDE_DIR 寻头⽂件并且设置可执⾏⽂件 main 需要与链接库 LIBDB_CXX_LIBRARIES 进⾏连接。
第三步,执⾏ cmake
完成 ake 和 的编写后在项⽬的根⽬录依次执⾏ “cmake . ” 和 “make ” 可以进⾏编译,结果如下图所⽰:
图 4. 使⽤其他程序库时 cmake 的执⾏结果
使⽤其他程序库时 cmake 的执⾏结果
使⽤ cmake ⽣成 debug 版和 release 版的程序
在 Visual Studio 中我们可以⽣成 debug 版和 release 版的程序,使⽤ CMake 我们也可以达到上述效果。debug 版的项⽬⽣成的可执⾏⽂件需要有调试信息并且不需要进⾏优化,⽽ release 版的不需要调试信息但需要优化。这些特性在 gcc/g++ 中是通过编译时的参数来决定的,如果将优化程度调到最⾼需要设置参数-O3,最低是 -O0 即不做优化;添加调试信息的参数是 -g -ggdb ,如果不添加这个参数,调试信息就不会被包含在⽣成的⼆进制⽂件中。
CMake 中有⼀个变量 CMAKE_BUILD_TYPE ,可以的取值是 Debug Release RelWithDebInfo 和 MinSizeRel。当这个变量值为 Debug 的时候,CMake 会使⽤变量 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_C_FLAGS_DEBUG 中的字符串作为编译选项⽣成 Makefile ,当这个变量值为 Release 的时
候,⼯程会使⽤变量 CMAKE_CXX_FLAGS_RELEASE 和 CMAKE_C_FLAGS_RELEASE 选项⽣成 Makefile。现假设项⽬中只有⼀个⽂件 main.cpp ,下⾯是⼀个可以选择⽣成 debug 版和 release 版的程序的 :
清单 7
1 PROJECT(main)
2 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
3 SET(CMAKE_SOURCE_DIR .)
4
5 SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
6 SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
7
8 AUX_SOURCE_DIRECTORY(. DIR_SRCS)
9 ADD_EXECUTABLE(main ${DIR_SRCS})
第 5 和 6 ⾏设置了两个变量 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_CXX_FLAGS_RELEASE, 这两个变量是分别⽤于 debug 和release 的编译选项。编辑 后需要执⾏ ccmake 命令⽣成 Makefile 。在进⼊项⽬的根⽬录,输⼊ "ccmake ." 进⼊⼀个图形化界⾯,如下图所⽰:
图 5. ccmake 的界⾯
ccmake 的界⾯
为什么现在都用cmake
按照界⾯中的提⽰进⾏操作,按 "c" 进⾏ configure ,这时界⾯中显⽰出了配置变量 CMAKE_BUILD_TYPE 的条⽬。如下图所⽰:
图 6. 执⾏了 configure 以后 ccmake 的界⾯
执⾏了 configure 以后 ccmake 的界⾯
下⾯我们⾸先⽣成 Debug 版的 Makefile :将变量 CMAKE_BUILD_TYPE 设置为 Debug ,按 "c" 进⾏ configure ,按 "g" ⽣成 Makefile 并退出。这时执⾏命令 find * | xargs grep "O0" 后结果如下:
清单 8 find * | xargs grep "O0"的执⾏结果
CMakeFiles/main.dir/flags.make:CXX_FLAGS = -O0 -Wall -g -ggdb
CMakeFiles/main.:/usr/bin/c++ -O0 -Wall -g -ggdb
CMakeFiles/main.dir/main.cpp.o -o main -rdynamic
<:SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
这个结果说明⽣成的 Makefile 中使⽤了变量 CMAKE_CXX_FLAGS_DEBUG 作为编译时的参数。
下⾯我们将⽣成 Release 版的 Makefile :再次执⾏命令 "ccmake ." 将变量CMAKE_BUILD_TYPE 设置为 Release ,⽣成 Makefile 并退出。执⾏命令 find * | xargs grep "O0" 后结果如下:
清单 9 find * | xargs grep "O0"的执⾏结果
<:SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
⽽执⾏命令 find * | xargs grep "O3" 后结果如下:
清单 10. find * | xargs grep "O3"的执⾏结果
<:CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
<:CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
CMakeFiles/main.dir/flags.make:CXX_FLAGS = -O3 -Wall
CMakeFiles/main.:/usr/bin/c++ -O3 -Wall
CMakeFiles/main.dir/main.cpp.o -o main -rdynamic
<:SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
这两个结果说明⽣成的 Makefile 中使⽤了变量 CMAKE_CXX_FLAGS_RELEASE 作为编译时的参数。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。