如何编写一个CMakeLists.txt文件(由简到难,较详细)

news2024/11/12 17:26:50

在Linux系统下,经常使用CMakeLists.txt文件来链接、编译C++工程,大部分人clone的代码里都是有CMakeLists.txt文件的,只需要cmake ..make就完事了,但在工作中,你必须要有从无到有编写CMakeLists.txt文件的能力。

一、简单CMakeLists.txt文件的编写

1.先看文件目录

在这里插入图片描述
在repository4文件夹下存在好几个C++相关的文件和一个CMakeLIsts.txt文件,文件清单如下表:

文件(夹)名文件(夹)类型/用途
repository4工程文件夹
calc.h头文件,声明了几个函数定义
add.cpp/div.cpp/mult.cpp/sub.cpp头文件中声明的函数的实现
test_calc.cpp调用前面实现的几个函数
CMakeLists.txt编写此文件,用于此工程的链接和编译

以上除CMakeLists.txt文件以外的文件自己在保证调用、语法、功能正确的前提下随便编写。

2.编写CMakeLists.txt文件

(1)编写简单CMakeLists.txt文件的流程

设置cmake的最低版本要求
指定C++标准
设置项目名称
搜索文件夹中源文件
添加头文件路径
生成可执行文件

(2)编写简单CMakeLists.txt文件

cmake_minimum_required(VERSION 3.16)  #cmake最低版本要求,不得高于你计算机安装的版本

set(CMAKE_CXX_STANDARD 11)  #指定C++标准

project(test_calculate)  #设置项目名称“test_calculate”

aux_source_directory(${PROJECT_SOURCE_DIR} SRC)  #添加源文件到变量SRC中,宏PROJECT_SOURCE_DIR指的是正在编写的CMakeLists.txt文件所在的文件夹

include_directories(${PROJECT_SOURCE_DIR})  #制定头文件路径,${}符号是CMake语法中从变量取出变量的值的符号

add_executable(test_cacl ${SRC})  #使用源文件生成可执行文件test_cacl,源文件的路径及名称位于SRC变量中

这是非常简单的一个CMakeLists.txt文件,仅用于新手了解cmake的体验装

3.建立build文件夹,运行CMakeLists.txt文件,测试文件是否编写正确

建立build文件夹是为了保证源码文件的整洁,链接、编译生成的文件均在build文件夹下。

mkdir build  #在repository4文件夹下执行此命令生成build文件夹
cd build  #进入build文件夹
cmake ..  #使用CMakeLists.txt文件链接项目
make  #编译项目 生成可执行文件
./test_calc  #在编译正确后,运行生成的可执行文件test_calc

运行截图如下
在这里插入图片描述

4.稍微改进一下,显得整齐点

上面文件夹的内容显得比较凌乱,对其进行整理,整理后的文件目录如下,文件内容不变
在这里插入图片描述
将函数实现的cpp文件放在src目录下,将头文件放在include目录下
对应的CMakeLists.txt文件的内容如下

cmake_minimum_required(VERSION 3.16)

set(CMAKE_CXX_STANDARD 11)

project(test_calculate)

aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC)  #修改

include_directories(${PROJECT_SOURCE_DIR}/include)  #修改

add_executable(test_cacl ${SRC} test_calc.cpp)  #修改

执行命令不变!

二、编写嵌套CMakeLists.txt文件

1.先看文件目录

在这里插入图片描述

在repository5文件夹下存在好几个C++相关的文件和一个CMakeLIsts.txt文件,文件清单如下表:

文件(夹)名文件夹中的文件文件(夹)类型/用途
repository5以下文件(夹)工程文件夹
build链接、编译产生的文件用于存放链接、编译时产生的文件
includecalc.h / convert.h头文件,声明了几个函数定义
calccalc.cpp / CMakeLists.txt头文件calc.h中声明的函数的实现
convconv.cpp / CMakeLists.txt头文件convert.h中声明的函数的实现
test_calctest_calc.cpp / CMakeLists.txt调用calc.cpp实现的几个函数
test_convtest_conv.cpp / CMakeLists.txt调用conv.cpp实现的几个函数
CMakeLists.txt编写此文件,用于此工程的链接和编译

这些文件总共生成两个可执行文件,一个是计算相关的calc,一个是转换大小写字母的conv,我们需要在实现头文件中声明的那些函数的源文件所在的文件夹调用头文件中函数的测试文件(main函数)所在的文件夹 以及 工程项目repository文件夹中创建CMakeLists.txt文件。
以上除CMakeLists.txt文件以外的文件自己在保证调用、语法、功能正确的前提下随便编写。

2.编写嵌套CMakeLists.txt文件

(1)编写嵌套CMakeLists.txt文件的流程
在这里插入图片描述
(2)编写外层CMakeLists.txt文件

cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 11)
project(test)

#添加子文件夹
add_subdirectory(calc)
add_subdirectory(conv)
add_subdirectory(test_calc)
add_subdirectory(test_conv)

(3)编写内层CMakeLists.txt文件
a.编写calc文件夹中的CMakeLists.txt文件

cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 11)
project(calc)

aux_source_directory(./ SRC)  # ./=${PROJECT_SOURCE_DIR},在当前CMakeLists.txt所在文件夹(calc)中搜索源文件,并放置在SRC变量中
include_directories(../include)  #添加头文件路径,../表示上一层目录,../include即在上一层目录下的include中
add_library(calculate STATIC ${SRC})  #取出SRC变量的值,生成一个名为calculate的静态库

b.编写test_calc文件夹中的CMakeLists.txt文件

cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 11)
project(calculator)
aux_source_directory(./ SRC)
include_directories(../include)

link_libraries(calculate)  #链接生成的静态库
add_executable(calculator ${SRC})  #生成名为calculator的可执行文件

与calc相关的CMakeLists.txt就写好了

c.剩下的两个与conv相关的CMakeLists.txt文件其实与上面两个编写是一样的,只是更改一下相关名称而已,其内容如下:
conv文件夹中的CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 11)
project(conv)

aux_source_directory(./ SRC)
include_directories(../include)
add_library(convert STATIC ${SRC})

test_conv文件夹中的CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 11)
project(convertor)
aux_source_directory(./ SRC)
include_directories(../include)
link_libraries(convert)
add_executable(convertor ${SRC})

3.在项目文件夹下创建build文件夹,测试刚才编写的CMakeLists.txt文件

mkdir build
cd build
cmake ..
make
./test_conv/convertor

在这里插入图片描述

4. 同样改进一下,指定一下生成的库、可执行文件的存放目录

(1)在外层CMakeLists.txt文件中定义要存放生成的库、可执行文件的变量以及路径

set(LIBPATH ${PROJECT_SOURCE_DIR}/lib)  #设置生成库的路径,并放置在变量LIBPATH中
set(EXEPATH ${PROJECT_SOURCE_DIR}/bin)  #设置可执行文件的路径,并放置在变量EXEPATH中
#文件夹bin和lib会自动生成

(2)在内层CMakeLists.txt文件中使用上面两个全局变量LIBPATHEXEPATH

set(LIBRARY_OUTPUT_PATH ${LIBPATH})  #设置库的生成目录,在calc和conv文件夹下的CMakeLists.txt中添加
link_directories(../lib)  #设置要链接的库的目录,在test_calc和test_conv文件夹下的CMakeLists.txt中添加
set(EXECUTABLE_OUTPUT_PATH ${EXEPATH})  #设置可执行文件的生成目录,在test_calc和test_conv文件夹下的CMakeLists.txt中添加

(3)添加后在当前工程的build路径下执行以下命令来测试CMakeLists.txt文件

cmake ..
make
../bin/convertor  #当前在build文件夹中,bin文件夹和build并列,因此需要../

在这里插入图片描述

三、cmake常用函数

1.基础命令

命令功能备注
#单行注释
#[ [ ] ]块注释
${ }从变量或宏中取出其值例如:${PROJECT_SOURCE_DIR}
cmake_minimum_required(VERSION 3.0)指定使用的 cmake 的最低版本非必须,如果不加可能会有警告
project()定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言如果不需要这些都是可以忽略的,只需要指定出工程名字即可
add_executable(可执行程序名 源文件名称)使用后面的源文件生成可执行程序源文件名可以是一个也可以是多个,如有多个可用空格或;间隔
set(VAR [value]将[value]存入变量VAR中[value]可以是多个,空格或;间隔
set(CMAKE_CXX_STANDARD 11)设置C++的标准CMAKE_CXX_STANDARD是预定义的宏
aux_source_directory(< dir > < variable >)dir:要搜索的目录,variable:将从dir目录下搜索到的源文件列表存储到该变量中
file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中,GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中GLOB与GLOB_RECURSE 二选一
include_directories(headpath)添加含头文件路径headpath:头文件路径
add_library(库名称 STATIC/SHARED 源文件1 [源文件2] …)使用源文件生成库STATIC:静态库,SHARED:动态库

2.包含库文件

link_libraries(<static lib> [<static lib>...])

参数1:指定出要链接的第一个静态库的名字,可以是全名 libxxx.a,也可以是掐头(lib)去尾(.a)之后的名字 xxx;
参数2-N:要链接的其它静态库的名字(根据实际情况是否需要);
如果该静态库不是系统提供的(自己制作或者使用第三方提供的静态库)可能出现静态库找不到的情况,此时可以将静态库的路径也指定出来:

link_directories(<lib path>)

在cmake中链接动态库的命令如下:

target_link_libraries(
    <target> 
    <PRIVATE|PUBLIC|INTERFACE> <item>... 
    [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)

target:指定要加载的库的文件的名字:该文件可能是一个源文件、一个动态库/静态库文件、一个可执行文件
PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为PUBLIC
如果各个动态库之间没有依赖关系,无需做任何设置,三者没有没有区别,一般无需指定,使用默认的 PUBLIC 即可,动态库的链接具有传递性,如果动态库 A 链接了动态库B、C,动态库D链接了动态库A,此时动态库D相当于也链接了动态库B、C,并可以使用动态库B、C中定义的方法。

3.日志

message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)

(无) :重要消息
STATUS :非重要消息
WARNING:CMake 警告, 会继续执行
AUTHOR_WARNING:CMake 警告 (dev), 会继续执行
SEND_ERROR:CMake 错误, 继续执行,但是会跳过生成的步骤
FATAL_ERROR:CMake 错误, 终止所有处理过程
例如:

# 输出一般日志信息
message(STATUS "source path: ${PROJECT_SOURCE_DIR}")
# 输出警告信息
message(WARNING "source path: ${PROJECT_SOURCE_DIR}")
# 输出错误信息
message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}")

4.变量操作

命令功能
set(变量名1 ${变量名1} ${变量名2} …)将从第二个参数开始往后所有的字符串进行拼接,结果覆盖写入到第一个参数中
list(APPEND < list> [< element> …])APPEND表示进行数据追加,后边的参数和set就一样了
list(REMOVE_ITEM < list> < value>)从list中移除后面的value
list(LENGTH < list> < outputvariable>)获取 list 的长度,< output variable>:新创建的变量,用于存储列表的长度
list(GET < list> < element index> < output variable>)读取列表中指定索引的的元素,可以指定多个索引
list (JOIN < list> < glue> < output variable>)将列表中的元素用连接符(glue)连接起来组成一个字符串
list(FIND < list> < value> < output variable>)查找列表是否存在指定的元素,若果未找到,返回-1
list(INSERT < list> < element_index> < element> [< element> …])在list中指定的位置插入若干元素
list (PREPEND < list> [< element> …])将元素插入到列表的0索引位置
list (POP_BACK < list> [< out-var>…])将列表中最后元素移除
list (POP_FRONT < list> [< out-var>…])将列表中首元素移除
list (REMOVE_ITEM < list> < value> [< value> …])将指定的元素从列表中移除
list (REMOVE_AT < list> < index> [< index> …])将指定索引的元素从列表中移除
list (REMOVE_DUPLICATES < list>)移除列表中的重复元素
list(REVERSE < list>)列表翻转

列表排序

list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
/*
COMPARE:指定排序方法。有如下几种值可选:
	STRING:按照字母顺序进行排序,为默认的排序方法
	FILE_BASENAME:如果是一系列路径名,会使用basename进行排序
	NATURAL:使用自然数顺序排序
CASE:指明是否大小写敏感。有如下几种值可选:
	SENSITIVE: 按照大小写敏感的方式进行排序,为默认值
	INSENSITIVE:按照大小写不敏感方式进行排序
ORDER:指明排序的顺序。有如下几种值可选:
	ASCENDING:按照升序排列,为默认值
	DESCENDING:按照降序排列
*/

5.宏定义

在进行程序测试的时候,我们可以在代码中添加一些宏定义,通过这些宏来控制这些代码是否生效,如下所示:

#include <stdio.h>
#define NUMBER  3

int main()
{
    int a = 10;
#ifdef DEBUG
    printf("我是一个程序猿, 我不会爬树...\n");
#endif
    for(int i=0; i<NUMBER; ++i)
    {
        printf("hello, GCC!!!\n");
    }
    return 0;
}

在程序的第七行对DEBUG宏进行了判断,如果该宏被定义了,那么第八行就会进行日志输出,如果没有定义这个宏,第八行就相当于被注释掉了,因此最终无法看到日志输入出(上述代码中并没有定义这个宏)。
为了让测试更灵活,我们可以不在代码中定义这个宏,而是在测试的时候去把它定义出来,其中一种方式就是在gcc/g++命令中去指定,如下:

gcc test.c -DDEBUG -o app

在gcc/g++命令中通过参数 -D指定出要定义的宏的名字,这样就相当于在代码中定义了一个宏,其名字DEBUG
在CMake中我们也可以做类似的事情,对应的命令叫做add_definitions():

add_definitions(-D宏名称)

//针对上面的例子在对应CMakeLists.txt文件中写这句就等于gcc test.c -DDEBUG -o app
//add_definitions(-DDEBUG)

下面的列表中为大家整理了一些CMake中常用的宏:

功能
PROJECT_SOURCE_DIR使用cmake命令后紧跟的目录,一般是工程的根目录
PROJECT_BINARY_DIR执行cmake命令的目录
CMAKE_CURRENT_SOURCE_DIR当前处理的CMakeLists.txt所在的路径
CMAKE_CURRENT_BINARY_DIRtarget 编译目录
EXECUTABLE_OUTPUT_PATH重新定义目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH重新定义目标链接库文件的存放位置
PROJECT_NAME返回通过PROJECT指令定义的项目名称
CMAKE_BINARY_DIR项目实际构建路径,假设在build目录进行的构建,那么得到的就是这个目录的路径

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2038258.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一款简易大众点评项目实战——达人探店、关注、附近商圈、签到、UV统计

一款简易大众点评项目实战——达人探店、关注、附近商圈、签到、UV统计 8.达人探店8.1上传接口与发布笔记接口8.2 达人探店-查看探店笔记8.3 达人探店-点赞功能8.4 达人探店-点赞排行榜 9、好友关注9.1 好友关注-关注和取消关注9.2 好友关注-共同关注9.3 好友关注-Feed流实现方…

Visual Studio 2024安装教程(非常详细),从零基础入门到精通,看完这一篇就够了(附安装包)

软件下载 软件&#xff1a;Visual Studio版本&#xff1a;2022语言&#xff1a;简体中文大小&#xff1a;4.11M安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU2.0GHz 内存4G(或更高&#xff09;下载通道①百度网盘丨下载链接&#xff1a;https://pan.baid…

C语言-在主函数中输入10个等长的字符串。用另一函数对他们进行排序,然后再主函数输出这10个排好序的数列(分别用数组法和指针法实现)

在主函数中输入10个等长的字符串。用另一函数对他们进行排序&#xff0c;然后再主函数输出这10个排好序的数列&#xff08;分别用数组法和指针法实现&#xff09; 一、数组法实现 void str_sort(char str[][32], int n) {int i, j;for (i 0; i < n - 1; i){for (j 0; j …

Stable Diffusion WebUI安装ControlNet 遇到的问题

最近研究Stable Diffusion &#xff0c;在安装ControlNet遇到了几个问题&#xff0c;总算解决了 1.第一个是连不上github,可以使用国内的这个 https://gitcode.com/gh_mirrors/sd/sd-webui-controlnet.git 2.第二个问题是一直Installing ,虽然下载下来了&#xff0c;但还是…

【Python】函数练习题

1、定义一个函数&#xff0c;用于计算一个字符串中字符a出现的次数并通过return返回。 代码&#xff1a; 2、写函数&#xff0c;判断用户传入的一个值&#xff08;字符串或列表或字典或元组&#xff09;长度是否大于5&#xff0c;如果大于5返回True,反之返回False. 代码&…

Python实现邮件发送时,如何优化邮件内容?

Python实现邮件发送如何设置&#xff1f;使用Python发信技巧&#xff1f; 无论是个人用途还是企业需求&#xff0c;一封优化良好的邮件能够提升用户体验&#xff0c;提高邮件的打开率和响应率。AokSend将探讨在Python实现邮件发送时&#xff0c;如何通过几个关键步骤来优化邮件…

【C#】中IndexOf的用法

在 C# 中&#xff0c;IndexOf 方法是字符串和列表&#xff08;如 List<T>&#xff09;等数据结构中常用的方法&#xff0c;用于查找指定元素或子串首次出现的位置。以下是针对不同情况使用 IndexOf 的示例。 对于字符串 对于字符串类型&#xff0c;IndexOf 方法返回子字…

【Python-办公自动化】1秒比较出2张表格之间的不同并标黄加粗

欢迎来到"花花 Show Python"&#xff0c;一名热爱编程和分享知识的技术博主。在这里&#xff0c;我将与您一同探索Python的奥秘&#xff0c;分享编程技巧、项目实践和学习心得。无论您是编程新手还是资深开发者&#xff0c;都能在这里找到有价值的信息和灵感。 自我介…

本地http://localhost/端口 通过本地服务,远程调用同一网络下的另一台主机的微服务方法

首先需要确保能够ping通另一台主机的ip&#xff0c;切记一定要确保可以ping通&#xff0c;否则无法正常访问 我使用的spring-cloud框架&#xff0c;在spring-cloud框架中进行操作&#xff0c;我是有一个单独的Remote模块用于远程调用&#xff0c;在这里 第一步&#xff1a;需要…

Stable Diffusion绘画 | 提示词中的符号解析

() 小括号-提升权重 每个单词默认的权重值为1&#xff0c; 每套上一层“()”后&#xff0c;权重增加1.1倍&#xff0c;最多可套3层小括号&#xff1a; blue&#xff1a;权重值1 (blue)&#xff1a;权重值1.1 ((blue))&#xff1a;权重值1.21 (((blue)))&#xff1a;权重值1.3…

进程与磁盘管理相关

进程与磁盘管理相关 进程创建&#xff08;或者存在的)的唯一标志》进程控制块 PCB是进程存在的唯一标志&#xff0c;当进程被创建时&#xff0c;操作系统为当前进程创建PCB&#xff0c;当进程结束时&#xff0c;会回收PCB PCB .进程描述信息 。进程控制和管理信息 。资源分…

GPS跟踪环路MATLAB之——数字锁相环

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 GPS跟踪环路MATLAB之——数字锁相环 前言为什么要锁相环科斯塔斯环锁相环的一些基本概念1、捕获、锁定与跟踪的概念2、捕获时间和稳态相差3、相位捕获和频率捕获4、捕获带和同…

《计算机组成原理》(第3版)第5章 输入输出系统 复习笔记

第5章 输入输出系统 一、概述 &#xff08;一&#xff09;输入输出系统的发展概况 1&#xff0e;早期阶段 早期的I/O设备种类较少&#xff0c;I/O设备与主存交换信息都必须通过CPU&#xff0c;如图5-1所示。 图5-1 I/O设备通过CPU与主存交换信息 2&#xff0e;接口模块和DMA…

JavaScript模拟空调效果

JavaScript模拟空调效果https://www.bootstrapmb.com/item/15074 在JavaScript中模拟空调效果主要依赖于前端界面的交互和状态变化&#xff0c;因为实际的温度调节、风扇速度调整等硬件操作无法直接通过JavaScript在浏览器中实现。不过&#xff0c;我们可以通过JavaScript来模…

yaml详解实战,读取实战,以及运行结果解析,以及单引号和双引号对转义符读取的影响

一、 YAML&#xff1a;数据格式 文章&#xff1a;《yaml基本语法&#xff0c;数据类型&#xff0c;对象&#xff0c;数组&#xff0c;复合结构&#xff0c;纯量&#xff0c;引用&#xff1b;文件后缀为.yml或.yaml》 里有一些介绍&#xff0c;以及我自己的理解。 这篇文章偏实战…

nvm介绍、下载、安装、配置及使用

一、背景 在工作中&#xff0c;我们可能同时在进行2个或者多个不同的项目开发&#xff0c;每个项目的需求不同&#xff0c;进而不同项目必须依赖不同版本的NodeJS运行环境&#xff0c;这种情况下&#xff0c;对于维护多个版本的node将会是一件非常麻烦的事情&#xff0c;nvm就…

Linux C 程序 【04】线程分离

1.开发背景 Linux 线程区分于FreeRTOS&#xff0c;线程的属性形态有2中&#xff0c;在 pthread.h 中有注解&#xff0c;如下。 /* Detach state. */ enum {PTHREAD_CREATE_JOINABLE, #define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_JOINABLEPTHREAD_CREATE_DETACHED #defin…

72v-85v输出12v/13.5v电流6A用在电自上AH1009

135-3806-7573.在探讨“72V-85V输出12V/13.5V电流6A用在电动自行车&#xff08;电自上&#xff09;AH1009”这一主题时&#xff0c;我们深入到一个结合了电力电子技术、电池管理系统以及电动自行车性能优化的专业领域。。 *****芯片概述&#xff1a; AH1009是一款宽电压范围、…

数据中心服务器监控系统搭建方案

数据中心监控系统搭建方案 1、背景2、环境3、部署3.1、终端部署3.1.1、服务器配置3.1.1.1、浪潮服务器3.1.1.2、曙光服务器 3.2、服务端部署3.2.1、在线环境下载3.2.2、离线环境安装3.2.3、系统配置 1、背景 服务部署与三个网络环境中&#xff0c;其中某个网位于线下机房&…

Leetcode面试经典150题-2.两数相加

2. 两数相加 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外&#xff0c;这两个数都不…