CMake学习笔记

CMake是开源、跨平台的构建工具,主要讲述在Linux下如何使用CMake来编译程序。该博客根据多篇教程进行系统的汇总整理,方便日后工作查询。在ROS中构建功能包也需要用到CMake的相关知识。本人主要是在Linux平台使用cmake。
参考链接:
Linux下CMake简明教程
ROS学习之catkin CMakeList.txt

1.安装及版本查看

sudo apt install cmake

版本查询

cmake -version

2.简单demo示例

在这里插入图片描述
编写一个主程序main.cpp

#include <stdio.h>
int main(void){
	printf("Hello World\n");
	return 0;
}

然后在main.cpp相同目录下编写CMakeLists.txt,内容如下

cmake_minimum_required (VERSION 2.8) #cmake的最低版本要求
project (demo) #工程名叫demo
add_executable(main main.cc) #最终要生成的可执行文件的名字叫main,使用的源文件是main.cc

在该工程文件夹,和CMakeList.txt的这一级目录新建一个名为"build"的空文件夹,终端进入build文件夹,这里存放生成的可执行文件main.o

#在build文件夹路径下终端执行下列语句
cmake .. #会生成makefile定义编译规则等中间文件
make -j6 #利用make可以编译生成可执行文件,-j6调用6核进行编译,-j调用所有资源编译
./main  #运行可执行文件

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

3.重新生成make clean

#在build文件夹下
make clean #清除生成的可执行文件
make -j6

4.同一目录下多个源文件add_executable

在同一个目录下有多个源文件。
在第2节的目录下添加2个文件,testFunc.cc和testFunc.h。
假设要在main.cpp中用到testFunc的定义,需要在CMakeList.txt中将其写入进行编译:

cmake_minimum_required (VERSION 2.8)
project (demo)
add_executable(main main.cpp testFunc.cc)

就是在add_executable(),第一个main就是生成可执行文件的名称,后面的两个.cc就是需要编译的源文件

5.不同目录下多个源文件 aux_source_directory

当程序文件比较多时,我们会进行分类管理,把代码根据功能放在不同的目录下,这样方便查找。那么这种情况下如何编写CMakeLists.txt呢?
假设实现功能1的源文件存放在Func1文件夹中
假设实现功能2的源文件存放在Func2文件夹中
工程目录结构如下:
在这里插入图片描述
CMakeLists.txt内容修改成如下所示,

cmake_minimum_required (VERSION 2.8)
project (demo)
include_directories (Func1 Func2)  #代表头文件所在的路径
aux_source_directory (Func1 SRC_LIST1) #辅助源文件夹Func1别名为SRC_LIST1 aux辅助
aux_source_directory (Func2 SRC_LIST2) #辅助源文件夹Func2别名为SRC_LIST2
add_executable (main main.cpp ${SRC_LIST1} ${SRC_LIST2}) #生成可执行文件,编译辅助源文件目录SRC_LIST1,SRC_LIST2下面的文件夹

主要是靠aux_source_directory (Func1 SRC_LIST1)
增加辅助源文件夹

6.CMakeList.txt中的路径

cmakelist路径起点是cmakelist.txt所在的这一级目录,
./代表当前目录,也可以直接写文件夹名
…/代表上一级目录
举个例子:
…/CMAKE_DEMO/Func1/Func1.cc

在这里插入图片描述
在cmakelist.txt中表示Func1.cc
Func1/Func1.cc
然后对于源代码的include的头文件的路径该怎么写:
cmakelist里定义 include_directories (pathA pathB pathC)
那么你可以以pathA/B/C任意一个路径为起点写头文件所在的相对路径

7.标准的框架

正规一点来说,一般会把源文件放到src目录下,把头文件放入到include文件下,生成的对象文件放入到build目录下,辅助源文件分类放在辅助源文件夹
在这里插入图片描述
重写cmakelist.txt

cmake_minimum_required (VERSION 2.8)
project (demo)
include_directories (include) #指定头文件所在目录
aux_source_directory (Func1 SRC_LIST1) 
aux_source_directory (Func2 SRC_LIST2) 
add_executable (main src/main.cpp ${SRC_LIST1} ${SRC_LIST2})

这里主要是将头文件放在include,源文件放在src

8.指定生成可执行文件的存放位置 set()

假设现在另外新建一个文件夹"bin"用于存放可执行文件main.o

这里又出现一个新的命令set,是用于定义变量的,EXECUTABLE_OUT_PATH和PROJECT_SOURCE_DIR是CMake自带的预定义变量,其意义如下,
EXECUTABLE_OUTPUT_PATH :目标二进制可执行文件的存放位置
PROJECT_SOURCE_DIR:工程的根目录

cmake_minimum_required (VERSION 2.8)
project (demo)
include_directories (include) #指定头文件所在目录
aux_source_directory (Func1 SRC_LIST1) 
aux_source_directory (Func2 SRC_LIST2) 
add_executable (main src/main.cpp ${SRC_LIST1} ${SRC_LIST2})
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) #设定可执行文件输出目录是在工程根目录下的bin文件夹

9.cmake自带的预定义变量

这些预定义变量可以在CMakeList.txt里可以直接使用,加上 变 量 名 , {变量名}, 取出值就可以直接使用
PROJECT_NAME : 通过 project() 指定项目名称
PROJECT_SOURCE_DIR : 工程的根目录
PROJECT_BINARY_DIR : 执行 cmake 命令的目录
CMAKE_CURRENT_SOURCE_DIR : 当前 CMakeList.txt 文件所在的目录
CMAKE_CURRENT_BINARY_DIR : 编译目录,可使用 add subdirectory 来修改
EXECUTABLE_OUTPUT_PATH : 二进制可执行文件输出位置
LIBRARY_OUTPUT_PATH : 库文件输出位置
BUILD_SHARED_LIBS : 默认的库编译方式 ( shared 或 static ) ,默认为 static
CMAKE_C_FLAGS : 设置 C 编译选项
CMAKE_CXX_FLAGS : 设置 C++ 编译选项
CMAKE_CXX_FLAGS_DEBUG : 设置编译类型 Debug 时的编译选项
CMAKE_CXX_FLAGS_RELEASE : 设置编译类型 Release 时的编译选项
CMAKE_GENERATOR : 编译器名称
CMAKE_COMMAND : CMake 可执行文件本身的全路径
CMAKE_BUILD_TYPE : 工程编译生成的版本, Debug / Release

10.多库联编 add_subdirectory

在构建大型工程时,涉及到多个库联编,之前的章节都是只有demo这一个project,有时候会写一些通用库便于移植/单独测试/任务分解,然后多个库联编,就是会有多个cmakelist.txt,而且这些库/project之间可能互相依赖,例子如下:
假设现在这个demo project要依赖Func3这个project,demo的目录结构如下:
Func3这个被依赖库的源文件都存放在demo根目录下Func3/文件夹里,且其拥有自己的一个CMakeList.txt
在这里插入图片描述

cmake_minimum_required (VERSION 2.8)
project (demo)
include_directories (include)
aux_source_directory (Func1 SRC_LIST1) 
aux_source_directory (Func2 SRC_LIST2) 
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Func3) #增加依赖库的根目录
add_executable (main src/main.cpp ${SRC_LIST1} ${SRC_LIST2})
target_link_libraries(${PROJECT_NAME} Func3) #指定demo需要依赖Func3这个库
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 

主要是:
通过add_subdirectory,target_link_libraries配合使用可以实现多库联编

被依赖的Func3 project,相对可以独立于demo这个project运行,甚至可以单独编译
在这里插入图片描述

11.动态库和静态库的编译控制 add_library

先解释下静态库,动态库
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

有时只需要编译出动态库和静态库,然后等着让其它程序去使用。让我们看下这种情况该如何使用cmake。首先按照如下重新组织文件,只留下testFunc.h和testFunc.c
在这里插入图片描述
在build目录下运行cmake …,并把生成的库文件存放到lib目录下。
CMakeLists.txt内容如下:

cmake_minimum_required (VERSION 3.5) #cmake最低要求版本

project (demo)  #工程名

#设定指定路径文件 工程根目录/testFunc/testFunc.c
#设定将该路径的文件为别名 SRC_LIST,意为源码列表
#这里可以换行添加多个.cc .cpp程序
set (SRC_LIST ${PROJECT_SOURCE_DIR}/testFunc/testFunc.c) 

#add_library编译生成静/动态库,第1个参数指定库名称
#第2个参数指定生成的是静态还是动态链接库
#第3个参数指定生成库的源文件
#可以把一堆功能相似的源代码打包成一个动态或静态链接库
add_library (testFunc_shared SHARED ${SRC_LIST})
add_library (testFunc_static STATIC ${SRC_LIST})

#set_target_properties: 设置目标属性,设置最终生成的库文件的名称,会自动补上前后缀lib .a .so等
set_target_properties (testFunc_shared PROPERTIES OUTPUT_NAME "testFunc")
set_target_properties (testFunc_static PROPERTIES OUTPUT_NAME "testFunc")

#set 设定预定义变量的值
#预定义变量LIBRARY_OUTPUT_PATH,库文件的默认输出路径,这里设置为工程目录下的lib目录
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

进入build目录下运行cmake …,成功后再运行make
在这里插入图片描述
cd到lib目录下进行查看,发现已经成功生成了动态库.so和静态库.a
这一块功能主要是靠add_library ,set_target_properties 实现

12.对库进行链接 target_link_libraries

链接生成的库。重新建一个工程目录,然后在在工程目录下新建src目录,在src目录下添加一个main.cc,整体结构如下
在这里插入图片描述
仍然是用testFunc.cc生成一个testFunc静态库,然后让main链接这个静态库
代码如下:

//testFunc.h
#include <iostream>
void testFunc();
//testFunc.cc
#include "testFunc/testFunc.h"
void testFunc(){
std::cout<<"From testFunc lib!"<<std::endl;
}
//main.cc
#include "testFunc/testFunc.h"
int main(void)
{
testFunc();
return 0;
}
#CMakeList.txt
#cmake要求的最低版本
cmake_minimum_required (VERSION 3.5)

#工程名
project (demo)

#包含目录就是工程的根目录
include_directories(${PROJECT_SOURCE_DIR})

#打包源代码 工程根目录/src/main.cc 为 SRC_LIST
set (SRC_LIST ${PROJECT_SOURCE_DIR}/src/main.cc)
#打包源代码 工程根目录/testFunc/testFunc.cc 为 SRC_LIST1
set (SRC_LIST1 ${PROJECT_SOURCE_DIR}/testFunc/testFunc.cc)

#生成静态库, 库名命名为testFunc,库源代码为SRC_LIST1里的代码
add_library (testFunc STATIC ${SRC_LIST1})

#生成可执行文件 main.o  可执行文件源代码为SRC_LIST里的代码
add_executable (main ${SRC_LIST})

#main.o这个可执行文件链接的库 testFunc
target_link_libraries (main testFunc)

要链接的库本身就是这个CmakeList生成的,直接target_link_libraries
在Build文件夹下执行
cmake …
make
输出的可执行文件和静态库都默认存放在build文件夹下
在这里插入图片描述
代码运行结果
在这里插入图片描述

13.路径下遍历全局表达式匹配 file

例如在CMakeList.txt中见到如下语句:

#作用就是将src路径下的所有.cc文件,打包取个别名叫做变量SRC_FILES
#GLOB :产生一张与全局表达式匹配的所有文件的表 并存入指定变量SRC_FILES中
file(GLOB SRC_FILES src/*.cc)
#取出变量SRC_FILES的值,也就是打包在一起的所有.cc文件
#将这些打包在一起的.cc文件生成STATIC静态库,静态库的名称就是工程名
add_library(${PROJECT_NAME} STATIC ${SRC_FILES})

上面的*.cc也可以换成*.txt,*.proto等等
总之就是可以将一包文件打包在一起
GLOB_RECURSE :与 GLOB 类似, 但会遍历匹配路径下面的所有子路径并匹配文件

file(GLOB_RECURSE SRC_FILES src/*.cc)

14.引入外部依赖包 find_package

如果CMake通过 find_package()查找到一个软件包,它就会创建几个CMake环境变量,以提供有关已查找到的软件包的信息。这些环境变量可以在后面的CMake脚本中使用,它们表示软件包导出的头文件所在的位置、源文件所在的位置、软件包依赖的库以及这些库的查找路径,环境变量的名字遵循 packagename_property,即包名-属性:
PACKAGENAME_FOUND:当库被查找到时置为true,否则为false
PACKAGENAME_INCLUDE_DIRS或PACKAGENAME_INCLUDES:软件包导出的头文件路径PACKAGENAME_LIBRARIES或_LIBS:软件包导出的库的路径

假设此时我们需要引入google glog库来进行日志的记录,我们需要自行安装glog库,再进行引用。
CMakeList.txt写法

find_package(GLOG)
add_executable(glogtest glogtest.cc)
if(GLOG_FOUND)
    message(STATUS ”GLOG library found”)
else()
    message(FATAL_ERROR ”GLOG library not found”)
endif()
#找到glog库后就可以链接了
target_link_libraries(${PROJECT_NAME} glog)
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wujiangzhu_xjtu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值