本文是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和实际开发中显然是不一样的,这里主要为了说明各文件都是些什么。

  1. 创建etc,doc,src,src/lib,src/lileiandhanmeimei,src/test等目录
  2. 创建doc/example1.txt和etc/example1.conf,因为这些文件只用来演示,里面随便什么内容都无关紧要
  3. 在src目录下创建各源代码文件,具体内容见项目链接
  4. 创建各个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

Footnote



blog comments powered by Disqus

Published

30 January 2015

Tags