必读干货|使用Cmake管理C++项目简明教程
liuian 2025-01-21 19:28 14 浏览
一、背景
Cmake是 kitware公司以及一些开源开发者在开发几个工具套件(VTK)的过程中衍生品,最终形成体系,成为一个独立的开源项目。其官方网站是 cmake.org,可以通过访问官方网站获得更多关于cmake的信息。
它是一个跨平台的编译(Build)工具,可以用简单的语句来描述所有平台的编译过程。CMake能够输出makefile文件,然后系统会根据这个makefile文件内容编译整个工程。
图1 kitware公司和Cmake套件
软件开发有以下三个步骤:
1.编译源代码;
2.链接到其他库;
3.将应用程序打包为库或二进制文件。
假如我们有一个大型的C++项目,由非常多的互相调用的工程共同组成,一些用于生成库文件,一些用于实现逻辑功能。他们之间的调用关系复杂而严格,如果想在这样复杂的框架下进行二次开发,显然只拥有它的源码是远远不够的,还需要清楚的明白这几十个项目之间的复杂关系。即使是原作者给出了相关的结构文档,对新手来说建立工程的过程依旧是漫长而艰辛的,开发人员的核心业务是软件开发,而不是软件构建。Cmake目的是实现软件构建流程的自动化,并且是跨平台的。
原作者只需要生成一份CMakeLists.txt文档,框架的使用者们只需要在下载源码的同时下载作者提供的CMakeLists.txt,就可以利用CMake,在原作者的帮助下进行工程的搭建。Cmake编写的过程实际上是编程的过程,不过你需要编写的是 CMakeLists.txt(一般情况下每个目录一个),使用的是“Cmake”语言和语法。
刚入门的开发者拿到Cmakelist看着一脸雾水,里面众多的语法指令不知道是什么含义甚至不能自己修改,本文接下来将介绍Cmakelist的各种重要的变量、指令、语句及其作用。
图2 Cmake的构建流程
二、安装
大多数Linux的操作系统会在系统中直接包含,不必手动安装。其他操作系统,比如:Windows或者某些Linux系统,可以从Cmake官网上下载安装包进行安装:cmake.org/HTML/Download。安装步骤省略,可以借鉴网上其他资料。
三、使用
因为cmake会生成一些中间文件(CMakeFiles,CMakeCache.txt, cmake_install.cmake, Makefile),所以最好在项目源代码目录重新建一个文件夹存放生成的中间文件,即外部编译。在中间文件夹中依次执行编译指令cmake ..,make,那么编译器会按照生成的Makefile中的指令编译C++工程。如果要对编译的结果清理,使用make clean 指令即可。在 Windows 上,还可以使用 CMakeSetup (GUI)进行工程构建。
make install命令将编译好的目标文件默认直接安装到/usr/local目录,如果想安装到其他目录,可以使用make install DESTDIR=< install_path >。
本文接下来介绍Cmakelist的编写方法,这是开发者最关心的地方。Cmakelist编写指令时不区分大小写,为了统一风格,本文使用大写字母。
四、基础指令
要编译非常简单的C++工程,工程不依赖第三方库,那么Cmakelist中至少包含以下内容:
#声明要求的最低Cmake版本
CMAKE_MINIMUM_REQUIRED(VERSIN 2.8)
#Cmake工程名。这个指令隐式的定义了两个 cmake变量: PROJECT_BINARY_DIR以及PROJECT_SOURCE_DIR。分别指向项目的编译路径和工程路径。
PROJECT (HELLO)
#生成可执行文件,${}来引用变量。
ADD_EXECUTABLE(Executable program name ${ Source files })
到这里,一个可执行程序就生成了。如果要管理依赖第三方库的工程,就需要加入一些额外指令。除此之外,还有一些Cmake中重要的变量和语句需要掌握,才能使用起来得心应手。在下面列出:
五、重要的变量
#Cmake版本
CMAKE_SYSTEM_VERSION
#可执行文件的输出路径
EXECUTABLE_OUTPUT_PATH
#库文件输出路径
LIBRARY_OUTPUT_PATH
#安装路径
CMAKE_INSTALL_PREFIX
Note:该变量可以在Cmakelist中显式地定义,即:SET(CMAKE_INSTALL_PREFIX < install_path >);也可以作为命令行参数传递:cmake -DCMAKE_INSTALL_PREFIX=< install_path > ;还可以在cmake完成后使用make install DESTDIR=< install_path >指定安装路径。
#工程编译时存放二进制文件的目录。
CMAKE_BINARY_DIR
#将添加的头文件搜索路径放在已有路径的前面。
CMAKE_INCLUDE_DIRECTORIES_BEFORE
#将添加的头文件搜索路径放在已有路径的后面。
CMAKE_INCLUDE_DIRECTORIES_ AFTER
#工程顶层目录
CMAKE_SOURCE_DIR
#定义自己的 cmake模块所在的路径
CMAKE_MODULE_PATH
#控制库的默认编译方式
BUILD_SHARED_LIBS
#设置 C++/C的编译选项,也可以通过指令 ADD_DEFINITIONS()添加
CMAKE_CXX_FLAGS/ CMAKE_CXX_FLAGS
Note:该指令非常重要,因为它能选择C++/C工程的编译器行为。有时候我们依赖的第三方库的编译方式与自己项目的编译方式不一致时,编译会通过,但是运行时出现非常隐蔽的bug。此时就需要检查,项目中各种库的编译行为是否一致。
# 设置构建类型,类型选项有Debug/Release。当使用GDB调试工程时要使用Debug选项。该变量可以通过命令行:cmake -DCMAKE_BUILD_TYPE=Release或指令SET(CMAKE_BUILD_TYPE [type])设置。
CMAKE_BUILD_TYPE
六、重要的指令和语句
#查看Cmake生成的详细编译信息,相当于执行make VERBOSE=1。当构建失败并且需要找出原因时使用该条指令非常有帮助。
SET(CMAKE_VERBOSE_MAKEFILE on )
#显式的为变量赋值,非常重要
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
#用来寻找第三方库的Find<name>.cmake模块,该模块里提供了库的头文件路径和库文件路径。如果找不到,通过设置 CMAKE_MODULE_PATH 变量来告诉 Cmake 在哪里找到它。
FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE] [[REQUIRED|COMPONENTS] [componets...]])
Note:编译一个链接第三方库的软件时,我们需要知道该库的头文件路径、库文件路径、要链接的库的文件名。使用第三方库时必须在Cmakelist中提供这些信息。
#向终端输出用户定义的信息,显示状态信息、警告或错误。包含了三种类型:SEND_ERRO、SATUS、FATAL_ERROR。
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display"...)
例:MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})
#添加可执行文件
ADD_EXECUTABLE(<name> IMPORTED [GLOBAL])
#向当前工程添加存放源文件的子目录,并指定中间二进制和目标二进制存放的位置。
ADD_SUBDIRECTORY(source_dir [binary_dir] )
#生成库。生成的库的类型有三种:SHARED,动态库;STATIC,静态库;MODULE,在使用 dyld的系统有效。
ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
#设置输出目标的属性
SET_TARGET_PROPERTIES(target1 target2 ...PROPERTIES prop1 value1 prop2 value2 ...)
例:设置目标库的名称:SET_TARGET_PROPERTIES(taget_name PROPERTIES OUTPUT_NAME "hello")
#设置动态库的版本号,VERSION指代动态库版本,SOVERSION指代 API版本。
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
#向工程添加多个特定的头文件搜索路径。系统默认的头文件路径是/usr/include和/usr/local/include,如果不是,需要在Cmakelist中显式的定义。
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
#将目标文件链接到第三方库,非常重要的语句
TARGET_LINK_LIBRARIES(target library1<debug | optimized> library2...)
#在指定路径中搜索文件名,指定路径指的是环境变量CMAKE_INCLUDE_PATH中包含的所有路径。
FIND_PATH(filename)
#向 C/C++编译器添加-D定义。修改预处理器设置
ADD_DEFINITIONS()
#定义 target依赖的其他 target,确保在编译本 target之前,其他的 target已经被构建
ADD_DEPENDENCIES()
#搜索一个目录下所有的源代码文件并将列表存储在一个变量,可以与ADD_EXECUTABLE连用。
AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(main ${SRC_LIST})
#if语句, 如果表达式内容不是:空,0,N, NO, OFF, FALSE, NOTFOUND或<var>_NOTFOUND时,表达式为真。
IF(expression)
…..
ELSE(expression)
…..
ENDIF(expression)
#列表变量遍历语句
FOREACH(loop_var)
...
ENDFOREACH(loop_var)
#while语句
WHILE(condition)
...
ENDWHILE(condition)
本文提到的变量、语句、指令包含了Cmakelist编写时最常见、最重要的部分,编写Cmake的过程其实就是编程的过程。
相关推荐
- Flutter——输入部件
-
上一节介绍了文本部件,用于显示文本。但是在我们进行日常软件开发过程中,经常是需要获取用户键入的内容,比如用户名、密码、性别、喜好等等。Flutter提供了丰富的部件来处理用户输入,本节将主要介绍以下...
- flutter系列之:做一个图像滤镜
-
简介很多时候,我们需要一些特效功能,比如给图片做个滤镜什么的,如果是h5页面,那么我们可以很容易的通过css滤镜来实现这个功能。那么如果在flutter中,如果要实现这样的滤镜功能应该怎么处理呢?一起...
- 免费好用还高级!3个必须知道的设计网站,让你的PPT好看10倍
-
哈喽,大家早上好鸭~好久不见,我是三石,今天我又来进行PPT技巧分享啦~诶,这不前几天编辑部的萌萌就来找我约稿,说小叶子们对于神器网站的呼声很高,问我能不能写一篇文章汇总一下,我二话没说就答应了。常言...
- flutter软件开发笔记08-容器使用方法
-
在Flutter3中,容器组件是用于布局、装饰或约束子组件的核心部件,能让程序更加美观,如何学习呢,能快速的应用起来,下面通过例子,来快速理解各种容器组件的使用方法。一程序界面二代码实现imp...
- Tauri:下一代桌面应用开发框架?
-
大厂技术坚持周更精选好文本文为来自教育-智能学习-前端团队成员的文章,已授权ELab发布。智能学习前端团队自创立以来,团队专注于打破大众对教育的刻板印象,突破固有的教学思维,攻破各类...
- Google移动开发平台Flutter发布 iOS和Android开发者神器
-
在昨晚的FlutterLive2018上,Google宣布Flutter1.0正式发布。这是一个基于Dart的移动开发平台,旨在帮助开发者在iOS和Android两个平台上...
- Flutter如何内存优化
-
Flutter是一种流行的跨平台移动应用程序开发框架,它提供了一种高效的方式来构建美观、快速、可靠的应用程序。然而,随着应用程序规模的增加,内存管理成为了一个重要的问题。本文将探讨如何优化Flutte...
- 阿里卖家 Flutter for Web 工程实践
-
作者:马坤乐(坤吾)Flutter自2015年初次亮相以来,经过了多年的发展已经相当成熟,在阿里、美团、拼多多等互联网公司都有广泛的应用。在ICBU阿里卖家上90+%的新业务使用Flu...
- Flutter——按钮
-
上一节介绍了输入组件,让我们了解了输入框、单选、多选、进度条等部件。为了响应用户的点击或触摸操作,就需要学习一种新的部件-按钮。Flutter中有多种类型的按钮,用于响应用户的点击或触摸操作。接下来...
- Flutter vs React Native 性能测试结果
-
FluttervsReactNative性能测试结果:谁才是王者?大家好,我是猫哥。今天会去讲一下Flutter和ReactNative的性能比较的情况。首先说一下ReactNative猫哥...
- 定位Flutter内存问题很难么?
-
内存水位升高导致的稳定性问题严重影响app用户体验,所以开发者们非常关注Flutter的内存表现。随着Flutter业务越来越多,闲鱼也面临着oom导致的crash率提升的问题,下面我们结合项目中实际...
- 超详解析Flutter渲染引擎|业务想创新,不了解底层原理怎么行?
-
作者|万红波(远湖)出品|阿里巴巴新零售淘系技术部前言Flutter作为一个跨平台的应用框架,诞生之后,就被高度关注。它通过自绘UI,解决了之前RN和weex方案难以解决的多端一致性...
- flutter软件开发笔记25-内置组件总结
-
以下是Flutter内置的核心UI组件分类及常用组件列表,方便你系统化学习和掌握:1.基础组件组件名说明Text显示文本,支持样式、换行和富文本(TextSpan)Icon显示Materi...
- Flutter CarouselView 3.24版本的一个新组件
-
Flutter3.24带来了几个令人兴奋的新功能,其中最引人注目的是CarouselView小部件。这个小部件允许开发人员创建可滚动的、动态的项目列表,这些列表可以在进入和退出视图时调整大小,这对于...
- 详解Flutter如何用思源宋体炫出你的UI
-
如何实现使用思源宋体字体样式。首先,让我们来了解一下什么是资源素材管理。简单来说,它就是为了方便我们管理各种资源文件,比如图片、字体、音频等。在Flutter中,我们可以使用pubspec.yam...
- 一周热门
-
-
Python实现人事自动打卡,再也不会被批评
-
Psutil + Flask + Pyecharts + Bootstrap 开发动态可视化系统监控
-
一个解决支持HTML/CSS/JS网页转PDF(高质量)的终极解决方案
-
【验证码逆向专栏】vaptcha 手势验证码逆向分析
-
再见Swagger UI 国人开源了一款超好用的 API 文档生成框架,真香
-
网页转成pdf文件的经验分享 网页转成pdf文件的经验分享怎么弄
-
C++ std::vector 简介
-
python使用fitz模块提取pdf中的图片
-
《人人译客》如何规划你的移动电商网站(2)
-
Jupyterhub安装教程 jupyter怎么安装包
-
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- table.render (33)
- uniapp textarea (33)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- react-admin (33)
- vscode切换git分支 (35)
- vscode美化代码 (33)
- python bytes转16进制 (35)