本文是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)、一个可执行文件、配置文件和普通文档。
基础使用说多了都是废话,不如直接看官方文档或者CMake Practice,网上也有各种“hello,world”。这里叙述一下创建过程,下一节讲一下一些小细节点。当然,这里的step by step和实际开发中显然是不一样的,这里主要为了说明各文件都是些什么。
老实说,以上完全不配叫做”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
本项目中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)