本文是cmake使用过程中的简单笔记,写作过程中大量参考了《CMake Practice中文版1》,如果需要快速从零开始学习cmake,推荐阅读CMake Practice。本人使用cmake的起因是因为工作时的一个项目需要使用C++,虽然代码量不多,但是我还是不愿意使用make直接管理。鉴于我个人又对gnu autotools有偏见,故选择使用cmake。
本文使用的示例代码放在:cmake example1
项目框架
示例项目的代码框架如下所示:
$ tree . . ├── build.sh ├── CMakeLists.txt ├── doc │ └── example1.txt ├── etc │ └── example1.conf ├── README.md ├── src │ ├── CMakeLists.txt │ ├── lib │ │ ├── CMakeLists.txt │ │ ├── include │ │ │ ├── speak.h │ │ │ └── speaktools.h │ │ ├── speak.cpp │ │ └── tools │ │ └── speaktools.cpp │ ├── lileiandhanmeimei │ │ ├── CMakeLists.txt │ │ └── lileiandhanmeimei.cpp │ └── test │ ├── CMakeLists.txt │ ├── testspeak.cpp │ ├── testtest1.cpp │ ├── testtest2.cpp │ └── unittest.h └── unittest.sh
项目最终给出的产品是一个动态库文件(.so)、一个静态库文件(.so)、头文件(.h)、一个可执行文件、配置文件和普通文档。
- buld.sh是项目构建脚本,unittest.sh执行单元测试
- doc存放文档
- etc存放配置文件
- src是项目代码目录
- lib是库代码
- lileiandhanmeimei
- test目录是测试代码
step by step
基础使用说多了都是废话,不如直接看官方文档或者CMake Practice,网上也有各种“hello,world”。这里叙述一下创建过程,下一节讲一下一些小细节点。当然,这里的step by step和实际开发中显然是不一样的,这里主要为了说明各文件都是些什么。
- 创建etc,doc,src,src/lib,src/lileiandhanmeimei,src/test等目录
- 创建doc/example1.txt和etc/example1.conf,因为这些文件只用来演示,里面随便什么内容都无关紧要
- 在src目录下创建各源代码文件,具体内容见项目链接
- 创建各个CMakeLists.txt。最外层的CMakeLists.txt设定项目名,包含子目录,设置编译后的文件存放目录。src里的CMakeLists.txt只是包含了子目录。test里面的CmakeLists.txt指定了哪几个是测试用例。
老实说,以上完全不配叫做”step by step”,但是实在没什么好啰嗦的。
构建脚本build.sh如下:
#!/usr/bin/env bash if [ $# -gt 1 ];then echo "the arguments error !" echo "no arguments or only -DCMAKE_INSTALL_PREFIX=/xxx/xxx" exit -1 fi if [ -e "build" ]; then rm -rf build fi mkdir build cd build if [ $# -eq 0 ];then cmake .. elif [ $# -eq 1 ];then cmake $1 .. fi make
测试脚本简单使用了cmake的ctest
#!/usr/bin/env bash cd build/src/test/ ctest
一些细节点
rpath
本项目中lileiandhanmeimei是依赖libspeak.so的。构建时如果只使用LINK_DIRECTORIES(${PROJECT_BINARY_DIR}/lib),lileiandhanmeimei中的rpath将写成{PROJECT_BINARY_DIR}/lib的绝对路径。这样,如果libspeak.so的安装目录不在系统的默认搜索目录下时,lileiandhanmeimei将会找不到libspeak.so,从而导致lileiandhanmeimei运行失败(当然这时候编译目录已经被删除,也就是{PROJECT_BINARY_DIR}/lib下的库已经不存在)。
为了解决这个问题,增加两行配置:
SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) SET(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib)
这样lileiandhanmeimei中的rpath就会成为安装libspeak.so的目录。例如,如果传给cmake参数-DCMAKE_INSTALL_PREFIX=/tmp/test,那么libspeak.so将会被安装在/tmp/test/lib下,而lileiandhanmeimei的rpath也会设置成/tmp/test/lib。
对于test中的测试程序,不用这一步,因为他们只在编译后测试使用,使用的库就应该是编译目录中的库文件。
动态库和静态库文件名冲突
如果想要同时生成动态库和静态库,就有一个名字冲突的问题,具体请参见cmake practice。这里只给出解决方案:
ADD_LIBRARY(speak_shared SHARED ${SPEAK_SRC}) ADD_LIBRARY(speak_static STATIC ${SPEAK_SRC}) SET_TARGET_PROPERTIES(speak_shared PROPERTIES OUTPUT_NAME "speak") SET_TARGET_PROPERTIES(speak_static PROPERTIES OUTPUT_NAME "speak") SET_TARGET_PROPERTIES(speak_shared PROPERTIES CLEAN_DIRECT_OUTPUT 1) SET_TARGET_PROPERTIES(speak_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) SET_TARGET_PROPERTIES(speak_shared PROPERTIES VERSION 1.0 SOVERSION 1.0)
尚待解决
- 项目里面的依赖关系处理(库和可执行文件)
- 编译时对系统环境的检测(configure脚本的功能)
- 如果执行uninstall
- 如何集成打包rpm