本文共 4640 字,大约阅读时间需要 15 分钟。
为啥要学习CMake?(不同环境下Makefile格式是不兼容的)这里我就不说啥是CMake了,因为我相信看到这篇文章的你肯定已经接触过CMake了。
最近开始搞SLAM,要运行视觉SLAM十四讲上的一些实践案例,里边涉及到CMake的相关知识。 本来觉得CMake大概会用就行,照着CSDN上一些教程运行了一些小例程,但是感觉还是走马观花,没搞清楚,决定自己写一个来记录如何使用CMake。 那就开始整活吧!在Ubuntu中,我们比较常见的编译器是gcc
和g++
,前者编译.c
文件,后者编译.cpp
文件
g++
编译命令为例,可以在网上查到它命令选项,例如该文章 里边可以查到很多有关编译命令的变化 每本书或教材应该都有相关使用的讲解,这里不再赘述最基本的编译命令的使用 几个基本步骤:预处理–编译–汇编–链接 下图展示了我的一些g++
编译命令实践,能看懂就没啥问题(里边有不小心打错的命令哦) 参照网上入门CMake的一些教程,我们也从最简单的一个.cpp
+一个CMakeLists.txt
开始
CMakeLists.txt
各行代码的含义 工程经过cmake和make后,长这个样子:
CMakeLists.txt
中代码及解释如下: #CMake需要的最低的版本,VERSION是固定的,后边的版本号可以变化#指定运行此配置文件所需的 CMake 的最低版本CMAKE_MINIMUM_REQUIRED(VERSION 3.10)#表示工程的名称是Demo1,貌似小写project也可以PROJECT(Demo1)#将源文件main.cpp添加到可执行文件demo1中ADD_EXECUTABLE(demo1 main.cpp)
有意思的是,参照网上一些博客的说法,CMake
的内置指令是不区分大小写的(比如PROJECT
?),而其内置参数是区分大小写的,写的时候要注意一下
demo1
的可执行文件,运行时比较有讲究,举个例子,这里需要用到main
函数的直接传入参数,具体内容可以参照该篇博文: 具体的输入指令及结果如下: 因为有了多个源文件,需要把多个源文件都放到可执行文件中,这是该部分的关键
先来看看此时编译后的文件结构,如下图所示:.cpp
,一个.hpp
,一个CMakeLists.txt
插一句,这个.hpp
文件貌似和.h
文件一个性质,都是头文件,另一个.cpp
则可以认为是库文件,这个在后边会再次说明 来看一下CMakeLists.txt
吧,内容及解释如下: CMAKE_MINIMUM_REQUIRED(VERSION 3.10)PROJECT(Demo2)#将源文件都放到变量DIR_SRCS里边#查找指定目录下的所有源文件,添加到指定变量中:aux_source_directory()#这里是指将当前目录下所有的源文件放到DIR_SRCS#./和.都可以表示当前目录,但二者略有区别,现在还没搞懂AUX_SOURCE_DIRECTORY(./ DIR_SRCS)#将包含源文件的变量添加到可执行文件中,注意使用语法ADD_EXECUTABLE(demo2 ${DIR_SRCS})
由此则可以进行编译运行了,由于仍为一个目录,所以操作方法及结果与Demo1几乎完全相同:
我们不能把所有的源文件,或者说库文件,以及头文件都放到一个目录下边,所以要进行分目录管理,这里首先讲一下将库文件放到新的目录下的方法
编译后的文件结构如下图所示:mylib
目录下放了库文件和头文件,根目录以及库目录各有一个CMakeLists.txt
,另外在main.cpp
文件下,包含了头文件所在路径,如下所示:
#include#include "./mylib/mymath.hpp"using namespace std;
貌似有的方法是不用把路径全部打进去,还不是很清楚这个怎么玩
看一下库目录下的CMakeLists.txt
,代码及解释如下: #将当前目录下的源文件名称放到变量DIR_LIB_SRCS中aux_source_directory(. DIR_LIB_SRCS)#将mylib下的源文件,即刚刚当前目录下的源文件,生成为静态链接库Mylib#此时只是生成了一个静态链接库,还需要在根目录对其进行链接操作#参数分别为[生成的库的名字] [STATIC/SHARED] [包含库的源文件]add_library(Mylib STATIC ${DIR_LIB_SRCS})
这里生成了一个静态库,要把它链接到根目录里,根目录下CMakeLists.txt
内容如下:
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)PROJECT(Demo3)#添加mylib子目录(新加的),直接用mylib的目录#add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])ADD_SUBDIRECTORY(./mylib)#查找当前目录下所有的源文件,并将名称保存到 DIR_SRCS变量AUX_SOURCE_DIRECTORY(./ DIR_SRCS)#指定生成目标,这里的DIR_SRCS其实只有demo.cppADD_EXECUTABLE(demo3 ${DIR_SRCS})#添加链接库,将子目录中生成的静态库链接到可执行文件TARGET_LINK_LIBRARIES(demo3 Mylib)
为了满足多目录的需要,这里引入了两条新的代码,一个是ADD_SUBDIRECTORY
,一个是TARGET_LINK_LIBRARIES
,我的理解是前者将包含库目录的子目录添加到编译里,后者将库链接进可执行文件,具体原理我还不是很清楚,有懂的小伙伴欢迎在评论区补充哈!
ADD_SUBDIRECTORY
的文章,用来解决子目录和根目录同级如何编译的问题,放到这里应该日后用得到: 执行指令是在根目录,生成的可执行文件也在根目录(后边会把它放到其他地方去),具体步骤及结果如下: 终于来到这里了,我们都想要搞一个正规的文件结构,并用CMake
对其进行编译
bin
、src
、lib
,当时不知道这些是什么含义,现在大概知道是咋回事了,如下表所示 文件名称 | 文件全名 | 文件含义 |
---|---|---|
bin | binary | 可执行(二进制)文件 |
build | build | 编译相关文件 |
include | include | 头文件 |
src | source | 源文件&库文件 |
看到这个我只想说,原来清晰的文件结构是这么漂亮呀!
src
各有一个CMakeLists.txt
,头文件放到了include
文件下,源文件放在了src
文件夹下,没有编译文件和可执行文件 先看下源文件src
里的CMakeLists.txt
: aux_source_directory (. SRC_LIST)#将工程用到的头文件目录添加进来#该命令是用来向工程添加多个指定头文件的搜索路径,路径之间用空格分隔。#命令将当前目录的上一级目录下的include目录添加进去,因为里边有用到的头文件include_directories (../include)add_executable (demo4 ${SRC_LIST})#set命令:将生成的指定文件放到指定目录下#这里设定把生成的可执行文件放到根目录下的bin目录下set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
需要说明一下的是,这里我们为工程添加了头文件的搜索路径,在main.c
就不用把头文件的完整路径放进去了,此时main.c
如下:
#include "testFunc.h"#include "testFunc1.h"
再把刚刚Demo3中的头文件写法放在下面做一下对比:
#include "./mylib/mymath.hpp"
我个人更加喜欢第一种写法,可能是看习惯了,但感觉第二种写法直接写出路径,是不是会更清晰呢?(⊙o⊙)
现在看看根目录下的CMakeLists.txt
: cmake_minimum_required (VERSION 2.8)project (Demo4)#将src目录作为次级目录,因为里边是我们的源文件add_subdirectory (./src)
这里没什么特别的,可以跟Demo3对比一下,因为两者的风格还是有一些不同的,可以对比学到很多有趣的东西
这时在build
目录下,进行cmake ..
,这样才能将编译用的文件放到build
目录下 CMakeFiles
会在build
目录下生成,然后在build
目录下运行make
: bin
文件夹下多出来了可执行文件demo4,在build
文件夹下多出了很多编译文件,但源文件和头文件没有被“污染”,结构仍然非常清晰舒服! 至此,希望你已经大概了解了CMake是个什么东西
不看别的也一定要仔细看CMakeLists.txt文件中的注释! 在本篇文章中,主要介绍了CMake最基本的操作 其中也穿插着一些小知识,包括希望你能在学习CMake知识的同时对这些也能有一些了解~
这就是传说中的递归学习法吗!?😣 哎呀,突然发现CSDN还可以打表情哈哈,表情都在这里哦:👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍
你都看到这了,难道不来个小小的点赞迈?!嘿嘿,与君共勉!转载地址:http://opewz.baihongyu.com/