http://www.ibm.com/developerworks/cn/linux/l-cn-cmake/

http://blog.csdn.net/dbzhang800/article/details/6314073

http://www.cnblogs.com/coderfenghc/archive/2013/01/20/2846621.html

http://blog.sina.com.cn/s/blog_4aa4593d0100q3bt.html

http://hahack.com/codes/cmake/

http://blog.chinaunix.net/uid-24512513-id-3196376.html

http://www.cppblog.com/tx7do/archive/2010/08/19/124000.html

http://name5566.com/1795.html

http://jiyeqian.is-programmer.com/2011/7/4/cmake_tutorial.27813.html

CMake快速入门教程-实战

0. 前言
一个多月前,由于工程项目的需要,匆匆的学习了一下cmake的使用方法,现在有时间拿出来整理一下。本文假设你已经学会了cmake的使用方法,如果你还不会使用cmake,请参考相关资料之后再继续向下看。
本文中介绍的是生成可执行程序的方法和步骤,生成动态库和静态库的方法与此有所不同,随后会介绍动态库和静态库项目中cmake的编写方法。
本文参考《CMake Practice》这篇文章完成,旨在指导用户快速使用CMake,如果需要更详细的内容,请通读《CMake Practice》这篇文章。下载路径:http://sewm.pku.edu.cn/src/paradise/reference/CMake%20Practice.pdf
1. 项目目录结构
我们项目的名称为CRNode,假设我们项目的所有文件存放再~/workspace/CRNode,之后没有特殊说明的话,我们所指的目录都以此目录为相对路径。
我们的目录结构如下:

  1. ~/workspace/CRNode
  2. ├─ src
  3. ├─ rpc
  4. ├─ CRMasterCaller.h
  5. ├─ CRMasterCaller.cc
  6. ├─ CRNode.h
  7. ├─ CRNode.cc
  8. ├─ Schd_constants.h
  9. ├─ Schd_constants.cc
  10. ├─ CRMaster.h
  11. ├─ CRMaster.cc
  12. ├─ CRNode_server.skeleton.h
  13. ├─ CRNode_server.skeleton.cc
  14. ├─ Schd_types.h
  15. └─ Schd_types.cc
  16. ├─ task
  17. ├─ TaskExecutor.h
  18. ├─ TaskExecutor.cc
  19. ├─ TaskMonitor.h
  20. └─ TaskMonitor.cc
  21. ├─ util
  22. ├─ Const.h
  23. ├─ Const.cc
  24. ├─ Globals.h
  25. ├─ Globals.cc
  26. ├─ Properties.h
  27. ├─ Properties.cc
  28. ├─ utils.h
  29. └─ utils.cc
  30. ├─ main.cc
  31. └─ CMakeLists.txt
  32. ├─ doc
  33. └─ crnode.txt
  34. ├─ COPYRIGHT
  35. ├─ README
  36. ├─ crnode.sh
  37. └─ CMakeLists.txt

其中,src存放源代码文件和一个CMakeLists.txt文件,CMakeLists文件的编写我们稍候介绍;doc目录中存放项目 的帮助文档,该文档以及COPYRIGHT和README一起安装到/usr/share/doc/crnode目录中;COPYRIGHT文件存放项目 的版权信息,README存放一些说明性文字;crnode.sh存放CRNode的启动命令;CMakeLists.txt文件稍候介绍。
除此之外,项目还依赖两个外部库:Facebook开发的thrift库,其头文件存放在/usr/include/thrift目录中;log4cpp库,其头文件存放再/usr/include下。
2. CMakeLists.txt文件
本工程中使用了两个CMakeLists.txt文件,分别项目的根目录(即~/workspace/CRNode目录,下同)和src目录中
(参考以上目录结构)。我们先给出两个CMakeLists.txt的内容,在下一节中再对两个CMakeLists.txt进行详细介绍。两个 CMakeLists.txt文件的内容分别如下:
2.1 根目录中CMakeLists内容

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  1. cmake_minimum_required (VERSION 2.6)
  2.  
  3. project (CRNode)
  4.  
  5. ADD_SUBDIRECTORY(src bin)
  6.  
  7. #SET(CMAKE_INSTALL_PREFIX ${PROJECT_BINARY_DIR})
  8. SET(CMAKE_INSTALL_PREFIX /usr/local)
  9.  
  10. INSTALL(PROGRAMS crnode.sh DESTINATION bin)
  11.  
  12. INSTALL(FILES COPYRIGHT README DESTINATION share/doc/crnode)
  13.  
  14. INSTALL(DIRECTORY doc/ DESTINATION share/doc/crnode)

2.2 src/CMakeLists.txt内容

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  1. INCLUDE_DIRECTORIES(/usr/include/thrift)
  2.  
  3. SET(SRC_LIST main.cc
  4. rpc/CRMasterCaller.cpp
  5. rpc/CRNode_server.skeleton.cpp
  6. rpc/Schd_constants.cpp
  7. rpc/CRMaster.cpp
  8. rpc/CRNode.cpp
  9. rpc/Schd_types.cpp
  10. task/TaskExecutor.cpp
  11. task/TaskMoniter.cpp
  12. util/Const.cpp
  13. util/Globals.cc
  14. util/utils.cc
  15. util/Properties.cpp
  16. )
  17.  
  18. ADD_EXECUTABLE(crnode ${SRC_LIST})
  19.  
  20. TARGET_LINK_LIBRARIES(crnode log4cpp thrift)
  21.  
  22. INSTALL(TARGETS crnode
  23. RUNTIME DESTINATION bin
  24. )

3. CMake语法
A. 变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名;
B. 指令(参数 1 参数 2…),参数使用括弧括起,参数之间使用空格或分号分开;
C. 指令是大小写无关的,参数和变量是大小写相关的。但,推荐你全部使用大写指令。

4. CMakeLists.txt剖析
4.1 cmake_minimum_required命令

  1. 1
  1. cmake_minimum_required (VERSION 2.6)

规定cmake程序的最低版本。这行命令是可选的,我们可以不写这句话,但在有些情况下,如果CMakeLists.txt文件中使用了一些高版本cmake特有的一些命令的时候,就需要加上这样一行,提醒用户升级到该版本之后再执行cmake。

4.2 project命令

  1. 3
  1. project (CRNode)

指定项目的名称。项目最终编译生成的可执行文件并不一定是这个项目名称,而是由另一条命令确定的,稍候我们再介绍。
但是这个项目名称还是必要的,在cmake中有两个预定义变量:< projectname >_BINARY_DIR以及<
projectname
>_SOURCE_DIR,在我们的项目中,两个变量分别为:CRNode_BINARY_DIR和CRNode_SOURCE_DIR。内部编译
情况下两者相同,后面我们会讲到外部编译,两者所指代的内容会有所不同。要理解这两个变量的定义,我们首先需要了解什么是“外部构建(out-of-
source build)”,我们将在下一小节中介绍“外部构建”。
同时cmake还预定义了PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR变量。在我们的项目
中,PROJECT_BINARY_DIR等同于CRNode_BINARY_DIR,PROJECT_SOURCE_DIR等同于
CRNode_SOURCE_DIR。在实际的应用用,我强烈推荐使用PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR变
量,这样即使项目名称发生变化也不会影响CMakeLists.txt文件。

4.3 外部构建
假设我们此时已经完成了两个CMakeLists.txt文件的编写,可以执行cmake命令生成Makefile文件了。此时我们由两种方法可以执行cmake、编译和安装:

  1. 1
  2. 2
  1. cmake .
  2. make

或者

  1. 1
  2. 2
  3. 3
  4. 4
  1. mkdir build
  2. cd build
  3. cmake ..
  4. make

两种方法最大的不同在于执行cmake和make的工作路径不同。第一种方法中,cmake生成的所有中间文件和可执行文件都会存放在项目 目录中;而第二种方法中,中间文件和可执行文件都将存放再build目录中。第二种方法的优点显而易见,它最大限度的保持了代码目录的整洁。同时由于第二 种方法的生成、编译和安装是发生在不同于项目目录的其他目录中,所以第二种方法就叫做“外部构建”。
回到之前的疑问,再外部构建的情况下,PROJECT_SOURCE_DIR指向的目录同内部构建相同,仍然为~/workspace
/CRNode,而PROJECT_BINARY_DIR则有所不同,指向~/workspace/CRNode/build目录。
当然,cmake强烈推荐使用外部构建的方法。

4.4 ADD_SUBDIRECTORY命令

  1. 5
  1. ADD_SUBDIRECTORY(src bin)

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。 EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除。比如,工程的 example,可能就需要工程构建完成后,再进入 example 目录单独进行构建。
在我们的项目中,我们添加了src目录到项目中,而把对应于src目录生成的中间文件和目标文件存放到bin目录下,在上一节举例中“外部构建”的情况下,中间文件和目标文件将存放在build/srcobj目录下。

4.5 SET命令

  1. 8
  1. SET(CMAKE_INSTALL_PREFIX /usr/local)

现阶段,只需要了解SET命令可以用来显式的定义变量即可。在以上的例子中,我们显式的将CMAKE_INSTALL_PREFIX的值定 义为/usr/local,如此在外部构建情况下执行make install命令时,make会将生成的可执行文件拷贝到/usr/local/bin目录下。
当然,可执行文件的安装路径CMAKE_INSTALL_PREFIX也可以在执行cmake命令的时候指定,cmake参数如下:

  1. cmake -DCMAKE_INSTALL_PREFIX=/usr ..

如果cmake参数和CMakeLists.txt文件中都不指定该值的话,则该值为默认的/usr/local。

4.6 INCLUDE_DIRECTORIES命令

  1. 1
  1. INCLUDE_DIRECTORIES(/usr/include/thrift)

INCLUDE_DIRECTORIES类似gcc中的编译参数“-I”,指定编译过程中编译器搜索头文件的路径。当项目需要的头文件不在 系统默认的搜索路径时,需要指定该路径。在我们的项目中,log4cpp所需的头文件都存放在/usr/include下,不需要指定;但thrift的 头文件没有存放在系统路径下,需要指定搜索其路径。

4.7 ADD_EXECUTABLE和ADD_LIBRARY

  1. 3
  2. 4
  3. 5
  4. 6
  5. 7
  6. 8
  7. 9
  8. 10
  9. 11
  10. 12
  11. 13
  12. 14
  13. 15
  14. 16
  15. 17
  16. 18
  1. SET(SRC_LIST main.cc
  2. rpc/CRMasterCaller.cpp
  3. rpc/CRNode_server.skeleton.cpp
  4. rpc/Schd_constants.cpp
  5. rpc/CRMaster.cpp
  6. rpc/CRNode.cpp
  7. rpc/Schd_types.cpp
  8. task/TaskExecutor.cpp
  9. task/TaskMoniter.cpp
  10. util/Const.cpp
  11. util/Globals.cc
  12. util/utils.cc
  13. util/Properties.cpp
  14. )
  15.  
  16. ADD_EXECUTABLE(CRNode ${SRC_LIST})

ADD_EXECUTABLE定义了这个工程会生成一个文件名为 CRNode 的可执行文件,相关的源文件是 SRC_LIST 中定义的源文件列表。需要注意的是,这里的CRNode和之前的项目名称没有任何关系,可以任意定义。

4.8 EXECUTABLE_OUTPUT_PATH和LIBRARY_OUTPUT_PATH
我们可以通过 SET 指令重新定义 EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH 变量来指定最终的目标二进制的位置(指最终生成的CRNode可执行文件或者最终的共享库,而不包含编译生成的中间文件)。
命令如下:

  1. SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
  2. SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

需要注意的是,在哪里 ADD_EXECUTABLE 或 ADD_LIBRARY,如果需要改变目标存放路径,就在哪里加入上述的定义。

4.9 TARGET_LINK_LIBRARIES命令

  1. 20
  1. TARGET_LINK_LIBRARIES(CRNode log4cpp thrift)

这句话指定在链接目标文件的时候需要链接的外部库,其效果类似gcc的编译参数“-l”,可以解决外部库的依赖问题。

4.10 INSTALL命令
在执行INSTALL命令的时候需要注意CMAKE_INSTALL_PREFIX参数的值。该参数在3.5中已经有所介绍。其命令形式如下:

  1. INSTALL(TARGETS targets...
  2. [[ARCHIVE|LIBRARY|RUNTIME]
  3. [DESTINATION < dir >]
  4. [PERMISSIONS permissions...]
  5. [CONFIGURATIONS
  6. [Debug|Release|...]]
  7. [COMPONENT < component >]
  8. [OPTIONAL]
  9. ] [...])

参数中的 TARGETS 后面跟的就是我们通过 ADD_EXECUTABLE 或者 ADD_LIBRARY 定义的目标文件,可能是可执行二进制、动态库、静态库。
DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候CMAKE_INSTALL_PREFIX
其实就无效了。如果你希望使用 CMAKE_INSTALL_PREFIX
来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是${CMAKE_INSTALL_PREFIX} /<
destination 定义的路径>
你不需要关心 TARGETS 具体生成的路径,只需要写上 TARGETS 名称就可以了。
非目标文件的可执行程序安装(比如脚本之类):

  1. INSTALL(PROGRAMS files... DESTINATION < dir >
  2. [PERMISSIONS permissions...]
  3. [CONFIGURATIONS [Debug|Release|...]]
  4. [COMPONENT < component >]
  5. [RENAME < name >] [OPTIONAL])

跟上面的 FILES 指令使用方法一样,唯一的不同是安装后权限为OWNER_EXECUTE, GROUP_EXECUTE, 和 WORLD_EXECUTE,即 755 权限目录的安装。
安装一个目录的命令如下:

  1. INSTALL(DIRECTORY dirs... DESTINATION < dir >
  2. [FILE_PERMISSIONS permissions...]
  3. [DIRECTORY_PERMISSIONS permissions...]
  4. [USE_SOURCE_PERMISSIONS]
  5. [CONFIGURATIONS [Debug|Release|...]]
  6. [COMPONENT < component >]
  7. [[PATTERN < pattern > | REGEX < regex >]
  8. [EXCLUDE] [PERMISSIONS permissions...]] [...])

DIRECTORY 后面连接的是所在 Source 目录的相对路径,但务必注意:abc 和 abc/有很大的区别。如果目录名不以/结尾,那么这个目录将被安装为目标路径下的 abc,如果目录名以/结尾,代表将这个目录中的内容安装到目标路径,但不包括这个目录本身。我们来看一个例子:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  1. INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
  2. PATTERN "CVS" EXCLUDE
  3. PATTERN "scripts/*"
  4. PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
  5. GROUP_EXECUTE GROUP_READ)

这条指令的执行结果是:
将 icons 目录安装到 < prefix >/share/myproj,将 scripts/中的内容安装到< prefix
>/share/myproj,不包含目录名为 CVS 的目录,对于 scripts/*文件指定权限为 OWNER_EXECUTE
OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ。
因为crnode.txt 要安装到/< prefix >/share/doc/crnode,所以我们不能直接安装整个 doc 目录,这里采用的方式是安装 doc 目录中的内容,也就是使用”doc/”在工程文件中添加:

  1. 1
  1. INSTALL(DIRECTORY doc/ DESTINATION share/doc/crnode)

5. 编译安装
编译安装结果如下:

  1. [root@sim91 build]# cmake ..
  2. -- Configuring done
  3. -- Generating done
  4. -- Build files have been written to: /home/fify/workspace/CRNode/build
  5.  
  6. [root@sim91 build]# make
  7. Scanning dependencies of target crnode
  8. [ 7%] Building CXX object srcobj/CMakeFiles/crnode.dir/main.cc.o
  9. [ 15%] Building CXX object srcobj/CMakeFiles/crnode.dir/rpc/CRMasterCaller.cpp.o
  10. [ 23%] Building CXX object srcobj/CMakeFiles/crnode.dir/rpc/CRNode_server.skeleton.cpp.o
  11. [ 30%] Building CXX object srcobj/CMakeFiles/crnode.dir/rpc/Schd_constants.cpp.o
  12. [ 38%] Building CXX object srcobj/CMakeFiles/crnode.dir/rpc/CRMaster.cpp.o
  13. [ 46%] Building CXX object srcobj/CMakeFiles/crnode.dir/rpc/CRNode.cpp.o
  14. [ 53%] Building CXX object srcobj/CMakeFiles/crnode.dir/rpc/Schd_types.cpp.o
  15. [ 61%] Building CXX object srcobj/CMakeFiles/crnode.dir/task/TaskExecutor.cpp.o
  16. [ 69%] Building CXX object srcobj/CMakeFiles/crnode.dir/task/TaskMoniter.cpp.o
  17. [ 76%] Building CXX object srcobj/CMakeFiles/crnode.dir/util/Const.cpp.o
  18. [ 84%] Building CXX object srcobj/CMakeFiles/crnode.dir/util/Globals.cc.o
  19. [ 92%] Building CXX object srcobj/CMakeFiles/crnode.dir/util/utils.cc.o
  20. [100%] Building CXX object srcobj/CMakeFiles/crnode.dir/util/Properties.cpp.o
  21. Linking CXX executable crnode
  22.  
  23. [root@sim91 build]# make install
  24. [100%] Built target crnode
  25. Install the project...
  26. -- Install configuration: ""
  27. -- Installing: /usr/local/bin/crnode.sh
  28. -- Installing: /usr/local/share/doc/crnode/COPYRIGHT
  29. -- Installing: /usr/local/share/doc/crnode/README
  30. -- Installing: /usr/local/share/doc/crnode
  31. -- Installing: /usr/local/share/doc/crnode/crnode.txt
  32. -- Installing: /usr/local/bin/crnode

大功告成!更多内容请参考《CMake Practice》,再次对《CMake Practice》的作者表示感谢!

CMake快速入门教程-实战的更多相关文章

  1. 转:CMake快速入门教程-实战

    CMake快速入门教程:实战 收藏人:londonKu     2012-05-07 | 阅:10128  转:34    |   来源   |  分享               0. 前言一个多月 ...

  2. CMake快速入门教程:实战

    转自http://blog.csdn.net/ljt20061908/article/details/11736713 0. 前言    一个多月前,由于工程项目的需要,匆匆的学习了一下cmake的使 ...

  3. [转]CMake快速入门教程:实战

    转自http://blog.csdn.net/ljt20061908/article/details/11736713 0. 前言    一个多月前,由于工程项目的需要,匆匆的学习了一下cmake的使 ...

  4. BIML 101 - ETL数据清洗 系列 - BIML 快速入门教程 - 序

    BIML 101 - BIML 快速入门教程 做大数据的项目,最花时间的就是数据清洗. 没有一个相对可靠的数据,数据分析就是无木之舟,无水之源. 如果你已经进了ETL这个坑,而且预算有限,并且有大量的 ...

  5. ElasticSearch实战系列六: Logstash快速入门和实战

    前言 本文主要介绍的是ELK日志系统中的Logstash快速入门和实战 ELK介绍 ELK是三个开源软件的缩写,分别表示:Elasticsearch , Logstash, Kibana , 它们都是 ...

  6. 专为设计师而写的GitHub快速入门教程

    专为设计师而写的GitHub快速入门教程 来源: 伯乐在线 作者:Kevin Li     原文出处: Kevin Li 在互联网行业工作的想必都多多少少听说过GitHub的大名,除了是最大的开源项目 ...

  7. EntityFramework6 快速入门教程

    EntityFramework6 快速入门教程 不得不说EF在国内实在是太小众,相关的技术文章真实屈指可数,而且很多文章都很旧了,里面使用的版本跟如今的EF6差别还是比较大.我刚开始弄这个的时候真是绕 ...

  8. Apple Watch开发快速入门教程

     Apple Watch开发快速入门教程  试读下载地址:http://pan.baidu.com/s/1eQ8JdR0 介绍:苹果为Watch提供全新的开发框架WatchKit.本教程是国内第一本A ...

  9. 指示灯组与3个复位按钮的介绍Arduino Yun快速入门教程

    指示灯组与3个复位按钮的介绍Arduino Yun快速入门教程 1.4.2  指示灯组 指示灯组的放大图如图1.5所示. 图1.5  指示灯组 各个指示灯对应的功能如下: q  RX:对应于0号端口, ...

随机推荐

  1. Spring MVC url提交参数和获取参数

    [转载:http://blog.csdn.net/mahoking] 普通URL提交参数         该格式url为:url.do?param1=mahc&param2=8888.00 需 ...

  2. 部分实用的SQL语句

    一.在数据库创建表格的SQL语句 1,创建一个link表格,包含属性:lid  主键,title 标题,  imgpath 图片地址 , url  网址  , info 说明,  isshow 显示1 ...

  3. VC++中操作XMLWin32实例

    摘要:VC++中操作XML XML在Win32程序方面应该没有在Web方面应用得多,很多Win32程序也只是用XML来存存配置信息而已,而且没有足够的好处的话还不如用ini.VC++里操作XML有两个 ...

  4. shell 判断文件、目录是否存在

    shell判断文件是否存在   1. shell判断文件,目录是否存在或者具有权限 2. #!/bin/sh 3. 4. myPath="/var/log/httpd/" 5. m ...

  5. 关于坑爹的PopupWindow的“阻塞”争议问题:Android没有真正的“阻塞式”对话框

    请先允许我对网上某些没经过亲自实践人云亦云的同志呵呵... 那么开始正文,首先"阻塞"这个词本身就存在理解上的差异! 一般我们理解的阻塞,是阻塞了某个线程,即代码执行到这里后等待这 ...

  6. Java设计模式---装饰模式

    装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰模式的结构 装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任.换言之,客户 ...

  7. linux nohup命令

    nohup 命令 用途:不挂断地运行命令.如果你正在执行一个job,并且你希望在退出帐户/关闭终端之后继续运行,可以使用nohup命令.nohup就是不挂起的意思( no hang up). 语法:n ...

  8. 查看linux系统版本

    1.查看内核版本 #cat /proc/version Linux version 2.6.18-164.el5 (mockbuild@x86-003.build.bos.redhat.com) (g ...

  9. 使用HighCharts实现实时数据展示

    在众多的工业控制系统领域常常会实时采集现场的温度.压力.扭矩等数据,这些数据对于监控人员进行现场态势感知.进行未来趋势预测具有重大指导价值.工程控制人员如果只是阅读海量的数据报表,对于现场整个态势的掌 ...

  10. 像table一样布局div的CSS属性详解

    .equal {                     display:table;                     border-collapse:separate;margin: aut ...