前言

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. llama2+localGPT打造纯私有知识助手

    通过部署llama2系列,可以构建本地私有的知识小助手 用来输出一写周报.月报,甚至辅助数据分析都可以(想想都很轻松) 想要大模型支持特定的数据集,就需要进行专业的fine-turing 但是fine ...

  2. 网络设备性能指标之pps

    基本概念: Bps:Byte per second 每秒传输多少字节 bps: bits per second 每秒传输多少位 ,这个也叫做端口速率 pps:Packet Per Second(包每秒 ...

  3. #根号分治#洛谷 3645 [APIO2015]雅加达的摩天楼

    题目传送门 分析 设 \(d[i][j]\) 表示 所处位置为 \(i\),跳跃能力为 \(j\) 的步数, 若 \(j\leq \sqrt{n}\),这样的状态最多有 \(n\sqrt{n}\) 个 ...

  4. #博弈论#HDU 1847 Good Luck in CET-4 Everybody!

    题目 有\(n\)个石子,每次只能取2的自然数幂个, 取完石子的人获胜,问先手是否必胜 分析 如果不是3的倍数,那么取完一次一定能剩下3的倍数个, 反之亦然,那么3的倍数为必败状态 代码 #inclu ...

  5. #后缀数组#洛谷 4051 [JSOI2007]字符加密

    题目 分析 将字符串复制一份放入末尾,将其后缀排序之后 SA数组既然表示排名为\(i\)的后缀的起始位置, 那么只要它在\([1,len]\)范围内就是合法的, 那么输出以这个位置开头长度为\(len ...

  6. 深入了解 Spring Boot 核心特性、注解和 Bean 作用域

    Spring Boot 是什么? Spring Boot 是基于 Spring Framework 构建应用程序的框架,Spring Framework 是一个广泛使用的用于构建基于 Java 的企业 ...

  7. 一图读懂HUAWEI HiAI Foundation

    作为华为端侧AI的创新开放平台,HMS Core的HUAWEI HiAI Foundation开放AI算力,助力AI应用高效开发,同时联合多领域打造AI生态,实现日调用600亿次的突破,助力AI生态繁 ...

  8. Qt 桌面服务 QDesktopServices

    使用浏览器打开网址 #include <QDesktopServices> #include <QUrl> QUrl url(QString("https://cn. ...

  9. (数据科学学习手札159)使用ruff对Python代码进行自动美化

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 大家好我是费老师,在日常编写Python代码的过 ...

  10. CentOS 8开启防火墙端口关闭防火墙端口端口对外放行

    1:查看firewall防火墙状态 firewall-cmd --state 或者 systemctl status firewalld 2:打开防火墙 systemctl start firewal ...