Linux 系统下 CMake 示 例

news2024/11/15 20:02:19

在这里插入图片描述


CMake 是一个开源的跨平台工具,可以构建、测试和打包软件。
它具有如下特性:

  • 自动搜索可能需要的程序、库和头文件的能力;
  • 独立的构建目录(如build),可以安全清理;
  • 支持复杂的自定义命令(下载、生成各种文件);
  • 自定义配置可选组件;
  • 从简单的文本文件(CMakeLists.txt)自动生成工作区和项目的能力;
  • 在主流平台上自动生成文件依赖项并支持并行构建;
  • 几乎支持所有的 IDE

1. 安装CMake

sudo apt install cmake -y

安装完成后用查看版本指令cmake -version检查CMake是否安装成功。

在这里插入图片描述
出现以上提示代表安装成功。


2. 第一个CMake例子

我们首先新建两个文件,main.cppCMakeLists.txt

在这里插入图片描述

touch main.cpp CMakeLists.txt

main.cpp 中编写如下代码

#include <iostream>
using namespace std;

int main(){
    cout << "第一个CMake程序" << endl;
    return 0;
}

CMakeLists.txt 中编写如下代码

# CMake 最低版本号要求
cmake_minimum_required (VERSION 3.1)

# demo 代表项目名称
project (demo)

# 添加一个可执行程序,demo是可执行程序名称,main.cpp是源文件
add_executable(main main.cpp)

这里我们用到 add_executable,其中第一个参数是最终生成的可执行文件名以及在CMake 中定义的 Target 名。我们可以在 CMake 中继续使用 Target 的名字为 Target 的编译设置新的属性和行为。命令中第一个参数后面的参数都是编译目标所使用到的源文件。

在准备好后我们依次执行下列指令:

# 第一步:配置,-S 指定源码目录,-B 指定构建目录
cmake -S . -B build 
# 第二步:生成,--build 指定构建目录
cmake --build build
# 运行
./build/main
  1. 第一步,我们输入cmake -S . -B build

在这里插入图片描述

在这里插入图片描述

  1. 第二步,我们输入 cmake --build build

在这里插入图片描述

此时会产生一个可执行文件 main

在这里插入图片描述

  1. 第三步,我们输入 ./build/main 运行可执行文件 main

在这里插入图片描述


3. 同一个目录下编译多个文件

接下来我们尝试在同一个目录下编译多个文件,我们首先在文件夹下新建如下 4 4 4 个文件,我们首先演示同时编译带有头文件和 cpp 文件的案例。

在这里插入图片描述

touch main.cpp Account.cpp Account.h CMakeLists.txt

mian.cpp 的内容如下:

# include "Account.h"
# include <iostream>

int main()
{
    Account Q;
}

Account.cpp 的内容如下:

#include "Account.h"
#include <iostream>

Account::Account(/* args */)
{
    std::cout << "构造函数Account::Account()" << std::endl;
}
Account::~Account()
{
    std::cout << "析构函数Account::~Account()" << std::endl;
}

Account.h 的内容如下:

#ifndef Account_H
#define Account_H

class Account
{
private:
    /* data */
public:
    Account(/* args */);
    ~Account();
};


#endif // Account_H

CMakeLitsts.txt 的内容如下:

#account_dir/CMakeLists.txt

# 最低版本要求
cmake_minimum_required(VERSION 3.10)

# 项目信息
project(Account)

# main 是可执行文件名称,后面是源文件
add_executable(main Account.cpp main.cpp Account.h)

接下来我们开始编译,依次输入以下指令:

cmake -S . -B build
cmake --build build

在这里插入图片描述

编译成功后文件结构如下所示

在这里插入图片描述


这个案例是想说明,如果在同一目录下有多个源文件,那么只要在 add_executable 里把所有源文件都添加进去就可以了,但是当我们源文件过多的时候就不太方便了,这时候我们用到了 cmake 的另一个命令 aux_source_directory(dir var)
第一个参数 dir是指定目录,第二个参数 var是用于存放源文件列表的变量。

所以我们可以改写一下 CMakeLists.txt

# 最低版本要求
cmake_minimum_required(VERSION 3.10)

# 项目信息
project(Account)

aux_source_directory(. SRC_LIST)

add_executable(main ${SRC_LIST})

这段代码使用 aux_source_directory 把当前目录下的源文件存列表存放到变量 SRC_LIST 里,然后在 add_executable 里调用 SRC_LIST

再次执行 cmake 后最终的得到的结果是完全一样的。

在这里插入图片描述

值得注意的是,aux_source_directory() 也存在弊端,它会把指定目录下的所有源文件都加进来,可能会加入一些我们不需要的文件,此时我们可以使用 set 命令去新建变量来存放需要的源文件,如下:

cmake_minimum_required (VERSION 3.10)

project (Account)

set( SRC_LIST
	 ./main.cpp
	 ./Account.cpp
     ./Account.h)

add_executable(main ${SRC_LIST})

set 命令是用于定义变量的,set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
EXECUTABLE_OUT_PATHPROJECT_SOURCE_DIRCMake 自带的预定义变量,其意义如下,

  • EXECUTABLE_OUTPUT_PATH :目标二进制可执行文件的存放位置
  • PROJECT_SOURCE_DIR:工程的根目录

4. 不同目录下编译多个文件

接着上面的内容,我们来演示不同目录下编译多个文件的案例,我们将上面的文件按照如下的结构重构,内容不需要改变。

一般来说, 源文件都放到 src 目录下,把头文件放入到 include 文件下,生成的对象文件放入到 build 目录下,最终输出的 elf 文件会放到 bin 目录下

在这里插入图片描述

随后在最外层目录下新建 main.cppCMakeLists.txt

在这里插入图片描述

main.cpp代码如下:

#include <iostream>
#include "Account.h"

int main()
{
    Account alice_account;
    std::cout << "test Account 的main函数" << std::endl;
    return 0;
}

CMakeLists.txt 代码如下:

cmake_minimum_required (VERSION 3.10)

project (Account)

include_directories (include src)

aux_source_directory (include SRC_LIST)
aux_source_directory (src SRC_LIST1)

add_executable (main main.cpp ${SRC_LIST} ${SRC_LIST1})

这里出现了一个新的命令:include_directories 。该命令是用来向工程添加多个指定头文件的搜索路径,路径之间用空格分隔。
因为 main.cppincludeAccount.h,如果没有这个命令来指定头文件所在位置,就会无法编译。当然,也可以在main.cpp 里使用 include 来指定路径,如下

#include "include/Account.h""

完成后开始编译:

cmake -S . -B build
cmake --build build
./build/main

在这里插入图片描述

在这里插入图片描述


5. 静态库和动态库

5.1 什么是库文件?

库文件(Library files)是预先编译好的可重用代码和资源的集合,它们被用于简化软件开发过程并提供常见功能的封装。库文件包含一组函数、类、数据结构、变量、常量或其他可执行代码的实现。

库文件的主要目的是为了促进代码的重用和模块化开发,以提高开发效率、减少代码冗余,并使代码更易于维护。通过使用库文件,开发人员可以在自己的应用程序中调用库中提供的函数、方法和类,从而实现特定功能而无需从头开始编写代码。

库文件可以分为两种主要类型:

  1. 静态库(Static Library):静态库的代码在编译时被复制到可执行文件中,可执行文件独立于库文件运行。静态库提供了一种静态链接的方式,使得可执行文件可以在没有外部依赖的情况下运行。静态库通常以.lib (Dynamic-Link Libraries)(Windows)或.a(Unix/Linux)等文件扩展名表示。
  2. 动态库(Dynamic Library):动态库的代码在运行时由操作系统动态加载到内存中,并与可执行文件共享。动态库可以被多个应用程序共享使用,减少了内存占用和重复加载的开销。动态库通常以.dll(Windows)或.so (Shared Object)(Unix/Linux)等文件扩展名表示。

库文件可以提供各种功能,例如数学计算、文件操作、网络通信、图形界面、数据库访问、加密解密等。通过使用库文件,开发人员可以利用已经实现和测试过的功能,加速开发过程,减少错误和重复劳动。

5.2 静态库和动态库的区别?

  1. 链接方式:

    • 静态库:在编译时,静态库的代码会被完整地复制到可执行文件中。链接时,编译器将库的代码与应用程序代码合并成一个独立的可执行文件。这意味着可执行文件在运行时不依赖于外部的库文件。
    • 动态库:在编译时,动态库的代码不会被复制到可执行文件中。相反,可执行文件在运行时加载动态库,并在内存中共享库的代码和数据。这意味着可执行文件依赖于外部的库文件,并且可以与多个应用程序共享。
  2. 文件大小和内存占用:

    • 静态库:静态库将代码复制到可执行文件中,因此可执行文件的大小会增加。每个使用该静态库的可执行文件都包含该库的完整副本,因此占用的内存空间也会较大。

    • 动态库:动态库的代码不复制到可执行文件中,因此可执行文件的大小较小。多个应用程序可以共享同一个动态库的实例,因此内存占用可以被多个应用程序共享,减少了重复加载的开销。

  3. 更新和维护:

    • 静态库:静态库一旦被编译到可执行文件中,更新库的代码需要重新编译可执行文件。这意味着每个使用该静态库的应用程序都需要重新构建以包含最新版本的库。
    • 动态库:动态库可以被独立地更新,而不需要重新编译可执行文件。只需替换动态库文件即可,所有使用该库的应用程序都可以享受到更新的功能和修复的 bug。
  4. 可移植性:

    • 静态库:静态库是平台相关的,需要为每个目标平台编译不同的库文件。可执行文件与特定平台上的静态库文件紧密耦合,不易在不同平台上移植。
    • 动态库:动态库是平台独立的,可以在多个平台上共享使用。可执行文件只需要加载适应当前平台的动态库即可实现跨平台的移植性。

5.3 生成库文件

下面使用代码来演示一下生成静态库的过程,依然新建三个文件,Account.cppAccount.h CMakeLists.txt

在这里插入图片描述
Account.cpp 内容如下:

#include "Account.h"
#include <iostream>

Account::Account(/* args */)
{
    std::cout << "构造函数Account::Account()" << std::endl;
}
Account::~Account()
{
    std::cout << "析构函数Account::~Account()" << std::endl;
}

Account.h 内容如下:

#ifndef Account_H
#define Account_H

class Account
{
private:
    /* data */
public:
    Account(/* args */);
    ~Account();
};


#endif // Account_H

CMakeLists.txt 内容如下:

#account_dir/CMakeLists.txt

# 最低版本要求
cmake_minimum_required(VERSION 3.10)

# 项目信息
project(Account)

# 添加静态库,Linux下会生成libAccount.a
# Account是库名,STATIC表示静态库,SHARED表示动态库,后面是源文件
add_library(Account STATIC Account.cpp Account.h)

add_library 生成动态库或静态库 (第 1 1 1 个参数指定库的名字;第 2 2 2 个参数决定是动态还是静态 (默认静态);第 3 3 3 个参数指定生成库的源文件)

准备完成后我们使用如下指定编译:

cmake -S . -B build
cmake --build build

在这里插入图片描述

成功编译后我们就可以在 build 文件夹下看到我们以 .a 结尾的库文件了

在这里插入图片描述


下面来演示更加复杂的例子,我们首先将上一步编译好的部分文件移除,下图高亮部分,也就是在build 目录下只留下 libAccount.a 文件:

在这里插入图片描述

随后在 test_account 文件夹下新建如下两个文件:

mian.cpp 内容如下:

#include <iostream>
#include "Account.h"

int main()
{
    Account alice_account;
    std::cout << "main函数被调用" << std::endl;
    return 0;
}

CMakeLists.txt 内容如下:

# test_account/CMakeLists.txt

# 最低版本要求
cmake_minimum_required(VERSION 3.10)

# 项目名称
project(test_account)

# 添加执行文件
add_executable(test_account main.cpp)

# 指定目标包含的头文件目录
target_include_directories(test_account PUBLIC "../account_dir")
# 添加库文件目录,如果不添加,找不到库文件
target_link_directories(test_account PUBLIC "../account_dir/build")
# 指定目标链接的库
target_link_libraries(test_account PRIVATE Account)

通过 target_include_directories ,我们给 test_account 添加了头文件引用路径 "../account_dir"。上面的关键词 PUBLIC , PRIVATE 用于说明目标属性的作用范围。
通过 target_link_libraries ,将前面生成的静态库 libAccount.a 链接给对象 test_account ,但此时还没指定库文件的目录,CMake 无法定位库文件
再通过 target_link_directories ,添加库文件的目录即可。

准备完成后结构如下:

在这里插入图片描述
进入 test_account 目录开始编译:

在这里插入图片描述
编译后结构如下所示:
在这里插入图片描述


6. CMake OpenCV 例子

安装 OpenCV 库:

sudo apt install libopencv-dev

main.cpp 代码如下:

#include <iostream>
#include <opencv2/opencv.hpp>


# 输入一张图像返回这张图象缩放后的灰度图
int main(int argc, char **argv)
{
    if (argc != 2)
    {
        std::cout << "请输入图片路径" << std::endl;
        return -1;
    }
    else
    {
        std::cout << "图片路径为:" << argv[1] << std::endl;

        cv::Mat image = cv::imread(argv[1]);
        cv::Mat image_resized;
        cv::resize(image, image_resized, cv::Size(400, 400));
        cv::cvtColor(image_resized, image_resized, cv::COLOR_BGR2GRAY);
        cv::imwrite("resized.png", image_resized);

        std::cout << "图片已处理完毕,并保存为resized.png" << std::endl;
    }

    return 0;
}

CMakeLists.txt 代码如下

cmake_minimum_required(VERSION 3.10)

project(demo_opencv)

find_package(OpenCV REQUIRED)

if (OpenCV_FOUND)
    message(STATUS "OpenCV library status:")
    message(STATUS "    libraries: ${OpenCV_LIBS}")
    message(STATUS "    include path: ${OpenCV_INCLUDE_DIRS}")
else ()
    message(FATAL_ERROR "Could not find OpenCV")
endif ()


add_executable(demo_opencv main.cpp)
target_include_directories(demo_opencv PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(demo_opencv ${OpenCV_LIBS})

在这里插入图片描述
左侧为原图,右侧为缩放后的图


知识点整理较为混乱
TODO:后面应该多做几个真实案例


参考文献

Linux下CMake简明教程

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

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

相关文章

一文了解什么什么是加密货币及其工作原理

加密货币是基于区块链技术并由密码学保护的去中心化数字货币。要理解加密货币&#xff0c;首先需要理解三个术语——区块链、去中心化和密码学。 一、加密货币如何运作 简而言之&#xff0c;加密货币中的区块链是一种数字分类账&#xff0c;其访问权限分布在授权用户之间。该分…

hello算法学习笔记之排序

概述&#xff1a;排序算法 在排序算法中&#xff0c;数据类型可以是整数、浮点数、字符或字符串等&#xff1b;顺序的判断规则可根据需求设定&#xff0c;如数字大小、字符 ASCII 码顺序或自定义规则。 评价维度&#xff1a; 运行效率、就地性、稳定性、自适应性&#xff08…

21.RocketMQ源码之NameServer的路由管理和架构设计

highlight: arduino-light NameServer 路由管理 Broker消息服务器在启动的时向所有NameServer注册。 消息生产者Producer在发送消息之前先从NameServer获取Broker服务器地址列表然后根据负载均衡算法从列表中选择一台服务器进行发送。 NameServer与每台Broker保持长连接&#x…

单频/双频gps北斗模块相关应用领域详解_SKYLAB GPS+北斗模块

以“时空数据&#xff0c;赋能未来”为主题的第十二届中国卫星导航年会在江西南昌正式开幕&#xff0c;据悉&#xff0c;本届年会是北斗系统开启全球化、产业化的第一届年会。2020年&#xff0c;北斗三号全球卫星定位系统正式服务全球&#xff0c;作为北斗产业链中的一员&#…

小黑厦门极限神游,通宵环岛骑行,鼓浪屿徒步赏景的leetcode之旅:剑指 Offer 48. 最长不含重复字符的子字符串

小黑代码(与官方题解思路一致&#xff0c;比其可读性更强) class Solution:def lengthOfLongestSubstring(self, s: str) -> int:# 字符串长度n len(s)# 定义双指针head 0tail 0# 中间变量&#xff0c;存放窗口中的元素set_ set()# 结果变量length 0while tail < n…

Flutter iOS 打包 问题处理

日常问题收集&#xff1a; remark: Incremental compilation has been disabled: is not currently compatible with embedding LLVM IR bitcode a. 在Build Settings中搜索Enable Bitcode-> 设置No b. Project-> Targets-> Build Settings-> Custom Compiler Flag…

银行数字化转型导师坚鹏:银行数字化运营所必须采取的五大措施

银行数字化运营已经成为提升市场竞争力和客户满意度的重要战略。以下是银行数字化运营所必须采取的五大措施&#xff1a; 1) 建立强大的数字化基础设施&#xff1a;银行需要投资建立可靠的数字化基础设施&#xff0c;以支持数字化运营的各个方面。这包括更新和升级银行的IT系统…

springboot集成openfeign

一、Feign简介 Feign是一个声明式的伪Http客户端&#xff0c;它使得写Http客户端变得更简单。使用Feign&#xff0c;只需要创建一个接口并注解。它具有可插拔的注解特性&#xff0c;可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon&…

Django - 定时任务框架【django-apscheduler】基本使用详解(二)

一. 前言 一个网页会有很多数据是不需要经常变动的&#xff0c;比如说首页&#xff0c;变动频率低而访问量大&#xff0c;我们可以把它静态化&#xff0c;这样就不需要每次有请求都要查询数据库再返回&#xff0c;可以减少服务器压力 我们可以使用Django的模板渲染功能完成页面…

【Android Framework系列】第4章 PMS原理

1 PMS简介 PMS&#xff08;PackageManagerService&#xff09;是Android提供的包管理系统服务&#xff0c;它用来管理所有的包信息&#xff0c;包括应用安装、卸载、更新以及解析AndroidManifest.xml。通过解析每个安装应用的AndroidManifest.xml&#xff0c;将xml中的数据全部…

Acwing.846 数的重心(DFS)

题目 给定一颗树&#xff0c;树中包含n个结点&#xff08;编号1~n)和n-1条无向边。 请你找到树的重心&#xff0c;并输出将重心删除后&#xff0c;剩余各个连通块中点数的最大值。 重心定义:重心是指树中的一个结点&#xff0c;如果将这个点删除后&#xff0c;剩余各个连通块中…

Java项目实战——Linux入门

文章目录 一、Linux安装1.1、安装方式介绍1.2、网卡设置1.3、安装SSH连接工具1.4、Linux和windows目录结构对比1.5、Linux目录结构 2、Linux常用命令2.1、Linux命令初体验2.2、使用技巧2.3、命令格式2.4、文件目录操作命令文件目录操作命令ls小知识 文件目录操作命令cat文件目录…

数据倾斜排查

一、问题现象 租户反馈&#xff0c;任务执行时长加长&#xff0c;执行过程中任务卡在 99%&#xff0c;大概率是出现了数据倾斜 二、排查过程 数据倾斜大多数都是大 key 问题导致的。排查方法如下&#xff1a; 1.时间判断 reduce 的时间比其他 reduce 时间长的多&#xff0c;大…

基于STM32的户外环境监测系统的设计

目录 1 引言 1.1 本课题的研究意义 1.2 本课题的研究现状 1.3本课题的发展趋势和研究可行性 1.4本课题主要研究工作 2 系统的概述和相关原理 2.1 系统的概述 2.1.1 总体设计的方案 2.1.2 总体框图 2.2 相关理论 2.2.1 STM32平台 2.2.2 WIFI模块 3 硬件电路设计 8 3…

解决页面等比缩放问题

近些年可视化数据大屏技术早已成熟&#xff0c;在市场上相关技术也是五花八门&#xff1b;通常情况是自行开发&#xff0c;要不找技术比较成熟大厂定制&#xff0c;或者使用较成熟的低代码平台实现。 技术门槛比较低&#xff0c;不过在数据大屏项目实施过程中会发现&#xff0c…

《移动互联网技术》第一章 概述: 掌握移动互联网的基本概念和组成

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

密码找回安全总结-业务安全测试实操(28)

撞库攻击 撞库是黑客通过收集互联网已泄露的用户和密码信息,生成对应的字典表,尝试批量登录其他网站后,得到一系列可以登录的用户名和密码组合。由于很多用户在不同网站使用的是相同的账号和密码,因此黑客可以通过获取用户在 A 网站的账户从而尝试登录B网站,这就可以理解为…

Linux--时间相关的指令:date、cal

一、data显示 date 指定格式显示时间&#xff1a; date %Y:%m:%d date 用法&#xff1a; date [OPTION]... [FORMAT] 1.在显示方面&#xff0c;使用者可以设定欲显示的格式&#xff0c;格式设定为一个加号后接数个标记&#xff0c;其中常用的标记列表如下 %H : 小时(00..2…

threejs动画

个人博客地址: https://cxx001.gitee.io 前面我们所用的模型大都是静态的&#xff0c;没有动画&#xff0c;没有生命。这节我们将赋予它们生命。 动画本质是通过改变物体的旋转、缩放、位置、材质、顶点、面以及其它你所能想到的属性来实现的。这些其实在前面章节示例里或多或…

git 版本控制从入门到精通

文章目录 1、git安装1.1、Linux安装1.2、Windows安装1.3、MAC安装 2、配置git3、git命令使用4、git远程服务器5、提交到远端服务器6、commit合并7、创建分支8、命令练习记录 1、git安装 1.1、Linux安装 在linux上我们建议你用二进制的方式来安装git&#xff0c;可以使用发行版…