CMakeLists 写法总结

2023-05-16

0.前言

之前简单介绍了makefile的写法,但实际工程中基本不会手写makefile,通常情况是会写一个CMakeLists甚至是多层多个CMakeLists来构建整个工程。

关于makefile和CMakeLists的关系:make命令实际上是按照makefile中的内容来执行的,CMakeLists就是用来生成makefile的,是cmake命令按照CMakeLists中的内容生成makefile文件。所以一般来说,复杂工程的编译过程是:

mkdir build && cd build (创建并进入build 文件夹)

cmake .. (cmake命令执行build文件夹之外的CMakeLists中的内容)

make (make命令执行在build文件夹中生成的makefile文件)

对于复杂的工程来说,手写makefile是很复杂的,而手写CMakeLists会简单很多。对于写程序而言,掌握CMakeLists的写法更加实用。

下面会进行4个实例的解释:

1. 最简单的工程,只有build文件夹和一个main.cc文件和一个CMakeLists.txt

2. 简单工程文件,包含3个文件夹build, include, src及一个CMakeLists.txt。

3. 多层CMakeLists.txt,可执行源程序入口在最外层,不断调用里层的库。

4. 多层CMakeLists.txt, 可执行源程序入口在最里层,适用于源码相同,编译环境不同的情况,如交叉编译等。

1. 最简单的工程,只有build文件夹和一个main.cc文件和一个CMakeLists.txt。

最简单的工程文件格式如图所示,其中build 文件夹是用来存放编译的过程文件和生成的可执行程序的结果文件的。这个文件夹通常自己创建,也可以删除。

 代码示例如下:

这里最简单的CMakeLists的写法,包含三个语句。

cmake_minimum_require:表示指定cmake的最小版本,因为有些cmake命令可能高版本才有

project(project name): 用来指定工程的名称

add_executable(execfile, main.cc):注意这句非常关键,是指定生成的可执行文件的名称,以及生成该可执行文件需要用到的源文件,在当前简单的示例中,只有一个源文件。

 题外话:对于build文件夹的理解和CMakeLists也有很大的关系。上面已经说明build文件夹中的内容其实由cmake命令按照我们写的CMakeLists的内容生成的,其中生成的内容就有makefilemakefile我们已经知道主要是各个文件之间的依赖关系。所以,如果makefile中的内容不需要修改,那make命令是可以用新生成的文件覆盖原有生成的内容的,比如仅仅修改了一些源代码的逻辑,那么就可以直接重新make。而如果明确一些中间过程的.o文件不再需要,则需要使用make clean这里需要注意的是make clean也是按照makefile中的内容去clean的,所以前提是makefile不需要修改而如果makefile变化,即文件之间的依赖关系发生改变,简单说我们修改了CMakeLists的内容,那么需要删除整个build文件夹,然后重新新建build文件夹进行编译

2.简单工程文件,包含3个文件夹build, include, src及一个CMakeLists.txt

下面我们增加一些文件,使得整个工程变得复杂一些,同时新增一些CMakeLists.txt当中常用的cmake命令。首先看一下工程目录,可以看到main.cc被放到了src文件夹下,并且与function.cc 文件发生了依赖关系。这个工程框架是更一般的框架,即包括一个src文件夹存放源文件,一个include文件夹存放头文件,build文件夹存放编译过程文件和可执行文件,一个CMakeLists.txt。

相比与第一种工程情况,CMakeLists.txt的写法增加了以下内容

aux_source_directory(),因为编译涉及的源文件不再只有main.cc,本例中虽然只有两个源文件,但今后的工程中可能涉及到的源文件非常的多,所以为了避免add_executable中的第二个参数写的过多和繁琐,用aux_source_directory将涉及到的源文件的文件夹用变量管理起来,这样在add_executable中就可以直接解读变量了。
 

include_directories(),这个语句也非常有用,主要是用来添加第三方依赖的头文件,相当于g++中的-l,也相当于在环境变量中增加路径。一般的我们都会在用到某个头文件的时候在代码中进行include的引用,实际工程中将这些头文件的位置进行归置,可以减少代码中include的复杂程度。具体如以下代码中,将include文件夹的路径添加之后,代码中可以直接include “function.h” 而不用写成 include “../include/function.h”,这在涉及到使用很多第三方库的时候,可以保证条理清晰,不混乱。

Set(), set通常用来给一些变量赋值,同时有类似作用的还有option()。个人的使用习惯时set从来给一些默认的变量赋值,如当前CMakeLists.txt中写到的CMAKE_CXX_FLAGS,用来指定编译器,CMAKE_BUILD_TYPE用来指定编译类型等。

Option(),用法同set类似,但个人习惯用它给自定义的变量赋值,用法如当前CMakeLists.txt当中所写的那样。

3. 多层CMakeLists.txt,可执行源程序入口在最外层,不断调用里层的库。

在现实的工程中,我们往往会有比较复杂的文件结构,而子模块生成库供主程序调用就是很典型的用法,掌握这种方法,也就掌握了直接拿别人的库文件和头文件,调用头文件中的函数功能的情况。看例子中文件结构,在2的基础上增加了module文件夹,里面包含一个源文件和头文件,以及单独所用在submodule的CMakeLists.txt。在主函数中,引用subtest头文件,并调用subtest()函数。

|——— build
|——— include
|    |—— function.h
|——— src
|    |—— function.cc
|——— submodule
|    |—— submodule.cc
|    |—— submodule.h
|    |—— CMakeLists.txt
|——— test3.cc
|——— CMakeLists.txt

首先看subsubmodule中的CMakeLists,里面只有两个语句

# 将当前目录下的源文件添加到SUB_DIR_LIB_SRCS变量
aux_source_directory(. SUB_DIR_SRC_LIST)

# 生成静态库
add_library(submodule ${SUB_DIR_SRC_LIST})

aux_source_directory():和之前的说的一样,用变量表示所有需要编译的源文件

add_library():这体现出和主函数文件的不同,除了主函数文件,其他源文件编译是以生成库的方式被主函数调用的。这个语句就是将涉及的需要编译的源文件编译成一个指定名字的动态库或者静态库,供上一层CMakeLists中进行调用

然后再看上一层CMakeLists.txt

# cmake最小版本需求
cmake_minimum_required(VERSION 3.0.0)

# 设置此项目的名称
project(test3) 

# 设置工程名,通常在复杂的工程中会用到,会引用${PROJECT_NAME}
set(PROJECT_NAME test3)

# 指定编译器
# CMAKE_C_FLAGS_DEBUG          ----  C 编译器
# CMAKE_CXX_FLAGS_DEBUG        ----  C++ 编译器
# -std=c++11  使用 C++11
# -g:只是编译器,在编译的时候,产生调试信息。
# -wall:生成所有警告信息。以下是具体的选项,可以单独使用
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11   -g  -Wall")

# 指定编译类型,debug 或者为 release
# debug 会生成相关调试信息,可以使用 GDB 进行
# release 不会生成调试信息。当无法进行调试时查看此处是否设置为 debug.
set(CMAKE_BUILD_TYPE Debug)

# 打印消息
MESSAGE("test3") 

# 将包含源文件的文件夹放入参数中,并给一个命名
# 可以多次使用,包含多个存放源文件的文件夹
aux_source_directory(./src SRC_LIST)
aux_source_directory(. SRC_LIST)

# 这里直接将上面的命名放入第二个参数中即可
add_executable(${PROJECT_NAME} ${SRC_LIST})

include_directories(./include)

# 添加子目录,这样上一层CMakeLists才能找到并调用子目录的CMakeLists
add_subdirectory(submodule)

# 设置编译时依赖的submodule静态库
target_link_libraries(${PROJECT_NAME} submodule)


新增了两个语句

add_subdirectory(),这是将我们除了src文件夹之外的包含源文件的文件夹路径告诉编译器,让编译器能够找到子文件夹及其中的CMakeLists.txt

target_link_libraries(),给当前的主程序链接子文件夹中生成的依赖库,这样主程序中使用到相应函数时,才能够在依赖的库里找到。

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

CMakeLists 写法总结 的相关文章

随机推荐

  • 旋转矩阵、欧拉角

    旋转矩阵 欧拉角 注 xff1a 下面为学习空间机器人技术系列课程笔记 xff0c 加上一些自己的整理 xff0c 方便复习 一 旋转矩阵的引出 下面坐标系0的基向量为 x 0 xff0c
  • Makefile的入门完整教程(包学包会)

    Makefile的完整入门教程 xff08 实现不了来打我TAT xff09 看完能够了解Makefile是什么 xff1b 我们能用makefile做什么 xff1b makefile的简易使用 1 什么是Makefile Makefil
  • ubuntu18.04 升级内核后,进入系统页面卡在“started gnome display manager“的解决方案

    问题描述 安装了18 04后 xff0c 系统的内核是5 0的 xff0c 不支持电脑的wifi xff0c 所以就想升级一下 升级到5 4后在grub界面选择5 4的内核后 xff0c 进入系统 xff0c 界面一直卡在started g
  • Ubuntu 14.04下,分辨率只有800×600的解决方法

    对于Ubuntu 14 04 xff0c 在安装好后 xff0c 默认的分辨率是800 600 xff0c 对于有着高分辨率例如1920 1080的显示器 xff0c 或者想要拓展双屏 xff0c 本身默认显示器驱动无法实现 xff0c 因
  • ubuntu14.04下安装cmake 3.5

    对于ros的应用 xff0c 很多还局限在indigo下 xff0c 因此要求的Ubuntu版本还限制在14 04 但对于很多新的功能包 是在kinetic下运行的 xff0c 很多cmake要求在3 5以上 xff0c 而安装ros in
  • Ubuntu16.04下使用ros_qtc_plugin在qt下进行编译

    系统测试环境 系统版本 Ubuntu16 04ROS版本 kinetic 按照官网的教程https ros qtc plugin readthedocs io en latest source Improve ROS Qt Creator
  • Ubuntu16.04下openpose编译及测试demo

    一 安装 官方安装流程见openpose官方 在安装前 xff0c 尽量保证有很好的显卡以及内存 xff0c 不然在运行demo的过程中会出现out of memory的情况出现 流程如下 xff1a 1 下载 git clone http
  • ubuntu16.04 通过anaconda建立虚拟环境,安装tensorflow1.10,cuda9.0,cudnn7.1.2

    1 anaconda建立虚拟环境及conda操作 env name代表你想要建立的环境名字 n表示名字 conda create n env name python 61 3 5 激活环境 source activate env name
  • 运维人员核心职责

    运维小知识点 xff01 网站数据不能丢网站7 24小时运转提升用户体验 xff0c 访问速度要快 云计算 1 公有云 2 私有云 就是自己内部的运维工程师部署的一个云平台 xff0c 资源管理平台数据都放在自己手中 xff0c 不被别人看
  • Ubuntu16.04下向github传送或修改代码

    初次使用Git设置 这段针对的是初次使用Git的设置 xff0c 如果初次设置之后 xff0c 就直接跳到下一阶段 将本地仓库push至github远程仓库 1 首先要确定Ubuntu下是否有git 终端运行指令 sudo apt inst
  • 字符串尾部得加'\0'原因

    39 0 39 一般放在字符串的结束处 xff0c 表示字符串的结束 xff0c 其是ascii值为0的字符的转义 在头文件 include lt string h gt 中包含的一些字符串处理函数等中 xff0c 一般处理字符串时 xff
  • cmake-CMakeLists.txt中添加目标编译选项的方法

    CMakeLists txt中添加目标编译选项的方法 原因 xff1a 如果程序中用到了宏来区分不同的方法 xff0c 但是又不想每次在用到不同的方法的时候都要在程序中更改宏定义后再进行编译 xff0c 那么可以在CMakeLists tx
  • GDB多线程调试和死锁

    set schedular locking on off 条件断点查看循环中的某些变量 break if命令 示例 xff1a break test c 34 if x amp y 61 61 1 默认情况下我们执行到断点处继续执行时 xf
  • Gazebo仿真踩坑系列-乱飞、抖动等

    本文章记录机械臂 塔吊等仿真过程中遇到的各种问题 塔吊建模 前言 gazebo机械臂等控制仿真最重要的是建模 xff0c 也就是URDF的编写 xff0c 而这里面有着特别多要注意的地方 xff0c 否则会发生 启动乱飞 控制乱飞 和 启动
  • Qt6新创建CMake项目启动不了

    错误如下 xff1a 1 error CMake was unable to find a build program corresponding to 34 Ninja 34 CMAKE MAKE PROGRAM is not set Y
  • Linux/Centos 安装oracle报错“调用makefile ‘/oracle/product/11.2.0/dbhome_1/sysman/lib/ins_emagent.mk的目标” 解决

    解决centos7 redhat7安装oracle11g到 70报错问题 Linux Centos 安装oracle报错 调用makefile 39 oracle product 11 2 0 dbhome 1 sysman lib ins
  • C++使用技巧(四):单双冒号“:”和“::”用法

    C 43 43 单冒号与双冒号的作用 1 冒号 xff08 xff09 用法 xff08 1 xff09 表示结构体内位域的定义 xff08 即该变量占几个bit空间 xff09 span class token keyword typed
  • 交换机与路由器

    交换机 VS 路由器 交换机 xff1a 把数据包发送到正确的位置 xff0c 相当于邮递员 xff0c 根据数据包中的目标mac地址 xff0c 找到他对应的物理端口 xff0c 一台交换机有很多个端口 xff0c 它们都有自己的编号 x
  • 在C++中使用openmp进行多线程编程

    声明 xff1a 本文是基于Joel Yliluoma写的Guid into OpenMP Easy multithreading programming for C 43 43 而写的 xff0c 基本是按照自己的理解 xff0c 用自己
  • CMakeLists 写法总结

    0 前言 之前简单介绍了makefile的写法 xff0c 但实际工程中基本不会手写makefile xff0c 通常情况是会写一个CMakeLists甚至是多层多个CMakeLists来构建整个工程 关于makefile和CMakeLis