ceph自从Kraken版本以后,将构建方式从手写makefile,转到了cmake。在学习过程中,发现cmake,涉及到的东西比较多。在此做一个总结,也方便自己今后查阅。
一. 什么是cmake
cmake可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile,能测试编译器所支持的C++特性,类似UNIX下的automake。
二. CMake 使用方法
CMake的所有的语句都写在一个叫:CMakeLists.txt的文件中。当CMakeLists.txt文件确定后,应用cmake命令生成相应的makefile。
其基本操作流程为:
$> cmake [options] <path-to-source>
$> make
第一条命令用于根据CMakeLists.txt生成Makefile文件;
第二条命令用于执行Makefile文件,编译程序,生成可执行文件。
三. 一个CMakelists.txt的例子
1 #project name
2 PROJECT(test_math)
3 #head file path
4 INCLUDE_DIRECTORIES(
5 include
6 )
7 #source directory
8 AUX_SOURCE_DIRECTORY(src DIR_SRCS)
9 #set environment variable
10 SET(TEST_MATH
11 ${DIR_SRCS}
12 )
13 #set extern libraries
14 SET(LIBRARIES
15 libm.so
16 )
17 #add executable file
18 ADD_EXECUTABLE(../bin/bin ${TEST_MATH})
19 #add link library
20 TARGET_LINK_LIBRARIES(../bin/bin ${LIBRARIES})
21
或者另一个:
1 #project name
2 PROJECT(test_math)
3
4 add_definitions("-Wall -lpthread -g")
5
6 #head file path
7 INCLUDE_DIRECTORIES(
8 include
9 )
10
11 #source directory
12 AUX_SOURCE_DIRECTORY(src DIR_SRCS)
13
14 #set environment variable
15 SET(TEST_MATH
16 ${DIR_SRCS}
17 )
18
19 #set extern libraries
20 SET(LIBRARIES
21 libm.so
22 )
23
24 # set output binary path
25 SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
26
27 SET(FS_BUILD_BINARY_PREFIX "Yfs")
28
29 #add executable file
30 ADD_EXECUTABLE(${FS_BUILD_BINARY_PREFIX}sqrt ${TEST_MATH})
31
32 #add link library
33 TARGET_LINK_LIBRARIES(${FS_BUILD_BINARY_PREFIX}sqrt ${LIBRARIES})
这是一个测试数学函数的程序的CMakeLists.txt,"#"后面为注释的内容,CMake的命令全部为大写
第2行指定生成的工程名为test_math
第4行指定头文件目录为include
第8行指定源文件目录为src,并将其赋值给环境变量DIR_SRCS
第10行设定环境变量TEST_MATH的值为环境变量DIR_SRCS的值,此处用于显示如何用环境变量对环境变量进行赋值。
第14行将数学函数库赋值给环境变量LIBRARIES,当然,可以不用这个环境变量,而在后面直接使用该库名
第18行用于指定生成文件,将环境变量TEST_MATH目录下的所有文件编译生成../bin目录下的可执行文件bin
第20行指定../bin/bin执行时的链接库为环境变量LIBRARIES的值-libm.so
src/main.c:
#include<stdio.h>
#include"../include/a.h"
int main()
{
double b=25.0;
double a=0.0;
a=get_sqrt(b);
printf("a is %lf, b is %lf\n",a,b);
return 0;
}
/src/a.c
#include"../include/a.h"
double get_sqrt(double var1)
{
return sqrt(var1);
}
/include/a.h
#ifndef A_FILE_HEADER_INC
#define A_FILE_HEADER_INC
#include<math.h>
double get_sqrt(double var1);
#endif
将CMakeLists.txt放在当前目录下,执行CMakeLists.txt
$> cmake .
$> make
即可生成可执行文件,在目录/bin下的bin文件,好了运行看其效果是否和所想一样。
注:
aux_source_directory: 查找在某个路径下的所有源文件。
aux_source_directory(<dir> <variable>)
搜集所有在指定路径下的源文件的文件名,将输出结果列表储存在指定的变量中。该命令主要用在那些使用显式模板实例化的工程上。模板实例化文件可以存储在Templates子目录下,然后可以使用这条命令自动收集起来;这样可以避免手工罗列所有的实例。
PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR的区别 一般来说,都是这样用
cmake ./
这样PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR是等价的。也就是当前源码的目录。
如果执行cmake的时候,并不在源码的路径的话,比如:
cmake ../src
这样的好处是cmake生成的文件和编译出来的东西,就不放在源码路径下了,保证了源码路径的干净整洁。 比如可以在src的同级目录下建立build目录。然后在build目录下执行:
cmake ../src
这样编译出来的东西和cmake生成的东西,都放到了build目录下了。并且:
PROJECT_BINARY_DIR=全路径/build
PROJECT_SOURCE_DIR=全路径/src
四. CMakeLists.txt的语法
-
注释
- 变量:使用set命令显式定义及赋值,在非if语句中,使用${}引用,if中直接使用变量名引用;后续的set命令会清理变量原来的值;
- command (args ...) #命令不分大小写,参数使用空格分隔,使用双引号引起参数中空格
- set(var a;b;c) <=> set(var a b c) #定义变量var并赋值为a;b;c这样一个string list
- add_executable(${var}) <=> add_executable(a b c) #变量使用${xxx}引用
- 条件语句:
if(var) #var 非empty 0 N No OFF FALSE... #非运算使用NOT
…
else()/elseif() … endif(var)
- 循环语句
Set(VAR a b c)
Foreach(f ${VAR})
…
Endforeach(f)
- 循环语句
WHILE() … ENDWHILE()
-
内部变量
CMAKE_C_COMPILER:指定C编译器
CMAKE_CXX_COMPILER:指定C++编译器
CMAKE_C_FLAGS:编译C文件时的选项,如-g;也可以通过add_definitions添加编译选项
EXECUTABLE_OUTPUT_PATH:可执行文件的存放路径
LIBRARY_OUTPUT_PATH:库文件路径
CMAKE_BUILD_TYPE:build 类型(Debug, Release, ...),CMAKE_BUILD_TYPE=Debug
BUILD_SHARED_LIBS:Switch between shared and static libraries -
内置变量的使用:
在CMakeLists.txt中指定,使用set
cmake命令中使用,如cmake -DBUILD_SHARED_LIBS=OFF -
命令 project (HELLO) #指定项目名称
使用${HELLO_SOURCE_DIR}表示项目根目录
include_directories:指定头文件的搜索路径,相当于指定gcc的-I参数
include_directories (${HELLO_SOURCE_DIR}/Hello) #增加Hello为include目录
link_directories:动态链接库或静态链接库的搜索路径,相当于gcc的-L参数
link_directories (${HELLO_BINARY_DIR}/Hello) #增加Hello为link目录
add_subdirectory:包含子目录
add_subdirectory (Hello)
add_executable:编译可执行程序,指定编译,好像也可以添加.o文件
add_executable (helloDemo demo.cxx demo_b.cxx) #将cxx编译成可执行文件
add_definitions:添加编译参数
add_definitions(-DDEBUG)将在gcc命令行添加DEBUG宏定义;
add_definitions( “-Wall -ansi –pedantic –g”)
target_link_libraries:添加链接库,相同于指定-l参数
target_link_libraries(demo Hello) #将可执行文件与Hello连接成最终文件demo
add_library:
add_library(Hello hello.cxx) #将hello.cxx编译成静态库如libHello.a
add_custom_target: 增加一个没有输出的目标,使得它总是被构建。
enables testing and creates Make check command
add_custom_target(tests
COMMENT "Building tests")
enable_testing()
set(CMAKE_CTEST_COMMAND ctest)
add_custom_target(check
COMMAND ${CMAKE_CTEST_COMMAND}
DEPENDS tests)
set_target_properties( ... ): lots of properties... OUTPUT_NAME, VERSION, ....
为一个目标设置属性。该命令的语法是列出所有你想要变更的文件,然后提供你想要设置的值。你能够使用任何你想要的属性/值对,并且在随后的代码中调用GET_TARGET_PROPERTY命令取出属性的值。
link_libraries( lib1 lib2 ...):
All targets link with the same set of libs
add_dependencies: 怎样添加编译依赖项 用于确保编译目标项目前依赖项必须先构建好
执行CMAKE时打印消息
message([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)
五. 处理多源文件目录的方法
CMake 处理源代码分布在不同目录中的情况也十分简单。现假设我们的源代码分布情况如下,其中 src 目录下的文件要编译成一个链接库。
./step2
|
+---main.cpp
|
+---src
|
+ -- Test1.h
|
+ -- Test1.cpp
- 项目主目录中的 CMakeLists.txt
在目录 step2 中创建文件 CMakeLists.txt 。文件内容如下:
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 )
第三行,使用命令 ADD_SUBDIRECTORY 指明本项目包含一个子目录 src 。
第六行,使用命令 TARGET_LINK_LIBRARIES 指明可执行文件 main 需要连接一个名为Test的链接库 。
- 子目录中的 CMakeLists.txt
在子目录 src 中创建 CmakeLists.txt。文件内容如下:
1 AUX_SOURCE_DIRECTORY(. DIR_TEST1_SRCS)
2 ADD_LIBRARY ( Test ${DIR_TEST1_SRCS})
在执行 cmake 的过程中,首先解析目录 step2 中的 CMakeLists.txt ,当程序执行命令 ADD_SUBDIRECTORY( src ) 时进入目录 src 对其中的 CMakeLists.txt 进行解析。
六. 在工程中查找并使用其他程序库的方法
在开发软件的时候我们会用到一些函数库,这些函数库在不同的系统中安装的位置可能不同,编译的时候需要首先找到这些软件包的头文件以及链接库所在的目录以便生成编译选项。例如一个需要使用博克利数据库项目,需要头文件db_cxx.h 和链接库 libdb_cxx.so,现在该项目中有一个源代码文件 main.cpp ,放在项目的根目录中。
- 程序库说明文件 在项目的根目录中创建目录 cmake/modules/,在 cmake/modules/ 下创建文件 Findlibdb_cxx.cmake ,内容如下:
01 MESSAGE(STATUS "Using bundled Findlibdb.cmake...")
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 )
-
文件 Findlibdb_cxx.cmake 的命名要符合规范: FindlibNAME.cmake, 其中NAME 是函数库的名称。Findlibdb_cxx.cmake 的语法与 CMakeLists.txt 相同。
-
这里使用了三个命令: MESSAGE , FIND_PATH 和 FIND_LIBRARY 。
MESSAGE: 会将参数的内容输出到终端。
FIND_PATH: 指明头文件查找的路径,原型如下:
find_path(<VAR> name1 [path1 path2 ...])
该命令在参数 path* 指示的目录中查找文件 name1 并将查找到的路径保存在变量 VAR 中。
第3-8行的意思是在 /usr/include/ 和 /usr/local/include/ 中查找文件db_cxx.h ,并将 db_cxx.h 所在的路径保存在 LIBDB_CXX_INCLUDE_DIR 中。
命令 FIND_LIBRARY 同 FIND_PATH 类似,用于查找链接库并将结果保存在变量中。
第10-13行的意思是在目录 /usr/lib/ 和 /usr/local/lib/ 中寻找名称为 db_cxx 的链接库,并将结果保存在 LIBDB_CXX_LIBRARIES 。 -
在项目的根目录中创建 CmakeList.txt :
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 中查找 Findlibdb_cxx.cmake
8-19 行: 表示查找链接库和头文件的过程。
第8行: 使用命令 FIND_PACKAGE 进行查找,这条命令执行后 CMake 会到变量 CMAKE_MODULE_PATH 指示的目录中查找文件 Findlibdb_cxx.cmake 并执行。
13-19行:条件判断语句,表示如果 LIBDB_CXX_INCLUDE_DIR 和 LIBDB_CXX_LIBRARIES 都已经被赋值,则设置编译时到 LIBDB_CXX_INCLUDE_DIR 寻找头文件并且设置可执行文件 main 需要与链接库 LIBDB_CXX_LIBRARIES 进行连接。
5) 执行 cmake
七. 使用 cmake 生成 debug 版和 release 版的程序
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 版的程序的 CMakeList.txt :
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 的编译选项。