前言

Make工具因遵循不同的规范和标准,执行的Makefile的格式也是不同。主流的Make工具包括:

  • GNU Make
  • QT的qmake
  • 微软的 MS nmake
  • BSD的 pmake

每个平台都有自己的工具,则带来了很大的平台兼容性问题。CMake是一种跨平台的编译工具。

准备阶段:

  • 安装cmake
  • 编写CMake配置文件CMakeLists.txt

基本流程:

  • 执行cmake PATH或者ccmake PATH命令,将CMakeLists.tx文件转化为所需要的Makefile文件。其中PATH为CMAKLISTs.txt所在的目录
  • 执行make命令,编译原码生成可执行程序,或者库文件

单目录,单文件

一个简单的样例:

# CMake的最低版本要求
cmake_minimum_required (VERIONS 2.8) # 项目信息
project(Demo) # 指定生成目标
add_executable(Demo demo.cc)

语法规则:

  • 命令、空格、注释组成
  • 命令是不区分大小写的
  • 符号#后面内容为注释
  • 参数之间使用空格分隔

单目录,多文件

在实际项目中,一般不会只有一个demo.cc源码文件,常为一个目录下多个源文件。假设目录结构如下:

./Demo
|-- main.cc
|-- foo.cc
|-- foo.h

此时的CMakeLists.txt内容可以更新为如下:

# CMake的最低版本要求
cmake_minimum_required (VERIONS 2.8) # 项目信息
project(Demo) # 指定生成目标
add_executable(Demo demo.cc foo.cc)

即只需要在add_executable里把依赖的foo.cc源文件添加进来即可。

但引入另一个问题:新增的源文件越来越多,总不能一个个手动加进来吧?

cmake中有 aux_source_directory命令,会查找指定目录下所有源文件,并存放到指定的变量名中:

# CMake的最低版本要求
cmake_minimum_required (VERIONS 2.8) # 项目信息
project(Demo) # 查找当前目录下所有源文件
aux_source_directory(. DIR_SRCS) # 指定生成目标
add_executable(Demo ${DIR_SRCS})

多目录,多文件

针对一个项目中包含了多了层级目录,且每个目录下都包含一些源文件。若目录结构如下:

./Demo
|-- main.cc
|-- utils
|-- foo.cc
|-- foo.h

我们需要分别在Demoutils目录下各自编写一个CMakeLists.txt文件。

为了方便,可以先将utils目录里的文件编译成静态库,再由main函数调用。

根目录中的CMakeLists.txt

# CMake的最低版本要求,如果不满足则报错
cmake_minimum_required (VERIONS 2.8 FATAL_ERROR) # 项目信息
project(Demo) # 添加 math 子目录
add_subdirectory(utils) # 指定生成目标
add_executable(Demo main.cc) # 添加链接库
target_link_libraries(Demo utils)
  • add_subdirectory表示会处理子目录下的CMakeLists.txt和源代码
  • target_link_libraries表示main执行文件需要链接一个名为utils的链接库

utils目录中的CMakeLists.txt

# 查找当前目录下的所有源文件、并保存到 DIR_LIB_SRCS 变量中
aux_source_directory(. DIR_LIB_SRCS) # 生成链接库
add_library(utils ${DIR_LIB_SRCS})
  • add_library会将所有源文件编译为静态链接库

其他编译选项

如下是一个项目的CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)

project(Demo)

# 定一个开关选项,支持cmake时通过 -DUSE_MYUTILS=OFF 指定
option(USE_MYUITLS "whether use customized math" ON) # 自定义分支逻辑
if(USE_MYUITLS)
include_directories("{PROJECT_SOURCE_DIR}/utils")
add_subdirectory(utils)
set(EXTRA_LIBS ${EXTRA_LIBS} utils)
endif(USE_MYUITLS) # 查找目录下所有源文件
aux_source_directory(. DIR_SRCS)
# 添加执行文件
add_executable(Demo ${DIR_SRCS})
# 链接静态库
target_link_libraries(Demo ${EXTRA_LIBS})

关于option的生效机制,这里详细解释下。如main.cc中的代码:

#include <stdio.h>
#include <stdlib.h>
#include "config.h" // 此头文件是cmake自动生成的 #ifdef USE_PYUTILS
#include "utils/foo.h"
#else
#include <foo.h> // 假设标准库有foo.h头文件
#endif

为了打通CMakeLists.txt一键便携式配置,我们需要编写一个config.h.in文件:

#cmakedefine USE_MYUTILS

这样,在执行cmake命令时,就可以根据配置的参数,自动生成option相关的头文件。若指定-DUSE_MYUTILS=ON时,config.h中的内容为:

#define USE_MYUTILS

若为OFF时,则config.h的内容为:

/* #undef USE_MYUTILS */

安装和测试

camke支持安装测试,通过在生成Makefile后,使用make installmake test来执行。

接上述样例,首先在utils/CMakeLists.txt中加上如下内容:

# 指定utils库的安装路径
install(TARGETS utils DESTINATION bin)
install(FILES utils.h DESTINATION include)

Demo/CMakeLists.txt添加如下内容:

# 指定安装路径
install(TARGETS Demo DESTINATION bin)
install(FILES "${PROJECT_BINARY}/config.h" DESTINATION include)

原理 & 流程:

  • cmake编译产出的Demo文件和库libUtils.o文件将会被复制到/usr/local/bin
  • 头文件utils.hconfig.h则会被赋值到/use/local/include
  • 可以通过CMKAE_INSTALL_PREFIX修改默认安装的根目录/usr/local

关于测试,CMake提供一个称为CTest的测试工具,通过add_test命令添加:

# 启用测试
enable_testing() # 测试程序是否成功运行, arg*为函数接收的参数
add_test(test_run Demo arg1 arg2) add_test(test_usage Demo)
set_tests_properties(test_usage PROPERTIES PASS_REGULAR_EXPRESSION "Usage: .....") add_test(test_result Demo 10 2)
# 测试输出的结果是否包含字符串 "is 100"
set_tests_properties(test_result PROPERTIES PASS_REGULAR_EXPRESSION "is 100")

支持gdb

CMake支持gdb的方式很简单,只需指定Debug模式下开启-g,一个简单的样例如下:

set(CMAKE_BUILD_TYPE "Debug")
# debug模式下编译选项
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
# release模式下编译选项
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

小结

  • CMake的语法主要以命令、空格、参数来组成

  • 可以通过 set(<variable> <value>)设置变量的值

  • if语法

    if(<condition>)
    <commands>
    elseif(<condition>) # optional block, can be repeated
    <commands>
    else() # optional block
    <commands>
    endif()
  • for语法

    # usage 1:
    foreach(<loop_var> <items>)
    <commands>
    endforeach() # usage 2:
    foreach(<loop_var> RANGE <stop>)
  • while语法

    while(<condition>)
    <commands>
    endwhile()

附录:

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

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

    http://www.ibm.com/developerworks/cn/linux/l-cn-cmake/ http://blog.csdn.net/dbzhang800/article/detai ...

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

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

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

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

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

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

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

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

  6. EntityFramework6 快速入门教程

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

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

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

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

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

  9. 游戏控制杆OUYA游戏开发快速入门教程

    游戏控制杆OUYA游戏开发快速入门教程 1.2.2  游戏控制杆 游戏控制杆各个角度的视图,如图1-4所示,它的硬件规格是本文选自OUYA游戏开发快速入门教程大学霸: 图1-4  游戏控制杆各个角度的 ...

  10. Query 快速入门教程

    Query 快速入门教程 http://www.365mini.com/page/jquery-quickstart.htm#what_is_jquery jquery常用方法及使用示例汇总 http ...

随机推荐

  1. C#数据去重的5种方式,你知道几种?

    前言 今天我们一起来讨论一下关于C#数据去重的的5种方式,每种方法都有其特点和适用场景,我们根据具体需求选择最合适的方式.当然欢迎你在评论区留下你觉得更好的C#数据去重的方式. 使用HashSet去重 ...

  2. #分块,懒标记#LOJ 3631「2021 集训队互测」学姐买瓜

    题目传送门 分析 有一个很简单的做法就是处理出每个位置能够一次到达的最左边的右端点(后继). 然后直接从 \(l\) 开始能跳就跳,这样单次询问时间复杂度是 \(O(n)\) 的. 观察到时间复杂度因 ...

  3. #树状数组#洛谷 5677 [GZOI2017]配对统计

    题目 分析 考虑处理出所有右端点的能够匹配的左端点,然后用树状数组离线查询 代码 #include <cstdio> #include <cctype> #include &l ...

  4. java中的内部类内部接口详解

    目录 简介 内部类 静态内部类 非静态内部类 静态方法内部类 非静态方法的内部类 匿名类 内部接口 总结 简介 一般来说,我们创建类和接口的时候都是一个类一个文件,一个接口一个文件,但有时候为了方便或 ...

  5. 深入理解 C++ 语法:从基础知识到高级应用

    C++ 语法 让我们将以下代码分解以更好地理解它: 示例 #include <iostream> using namespace std; int main() { cout <&l ...

  6. C++ 面试必备:常见 C++ 面试题汇总及详细解析

    C++作为一门重要的编程语言,其在面试中常常是热门的考察对象.本文将会介绍一些常见的C++面试题,帮助C++面试者避免很多不必要的困惑和迷惑.每个问题都有相对应的答案,以便各位同学快速查阅. C++和 ...

  7. Windows 杀毒简单有效的方式

    Windows 电脑杀毒通常会选择杀毒软件,这样太笨重,且容易占内存和存在流氓软件侵入. 推荐使用 Windows 自带的恶意软件删除工具 按住 Win + R 键,弹出运行窗口,输入 mrt. 系统 ...

  8. centos部署Django二:项目上传及测试

    1. 上传项目 用 ftp 或者 sftp 上传项目到服务器. *:如果上传时,报各种错误,可以考虑下是不是服务器中文件夹权限的问题.如果是权限的问题,可以使用命令修改文件夹权限后在上传:chmod ...

  9. Qt调用系统DLL,判断网络连接状态

    *: Win32 网络连接 dll 文件名叫:wininet.dll,位置在 C:\WINDOWS\system32 目录下,将 其拷贝到项目工程下. #include <QLibrary> ...

  10. RC4Drop加密技术:原理、实践与安全性探究

    第一章:介绍 1.1 加密技术的重要性 加密技术在当今信息社会中扮演着至关重要的角色.通过加密,我们可以保护敏感信息的机密性,防止信息被未经授权的用户访问.窃取或篡改.加密技术还可以确保数据在传输过程 ...