CMake 基本使用方法

2023-05-16


1. 学习背景

C语言工程使用make来构建工程,但是对于大型工程来说文件的依赖关系很复杂,手写makefile非常麻烦,一般开源代码的构建方式都是使用autotool来配置编译环境和自动生成makefile,但是autotool配置涉及到的文件很多,操作步骤比较繁琐,产生了一些替代的方案,cmake是其中最优秀的之一。

cmake相较于autotool简化了很多步骤,只需编写CMakeLists.txt文件,然后运行cmake命令即可。

其实搭建一个非跨平台的工程,我更喜欢eclipse自带的自动构建方式,不用编写什么配置文件,只要在设置里添加头文件路径和库路径,添加文件直接把源文件拷到工程目录下就可以了,因为eclipse里工程目录和文件目录是保持同步的。但是涉及到跨平台的工程,所用到源代码是不同的,对应gcc有不同的编译选项,这时eclipse的灵活性就不够了,需要重新搭建工程。而用自动构建工具的配置文件和脚本结合在一起可以灵活的把不同平台下的编译环境组织到一个工程里面,在makefile里根据需要的平台提供了自定义的多种目标构建方式。

开源代码的编译环境都已经构建好了,不再需要我们重新编写配置文件,运行一条./configure或cmake命令即可,这时会生成一个makefile文件,然后就可以在eclipse指定外部的makefile进行编译了。

虽然开源代码不再需要我们自己写配置文件,但是会看懂配置文件还是十分有必要的,而makefile也需要懂一点,如果一点都不懂的话想添加个源文件或库都不知道从何下手。

以前的老项目都是使用autotool,而cmake出来的晚,使用cmake的项目没有autotool多,但是越来越多的大型项目开始使用cmake构建工程,这应该是一个趋势。

2. 使用方法

2.1工程目录

首先要安装cmake,在ubuntu下一句话搞定:

apt-get install cmake

接下来就以一个实际的小工程为例,来讲解怎么一步步编写CMakeLists.txt文件,工程的目录如下:

bplustree:.
│  CMakeLists.txt
│  coverage_build.sh
│  demo_build.sh
│  LICENSE
│  package.json
│  README.md
│
├─cmake
│      CodeCoverage.cmake
│
├─lib
│      bplustree.c
│      bplustree.h
│      CMakeLists.txt
│
└─tests
        bplustree_coverage.c
        bplustree_demo.c
        CMakeLists.txt
        testcase_generator.py

bplustree是一个主目录,在子目录lib和tests里含有源文件,我们需要在这3个目录下分别编写CMakeLists.txt文件。

下面先来看主目录的CMakeLists.txt文件,开头用project来定义项目名称,一般和主目录名保持一致。

cmake_minimum_required (VERSION 2.6)
#定义项目名称
project (bplustree)
#设置debug模式,如果没有这一行将不能调试设断电
set(CMAKE_BUILD_TYPE "Debug")
#设置debug模式下的编译参数
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
#配置自定义模块路径,这里是bplustree /cmake 
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
#配置库名
set(LIB_BPLUSTREE_NAME bplustree)
#进入子目录下执行 CMakeLists.txt文件
add_subdirectory(lib)
add_subdirectory(tests)

下面来分析一下PROJECT_SOURCE_DIR和PROJECT_BINARY_DIR的区别,如果在bplustree /build目录下执行cmake ..命令,那么PROJECT_BINARY_DIR指的的是bplustree /build目录,即执行cmake的当前目录,而PROJECT_SOURCE_DIR指的的是cmake执行的目录,这里是上一级目录bplustree。

2.2构建动态库和静态库

在lib目录下,通过编译bplustree.c文件生成动态库和静态库,cmake代码如下:

# LIB_BPLUSTREE_SRC是当前目录下的源代码文件集合
set(LIB_BPLUSTREE_SRC bplustree.c)
#这是最后库文件的输出目录,这里是bplustree /build/lib
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
#添加编译选项的宏定义_BPLUS_TREE_DEBUG
add_definitions(-D_BPLUS_TREE_DEBUG)
#生成一个名为bplustree的动态库,即libbplustree.so
add_library(${LIB_BPLUSTREE_NAME} SHARED ${LIB_BPLUSTREE_SRC})
#构建一个新的target,cmake会删除其他同名的库
#因此如果构建libbplustree.a,则会删除libbplustree.so
# CLEAN_DIRECT_OUTPUT 1避免了出现这个问题
set_target_properties(${LIB_BPLUSTREE_NAME} PROPERTIES CLEAN_DIRECT_OUTPUT 1)
#构建动态库版本号和api版本号libbplustree.so1.0和libbplustree.so1
set_target_properties(${LIB_BPLUSTREE_NAME} PROPERTIES VERSION 1.0 SOVERSION 1)
#把动态库安装到目录bplustree /build/lib
install(TARGETS ${LIB_BPLUSTREE_NAME} LIBRARY DESTINATION ${LIBRARY_OUTPUT_PATH})
#静态库和动态库的名字不能相同,静态库需要在末尾加上_static
add_library(${LIB_BPLUSTREE_NAME}_static STATIC ${LIB_BPLUSTREE_SRC})
#让静态库输出以libbplustree.a显示而不是libbplustree_static.a
set_target_properties(${LIB_BPLUSTREE_NAME}_static PROPERTIES OUTPUT_NAME "${LIB_BPLUSTREE_NAME}")
set_target_properties(${LIB_BPLUSTREE_NAME}_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
#把静态库输出到bplustree /build/lib
install(TARGETS ${LIB_BPLUSTREE_NAME}_static ARCHIVE DESTINATION ${LIBRARY_OUTPUT_PATH})

2.3构建目标

最后执行bplustree /test目录下的CMakeLists.txt来构建最终的目标,构建的目标有2种,分别是bplustree_coverage和bplustree_demo,由CMAKE_BUILD_TYPE来决定具体使用哪一种。

#设置构建目标的名字
set(TEST_NAME ${PROJECT_NAME}_test)
set(COVR_NAME ${PROJECT_NAME}_coverage)
set(DEMO_NAME ${PROJECT_NAME}_demo)
#设置目标输出目录,在bplustree /build/bin
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
#设置头文件所在目录bplustree / lib
set(INC_DIR ${PROJECT_SOURCE_DIR}/lib)
include_directories(${INC_DIR})
# 构建coverage目标
if(${CMAKE_BUILD_TYPE} MATCHES "Coverage")
        set(LIB_DIR ../lib)
        #设置构建目标所需的源文件
        set(SRC_LIST ${LIB_DIR}/bplustree.c bplustree_coverage.c)
        #构建可执行文件bplustree_coverage        
        add_executable(${COVR_NAME} ${SRC_LIST})
        #设置gcc的编译选项
        set(CMAKE_C_FLAGS "-O2 -Wall --coverage")
#使用自定义模块CodeCoverage.cmake   
        include(CodeCoverage)
#执行自定义模块里的函数,生成新的目标coverage
#第一个参数是新的目标名,最后编译时要生成coverage目标,
#使用make coverage,无参数的make命令不会生成该目标
#第2个参数是上面生成的可执行文件
#第3个参数是新目标coverage的输出文件
        setup_target_for_coverage(coverage ${COVR_NAME} coverage)
else()
        set(SRC_LIST bplustree_demo.c)
#生成bplustree_demo可执行文件
        add_executable(${DEMO_NAME} ${SRC_LIST})
        set(CMAKE_C_FLAGS "-O2 -Wall -Werror -Wextra")
#将库文件连接到目标,默认的是动态库优先
#如果要链接静态库需要指明实际的文件
# target_link_libraries(${DEMO_NAME} libbplustree.a)
        target_link_libraries(${DEMO_NAME} ${LIB_BPLUSTREE_NAME})
endif()

在makefile中的目标(target),相当于一个个段落标签,从当标签开始执行,到下一个标签结束,如

all:
  ……
coverager:
  …….
clean:
  ……

上面的例子中,all、coverager和clean就是一个目标,默认make执行的是make all,需要执行其他目标时需要在make后声明参数,如make coverager、make clean,此时将不会执行目标all里的语句。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CMake 基本使用方法 的相关文章

  • 堆和栈访问效率哪个更高

    1 栈分配的软件优势 xff1a 栈分配算法简单 xff0c 所以高效 xff1b 堆分配算法相对比较复杂 栈分配的硬件优势 xff1a 主要两点 xff0c cache和内存映射 如果在 栈上分配小块内存 xff0c 因为cache和内存
  • C++ Primer学习-第15章 面向对象编程

    15 1 面向对象编程 xff1a 概述 在C 43 43 中 xff0c 基类必须指出希望派生类重新定义那些函数 xff0c 定义为virtual的函数是基类期待派生类重新定义的 xff0c 基类希望派生类继承的函数不能定义为学虚函数 1
  • Ubuntu18.04创建工作空间和功能包

    由于本人记性实在不好 xff0c 每次新建工作空间和功能包的时候都要重新百度 xff0c 因此就以这种方式记下来 xff0c 有错误的地方还请批评指正 1 创建工作空间catkin ws mkdir p catkin ws src 2 初始
  • 关于安装双系统Linux不能完整分区解决方法

    在我安装双系统win7 43 ubuntu 16 x x版本时 xff0c ubuntu 在安装过程中会检测到win7 系统磁盘使用情况 将ubuntu 安装在空闲硬盘位置时 xff0c 系统设置不能有4 个主分区 xff0c 所以在分区过
  • 彻底删除VM虚拟机手把手详细教学

    一 在彻底删除VMware之前我们应在服务中把VM的任务和进程全部中止 1 我们首先按windows键 输入 服务 我们打开服务 2 在服务中我们找到vm开头的服务 并右键停止这些服务 3 按ctrl 43 alt 43 del选择任务管理
  • Leetcode 常用函数总结(C语言版本)

    include lt stdio h gt include lt stdbool h gt include lt math h gt include lt ctype h gt include lt string h gt include
  • C++ map表的应用

    map表可以存储数据对应关系 include lt map gt include lt string gt include lt iostream gt using namespace std int main map lt int str
  • C++判断是否是IP地址

    判断是否是IP地址 bool isIPAddress const char s const char pChar bool rv 61 true int tmp1 tmp2 tmp3 tmp4 i while 1 i 61 sscanf s
  • ARDUSUB 浏览

    ArduSub is an advanced open source ROV AUV control system Overview ArduSub水下机器人的控制器是一个完整的开源解决方案 提供远程操作控制 通过智能潜水模式 和全自动的执
  • C++判断是否是纯数字

    C 43 43 判断是否是纯数字 bool isDigitStr const char cstr if NULL 61 61 cstr cstr 0 61 61 0 return false int len 61 strlen cstr i
  • 命里有时终须有,命里无时莫强求

    命里有时终须有 xff0c 命里无时莫强求 今天是2012年2月24号 xff0c 和我谈了3个多月的女生突然之间说我们之间不合适 xff0c 让我以后不要再去骚扰她 真心第一次体会到失恋的感觉 xff0c 同时打电话给我姐姐诉说了下 xf
  • 共享内存--函数

    共享内存允许两个不相关的进程访问同一个逻辑内存 共享内存是在两个正在运行的进程之间传递数据的一种非常有效的方式 大多数的共享内存的具体实现 xff0c 都把由不同进程之间共享的内存安排为同一段物理内存 共享内存是由IPC为进程创建的一个特殊
  • assert用法

    判断是否为真 include 34 stdio h 34 include lt string h gt include lt stdlib h gt define NDEBUG include lt assert h gt void mai
  • strcpy原型

    已知strcpy 函数的原型是 char strcpy char strDest const char strSrc 其中strDest 是目的字符串 xff0c strSrc 是源字符串 xff08 1 xff09 不调用C 43 43
  • new与delete正确用法

    说明 xff1a 推荐使用如下宏 xff0c 可以在一定程度上避免使用空指针 xff0c 野指针的问题 define HW NEW var classname do try var 61 new classname catch var 61
  • Ubuntu分区和文件系统选择

    转载 xff0c 记录下 xff1a Ubuntu 分区和文件系统的选择 smwikipedia 博客园 对新手来说转移到 Ubuntu 第一件头痛的事情就是分区 xff0c 因为它和 Windows 上的分区方式有很大的不同 xff0c
  • [JavaScript]通过JS使用MD5加密

    JavaScript 通过JS使用MD5加密 目录 JavaScript 通过JS使用MD5加密项目信息如何使用额外信息 项目信息 github地址 https github com blueimp JavaScript MD5 作者dem
  • ThinkPad 插电时电源指示灯不亮

    今天来公司准备开电脑 xff08 ThinkPad T480 xff09 xff0c 开不开 电源键按了灯不亮 以为是插座没电 xff0c 但是发现手机充电器能给手机充上电 以为是电源适配器坏了 xff0c 因为是type c口的 xff0
  • [MongoDB] 启动报错ExecStart=/usr/bin/mongod --config /etc/mongod.conf (code=exited, status=14)

    原因是文件读取权限不足 xff0c 执行以下操作 span class token function sudo span span class token function chown span R mongod mongod var li
  • [Linux] Ubuntu如何查看磁盘剩余空间及内存

    如果有多台服务器 xff0c 这时需要安装一些新应用 xff0c 那么装在哪台设备上更合适呢 xff1f 这是我的第一个想法是看一看这些服务器的剩余空间和剩余内存 那么怎么才能看硬盘空间及内存呢 xff1f 查看剩余内存 span clas

随机推荐

  • [Linux] docker-compose环境下mongoDB4.4.2副本集搭建

    文章目录 实验目标实验环境目录结构操作步骤模拟宕机补充20201220补充2020122019参考 实验目标 两台服务器 主要使用服务器A进行读写 xff0c 每天需要定时重启 服务器B只进行备份操作 xff0c 不需要每天重启 当服务器A
  • [Alpine] 安装Alpine虚拟机,并安装docker,docker-compose

    下载alpine镜像 https www alpinelinux org downloads 开机后执行 setup alpine 一路回车直到需要输入root新密码 xff0c 可以先设置123456 以后再改 选择时区 xff0c 可以
  • 需求和预研

    需求 一定是有确定时间 xff0c 有衡量指标的任务 xff0c 这样才能可控 xff0c 才有意义 否则的话 xff0c 一个版本由若干个需求 xff0c 最后都没完成 xff0c 因为在做之前是未知数 xff0c 这个版本是一个有问题的
  • 设计/编码时,异常场景要处理到什么程度?容错到什么地步?

    设计时要清晰责任边界 xff0c 哪些异常需要吞掉自己处理 xff0c 哪些就暴露出去让其他服务 手段处理 这样目标清晰 xff0c 分工明确 xff0c 能够提高效率 xff0c 降低维护成本 xff0c 甩锅有理有据
  • 如何保质保量的完成需求

    如何保证数量 一个版本中 控制住每个需求的时间 即可保证数量 如何保证时间 避免返工 对齐 需求 设计方案 测试方案 代码质量 有问题一次解决 不修改引入新问题 设置路标 Roadmap 路标有时间节点 以及标明会完成哪些可度量的工作 每个
  • [Java] Request processing failed; nested exception is org.apache.ibatis.binding.Bin

    Request processing failed nested exception is org apache ibatis binding Bin springMVC xff0b mybatis 遇到的问题 xff0c 在XXXmapp
  • [Linux] CentOS7安装Chrome后yum不可以使用的解决方法

    症状 xff1a http dl google com linux chrome rpm stable x86 64 repodata repomd xml Errno 14 curl 52 Empty reply from server
  • [Java] 报错 java.lang.IllegalArgumentException: host parameter is null

    因为URL中存在中文字符 xff0c 需要编码 如 http baidu com 文件1 String encodeUrl 61 java span class hljs preprocessor net span span class h
  • Ubuntu 录屏

    GNOME录屏软件 Ubuntu 附带的屏幕录像机 xff0c 当您点击 Ctrl 43 Shift 43 Alt 43 R 时触发 它会自动开始录制整个屏幕 当您再次点击同一热键时 xff0c 截屏将结束并保存 点击录制热键时 xff0c
  • 【Odroid-XU4开发板】【安装Ubuntu18.04】【安装ROS】并读取串口数据的踩坑日记

    Odroid XU4开发板安装Ubuntu18 04安装ROS并读取串口数据的踩坑日记 文章目录 Odroid XU4开发板安装Ubuntu18 04安装ROS并读取串口数据的踩坑日记前言一 系统镜像准备二 烧录软件准备3 装机4 安装ro
  • 消除VS中C6054:可能没有为字符串"XXX"添加字符串零终止符的警告

    常见的是用 strlen 函数统计字符串的长度的时候 下面经常出现波浪线 xff08 即报警告C6054 xff09 xff0c 如下图所示 xff1a 怎么解决去掉这个警告呢 xff1f 64 wowpH 在这之前需要了解一下 strle
  • docker pull下载的image存在什么地方去了

    结论 xff1a 所有放入镜像文件都放在虚拟硬盘文件里面 windows上安装的docker其实本质上还是借助与windows平台的hyper v技术来创建一个linux虚拟机 xff0c 你执行的所有命令其实都是在这个虚拟机里执行的 xf
  • Eclipse CDT初步使用教程

    我用过Source insight和VS xff0c 感觉不顺手就没用了 xff0c 所以也没有深入研究过 xff0c 其他的如Code Blocks xff0c vim xff0c Clion Emacs我也没用过 xff0c 不好做评价
  • eclispe设置断点无效(No source file named)

    最近不知道做了什么操作导致eclipse的断点无效 xff0c 表现为gdb只认相对路径的断点 xff0c 不认绝对路径 xff0c 而eclipse打断点使用的是绝对路径 xff0c 导致无效 xff0c 输出信息是 xff1a No s
  • lemon源码基本概念整理

    1 数据结构 1 1 字符串存储 定义一个x1a的全局变量 xff0c 存放 y文件经过词法分析器分割出来的字符串 span class token keyword struct span s x1 span class token pun
  • lemon源码分析

    基本概念见上篇 lemon源码基本概念整理 1 follow集 对于如下4条产生式 program 61 expr TK SEM expr 61 expr TK IMPL expr expr 61 TK LPAREN expr TK RPA
  • Tcl脚本初步学习

    1 命令 Tcl 是一门基于命令的脚本语言 xff0c 每个命令通过换行符或分好隔开 每条命令都包含一个或多个单词 xff0c 第一个单词是命令名 xff0c 其他单词是命令的参数 xff0c 如 xff1a 命令 命令名 参数 set a
  • SQLite3源码学习(31) WAL日志的锁机制

    1 锁的原理 先来回顾一下回滚日志的文件锁 xff0c 之前的锁是针对数据库文件加锁的 xff0c 有4种类型 xff0c 分别是shared reserverd pending和exclusive 在WAL日志模式下不再使用原来的锁 xf
  • QGC调试px4固件飞控

    文章目录 前言一 开源软件地址二 硬件接线三 无人机调试1 刷固件2 选机架3 校准传感器4 校准遥控器5 飞行模式设置6 电源设置7 查看电机8 试飞 总结 前言 开源无人机调试 xff1a 硬件 xff1a pixhawk 2 4 8
  • CMake 基本使用方法

    1 学习背景 C语言工程使用make来构建工程 xff0c 但是对于大型工程来说文件的依赖关系很复杂 xff0c 手写makefile非常麻烦 xff0c 一般开源代码的构建方式都是使用autotool来配置编译环境和自动生成makefil