如何编写一个CMake工程

  • 上文分析了针对一个源文件、多个源文件、多个目录的情况
  • 1 CMake自定义编译选项Demo4
    • 1.1 效果展示
  • 2 CMake安装(make install)与测试(make test)Demo5
    • 2.1 安装(make install)
    • 2.2 测试(make test)
  • 3 配置Debug,添加版本号Demo6
    • 3.1 Debug/Release配置
    • 3.2 添加版本号

初识CMake,如何编写一个CMake工程(上)
https://blog.csdn.net/weixin_39956356/article/details/115253373

上文分析了针对一个源文件、多个源文件、多个目录的情况

接下来会继续分享自定义编译选项、安装与测试、生成安装包、环境检查等内容

1 CMake自定义编译选项Demo4

如下图,如何实现如下效果,当然,这是很常见的。

首先,应该添加一个宏名–USE_MYMATH,使用option(宏名 描述 默认值),如上图,默认值是ON

option(USE_MYMATH "use provided math implementation" ON)# 加入一个配置头文件config.h.in,用于编译选项的设置,注意这个文件必须用户提前建立,否则编译错误--找不到该文件
configure_file("${PROJECT_SOURCE_DIR}/config.h.in""${PROJECT_BINARY_DIR}/config.h"
)

进而,一种想法油然而生,条件编译,为了适应不同的平台、不同操作系统、不同系统环境。使用条件编译就会让CMake编译出适应当前环境、当前操作系统、当前平台的可执行程序。
目标:我们自己写了一个计算次方的函数,当然系统math.h也有,通过USE_MYMATH来选择是否使用自己的函数?

下面的代码,利用USE_MYMATH进行条件编译,

  1. 指示应该包含的头文件路径include_directories
  2. 需要编译math文件夹add_subdirectory,生成的库名是mathfun
  3. 最后将mathfun赋值给EXTRA_LIBS ,主要是方便最后的链接,如果利用系统的math.h,那么EXTRA_LIBS就是空啦
# 是否加入mathfun
if (USE_MYMATH)# include_directories("${PROJECT_SOURCE_DIR}/math")add_subdirectory(math)                              # 编译其他文件夹的源代码set(EXTRA_LIBS mathfun)
endif(USE_MYMATH)# 编译当前目录下的源文件
aux_source_directory(. DIR_SRC)
add_executable(Demo ${DIR_SRC})# 链接其他文件夹下的动态、静态库
target_link_libraries(Demo ${EXTRA_LIBS})

这时候main.cc需要对应的修改,逻辑上,如果定义了USE_MYMATH,就是用自己的头文件,进而计算一个次方。
注意
1. 这里最重要的是包含了一个#include "config.h",尤其注意,这个文件是CMake自动生成的,而且必须包含进来,否则怎么也不会使用自己的函数库mathfun
流程
编程人员需要建立一个config.h.in文件,并配置好,CMake通过这个文件自动生成config.h,最后由用户包含进main中

#include <stdio.h>
#include <stdlib.h>
#include "config.h"#ifdef USE_MYMATH#include "math/MathFunctions.h"
#else#include <math.h>
#endifint main(int argc, char *argv[])
{if (argc < 3){printf("Usage: %s base exponent \n", argv[0]);return 1;}double base = atof(argv[1]);int exponent = atoi(argv[2]);#ifdef USE_MYMATHprintf("Now we use our own Math library. \n");double result = power(base, exponent);
#elseprintf("Now we use the standard library. \n");double result = pow(base, exponent);
#endifprintf("%g ^ %d is %g\n", base, exponent, result);return 0;
}

1.1 效果展示

工程结构

.
├── CMakeLists.txt
├── config.h.in-------------(提前创建并写入配置,不然会报错)
├── main.cc
└── math├── CMakeLists.txt├── MathFunctions.cc└── MathFunctions.h

首先配置config.h.in,告诉CMake我们需要使用宏名USE_MYMATH

// 表示启用宏名USE_MYMATH,而且会在config.h中自动加入对应代码
#cmakedefine USE_MYMATH

目标:想使用自己的库?
有图形界面的同学可以修改图中ON或者OFF,观察输出。没有直接在源码修改即可

option(USE_MYMATH "use provided math implementation" ON)       # 修改ON或者OFF


自动生成的config.h

// 表示启用宏名USE_MYMATH,而且会在config.h中自动加入对应代码
#define USE_MYMATH

结果:和预期相同,使用自己函数库

➜  Demo4 git:(master) ✗ ./Demo 2 6
Now we use our own Math library.
2 ^ 6 is 64

假如我想使用系统库?

自动生成的config.h

// 表示启用宏名USE_MYMATH,而且会在config.h中自动加入对应代码
/* #undef USE_MYMATH */

结果:和预期相同,使用自己库函数

➜  Demo4 git:(master) ✗ ./Demo 5 6
Now we use the standard library.
5 ^ 6 is 15625

细心的同学会发现,使用math系统库的时候,连mathfun都没有编译,这是符合实际的。不是使用谁就去链接谁,而是不使用编译都不会

2 CMake安装(make install)与测试(make test)Demo5

一个软件在编译成功需要安装到用户的系统中,同时还可以测试一些简单的案例。现在分享下怎么实现的。
首先
install和test是Makefile(名字只有两种写法,这是一种,不要乱写)伪目标,CMake通过简单的语法就可以帮助自动在Makefile生成这些伪目标。

2.1 安装(make install)

安装是一个复杂的过程,因为需要拷贝所有的头文件、静态/动态库到系统的目录中,同时需要提升权限。
头文件散落在各种目录下,需要一个不多一个不少的拷贝,当然这不是本文的重心,下面就简单示意下
使用install()就可以执行安装操作,其实就是一个拷贝的过程,当然,拷贝对象不是单一的,可能有单一文件或者目录等。CMake提供不同的关键字
比如,拷贝文件–拷贝指定文件到指定目标,DESTINATION 后面是目标目录,同理TARGETS。
更多的信息可以参考官方,或者学习经典的openCV、openVINO等开源工程的使用方法

install(FILES 源文件 DESTINATION 目标目录)
# 安装(make isntall)
# Installing Targets
install(TARGETS Demo DESTINATION bin)
# Installing Files
install(FILES "${PROJECT_BINARY_DIR}/config.h" DESTINATION include)

使用

  1. 先编译哈, cmake . && make
  2. 安装,记得提升权限sudo make install
➜  Demo5 git:(master) ✗ sudo make install
[sudo] password for topeet:
[ 50%] Built target mathfun
[100%] Built target Demo
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/bin/Demo
-- Installing: /usr/local/include/config.h

2.2 测试(make test)

废话不多说,直接看模板

  1. 使用测试功能enable_testing
  2. 添加测试用例add_test
  3. 和预设值比对set_tests_propertiesPROPERTIES PASS_REGULAR_EXPRESSION "is 25"通过表达式值是26—FAILED
# 测试(make test)--相同的测试案例会自动忽略
# 使能测试
enable_testing()# 3号测试,结果是25,其他都会失败
add_test(test_5_2 Demo 5 2)
set_tests_properties(test_5_2 PROPERTIES PASS_REGULAR_EXPRESSION "is 26")     # 不写is也可以,会自动匹配答案

output

    Start 3: test_5_2
3/7 Test #3: test_5_2 .........................***Failed  Required regular expression not found.Regex=[26

当然比还可以写很多测试,但是这就很繁琐了。。。
如何简化工作,使用宏函数macro,也就是一个函数咯

  1. 注意语法形式,do_test是宏函数名字
  2. arg1 arg2 result表示两个输入,一个结果。注意CMake语法中都没有逗号,因为它不是函数,是宏,说函数好理解,
  3. ${arg1}表示取arg1的值,CMake支持shell语法
# 大量测试--定义一个宏,简化测试工作
macro(do_test arg1 arg2 result)add_test(test_${arg1}_${arg2} Demo ${arg1} ${arg2})set_tests_properties(test_${arg1}_${arg2} PROPERTIES PASS_REGULAR_EXPRESSION ${result})
endmacro(do_test)do_test(5 3 "is 125")
do_test(2 9 "is 512")
do_test(10 5 "is 100000")

注意,一摸一样的测试案例,是不会执行的

3 配置Debug,添加版本号Demo6

CMake编译的程序支持Debug/Release,也支持版本管理

3.1 Debug/Release配置

结果大家可以测试,Debug加了调试信息,Demo会大些,可以验证是否出错

  1. CMAKE_BUILD_TYPE 取Debug/Release
  2. 实际上是靠CMAKE_CXX_FLAGS_DEBUG 也即是gcc编译参数,-g即生成调试,最低优化(不优化),-wall表示让编译时能显示更多的错误信息,,,使其编译更准确,,,
  3. 其他的编译参数可以参考gcc官方文档
# 1 版本配置,Debug生成的文件会大些
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
# set(CMAKE_BUILD_TYPE "Release")
# set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

3.2 添加版本号

先自定义两个主次版本号的宏名Demo_VERSION_MAJORDemo_VERSION_MINOR
后在config.h.in写入配置
最后使用

  1. 根CMakeLists.txt
# 2 添加版本号,还需要再config.h.in添加宏定义
set(Demo_VERSION_MAJOR 1)
set(Demo_VERSION_MINOR 0)
  1. 配置config.h.in
// 版本号的宏定义
#define Demo_VERSION_MAJOR @Demo_VERSION_MAJOR@
#define Demo_VERSION_MINOR @Demo_VERSION_MINOR@
  1. main.cc
    if (argc < 3){// print version infoprintf("%s Version %d.%d\n",argv[0],Demo_VERSION_MAJOR,Demo_VERSION_MINOR);printf("Usage: %s base exponent \n", argv[0]);return 1;}

一个案例

./Demo Version 1.0
Usage: ./Demo base exponent

初识CMake,如何编写一个CMake工程(下)相关推荐

  1. 初识CMake,如何编写一个CMake工程(上)

    如何编写一个CMake工程 笔者想分享CMake工程的原因? 1 接触CMake 1.1 认识CMake被广泛的使用? 1.2 了解CMake运行流程 1.3 Make和Makefile是什么关系? ...

  2. 【ceph】cmake管理Ceph编译+Ceph工程目录+cmake 实战学习

    前言 Ceph cmake 工程 cmake生成的目录 cmake工程添加新模块(CMakeLists.txt) 添加动态库依赖 cmake导入外部链接库 *.cmake文件 cmake生成编译DEB ...

  3. Cmake知识----编写CMakeLists.txt文件编译C/C++程序

    1.CMake编译原理 CMake是一种跨平台编译工具,比make更为高级,使用起来要方便得多.CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt ...

  4. linux cmake 编译项目,使用CMake构建复杂工程

    0. 什么是CMake CMake是一个跨平台的编译.安装.测试以及打包工具:CMake不直接编译软件,而是结合原生构建系统来构建软件.CMake配置文件是CMakeList.txt文件(每个源码文件 ...

  5. (CMake) 从下载到构建第一个CMake应用

    文章目录 前言 下载和配置 下载 配置 第一个demo 创建基本项目结构 基本code 生成项目 构建 常用指令 总Code 变量 函数 message() 输出信息 cmake_minimum_re ...

  6. 从零开始自制实现WebServer(十六)---- 学习新工具CMake自动编写MakeFile 分门别类整理源文件心情愉悦

    文章目录 全流程实现博客链接 前引 (十六)---- 学习新工具CMake自动编写MakeFile 小改小动项目接近尾声 1.学习新工具 cmake / shell脚本 需要耐心与时间 2.分门别类整 ...

  7. 一步一步SharePoint 2007之二十三:编写一个最简单的WebPart(1)——创建工程

    摘要 在前面的文章中,我们讲解了很多基础的内容,主要包括安装配置.Form认证等.可能这些对很多朋友来说,是太容易了.那么,从下一篇文章开始,就让我们进入SharePoint的高级课题之旅吧. 本篇文 ...

  8. Linux下CMake简明教程(三)同一目录下多个源文件

    如果在同一目录下有多个源文件,那么只要在add_executable里把所有源文件都添加进去就可以了.但是如果有一百个源文件,再这样做就有点坑了,无法体现cmake的优越性,cmake提供了一个命令可 ...

  9. 记录一个CMake编译报错undefined reference to vtable问题的解决

    在编写一个简单的CMake demo: 问题描述 文件结构如下:头文件和cpp分别放在两个文件夹下面 如果使用下面的写法,会报错"undefined reference to vtable ...

最新文章

  1. 使用NPOI时ICSharpCode.SharpZipLib版本冲突问题解决
  2. [导入]微软研究院Detour开发包之API拦截技术
  3. 【MM】更改供应商账户组
  4. 关于管理的十个经典故事
  5. 把wasm反编译出来
  6. Vmware+Virtualbox+Ubuntu+debian+USB转串口+kermit
  7. Spring源码之bean的初始化initializeBean方法解读
  8. WORD排版视频教程
  9. 英语四六级听力调频广播电台方案
  10. 塔防类游戏实现(一)
  11. 新思课堂C语言答案,新思课堂APP最新版下载_新思课堂APP官方版1.6.8下载_QQ下载站...
  12. 北大元培学院数学与计算机,通识教育试验的尴尬 北京大学元培学院近距离观察...
  13. 西南大学2019春计算机作业答案,2019年西南大学作业答案[1175]《仪器分析》
  14. python序列的应用
  15. Android 学习笔记(十二):安卓中的事件分发机制
  16. 学会这三招引流方法,让你的淘宝店铺流量暴增
  17. web前端基础——第八章
  18. Jupyter 是什么
  19. 聊一聊IT行业哪个专业工资高?
  20. Excel快速对比两列数据

热门文章

  1. 什么学习软件需要身份证验证_什么是两层身份验证,为什么我需要它?
  2. 粉丝时代,明星不需要公关?
  3. 三极管类型及工作状态判断
  4. [笔记]n个点的基环树数量
  5. python绘制幂函数曲线_基于matplotlib的yaxis力指数幂函数
  6. Uncaught SyntaxError: The requested module ‘/node_modules/.vite/vue.js?v=bd1817bb‘ does not provide
  7. java设计模式-观察者模式和中介者模式的异同
  8. Redis学习之incr命令
  9. python_练习2:输入a,b,c,d 4个整数,计算a+b-c*d的结果
  10. 旋转矩阵、欧拉角、旋转矢量及四元数的介绍和工程应用