3.C++ Make

news2024/11/26 17:39:30

1.Makefile

1.1 什么是 Makefile

一个工程中有很多文件,文件之间都是相辅相成有着编译的先后顺序,但是如果自己手动根据编译顺序编译文件造成速度非常慢。Makefile 是”自动化编译“,只需一个 make 指令系统就会根据编译顺序帮自己编译文件

1.2 Makefile 文件命名规则

image-20240408221524107

1.3 代码演示

1.生成一个 Makefile 文件

vim Makefile

2.将执行规则书写入 Makefile

image-20211101121336569

3.使用 make 编译 Makefile 文件

sudo apt install make // 安装 make 
make // 在 Makefile 所在文件夹执行指令

可以看到下图中输出了 Makefile 中的相关指令,与此同时在对应的文件下生成的 app 可执行文件

image-20240408221610240

4.执行 app 可执行文件

./app

这个 Makefile 是 make 默认执行的文件,如果不想叫 Makefile 也可以自定义名称,那么在 make 时后面就要拼接相应的文件名称。比如新建的 Makefile 替代文件是 abc

make abc

1.4 Makefile 的工作原理

image-20240408221652903

1.命令执行之前,检查规则中的依赖是否存在

当发现前面的命令找不到时 Makefile 文件会顺着继续向下查找后面的目标有没有有关生成前面相关文件的,如下图,一开始是没有 add.o 文件的,但是 makefile 文件会继续向下查找有没有生成 .o 的文件指令

Makefile 的指令都是为第一条指令服务的

image-20240408221722726

2.检测更新

Makefile 会对文件进行检测,比如使用 make 编译过 Makefile 文件的情况下,再使用 make 编译 Makefile 文件,就发现提示

image-20211101162023622

这是因为 make 是检查更新的,如果其中的文件没有更改,则就不会执行 Makefile 中的指令

1.5 Makefile 的优化

下面就要对下图的这些定义进行优化。优化一共分为三个步骤:使用变量将长字符串代替;使用通配符将重复的后缀代替;使用查找替换将长字符串中的后缀名进行查找替换

image-20211101143607539

1.使用变量进行优化

如上图所示可以使用变量代替一个长的字符串

image-20211101150106817

重新定义 Makefile 文件使用变量形式将长字符串进行取代

在最开头定义了两个变量 : src 和 target 并为其赋值。在后面使用 $(变量名) 的方法得到该变量的值。这样长字符串就在前面被定义了

image-20211101151125890

2.使用通配符简化命令代码

在文件中有很多 .c ;.o 的文件,% 可以理解为是 * 的操作;%.o 类似于 *.o这样的话会将所有的 .o 文件生成 .c 文件

image-20211101151440792 image-20211101152933807

3.使用“查找替换”将所有文件名替换

查找:

在自定义变量中定义了可以进行通配的文件名,下面对这些文件名进行替换

示例的意思是将本路径所有 *.c 和 sub 文件下的 *.c 文件名查找出来

image-20211101153710078

替换:

image-20211101154009105

查找到 text 中的单词 patten 并用 replacement 进行替换

image-20211101160206121

最后 make 执行的结果

image-20211101154752330

1.6 中间文件的清除

从 .c 文件到 app 可执行文件生成过程中产生大量 .o 的中间文件,下面使用 clean 的方法将中间过程的 .o 文件进行删除。同样是在 Makefile 中进行定义

image-20211101160933915

执行下面的指令就可以进行 clean 操作

image-20211101161118123

对 clean 操作生成伪目标

指示 clean 是一个假的定义操作。真的定义操作利用了自定义 Makefile 的方法定义的

.PHONY:clean 
image-20211101161435904

2.Cmake 的使用

2.1 执行一个文件

S1:写一个 .cpp 文件

#main.cpp
#include <iostream>
int main(){
  std::cout <<  "hello word" << std::endl;
}

S2:写一个 CMakeLists.txt 文件

#CMakeLists.txt

PROJECT (HELLO)

SET(SRC_LIST main.cpp)

MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})

MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})

ADD_EXECUTABLE(hello ${SRC_LIST})

S3:对该文件进行 cmake

cmake . // 其中 . 就代表 CMakeLists.txt 在当前目录中

然后就进行 build ,目录下就会生成文件,关键是生成了 Makefile 的文件

image-20220824213013180

S4:再使用 make 对文件进行编译,生成可执行文件

make 

2.2 CMake 常用指令

PROJECT关键字

可以用来指定工程的名字和支持的语言,默认支持所有语言

PROJECT (HELLO) 指定了工程的名字,并且支持所有语言—建议

PROJECT (HELLO CXX) 指定了工程的名字,并且支持语言是C++

PROJECT (HELLO C CXX) 指定了工程的名字,并且支持语言是C和C++

该指定隐式定义了两个CMAKE的变量

_BINARY_DIR,本例中是 HELLO_BINARY_DIR

_SOURCE_DIR,本例中是 HELLO_SOURCE_DIR

MESSAGE关键字就可以直接使用者两个变量,当前都指向当前的工作目录,后面会讲外部编译

问题:如果改了工程名,这两个变量名也会改变

解决:又定义两个预定义变量:PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,这两个变量和HELLO_BINARY_DIR,HELLO_SOURCE_DIR是一致的。所以改了工程名也没有关系

SET关键字

用来显示的指定变量的

SET(SRC_LIST main.cpp) SRC_LIST变量就包含了main.cpp

也可以 SET(SRC_LIST main.cpp t1.cpp t2.cpp)

MESSAGE关键字

向终端输出用户自定义的信息

主要包含三种信息:

  • SEND_ERROR,产生错误,生成过程被跳过。
  • SATUS,输出前缀为—的信息。
  • FATAL_ERROR,立即终止所有 cmake 过程.

在我们进行 cmake 的时候这两句话也会被显示出来

image-20220824214933257

ADD_EXECUTABLE关键字

生成可执行文件

ADD_EXECUTABLE(hello ${SRC_LIST}) 生成的可执行文件名是hello,源文件读取变量SRC_LIST中的内容

也可以直接写 ADD_EXECUTABLE(hello main.cpp)

上述例子可以简化的写成

PROJECT(HELLO)
ADD_EXECUTABLE(hello main.cpp)

注意:工程名的 HELLO 和生成的可执行文件 hello 是没有任何关系的

语法的基本原则

  • 变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名

  • 指令(参数 1 参数 2…) 参数使用括弧括起,参数之间使用空格或分号分开。 以上面的 ADD_EXECUTABLE 指令为例,如果存在另外一个 func.cpp 源文件

    就要写成:ADD_EXECUTABLE(hello main.cpp func.cpp)或者ADD_EXECUTABLE(hello main.cpp;func.cpp)

  • 指令是大小写无关的,参数和变量是大小写相关的。但,推荐你全部使用大写指令

语法注意事项

  • SET(SRC_LIST main.cpp) 可以写成 SET(SRC_LIST “main.cpp”),如果源文件名中含有空格,就必须要加双引号
  • ADD_EXECUTABLE(hello main) 后缀可以不行,他会自动去找.c和.cpp,最好不要这样写,可能会有这两个文件main.cpp和main

2.3 cmake 的内部构建和外部构建

上面的方法,在 hello.cpp 的所在目录下进行 cmake 属于内部构建,但是我们经常看到在 build 中进行构建从而生成那一堆 cmake 文件,那样的方法叫做外部构建

mkdir build
cd build
cmake .. // CMakeLists.txt 在 build 外面

2.4 创建一个 cpp 工程

一个工程中有多个文件,我们要对这所有的文件进行编译,下面是文件的目录

文件保存在 /Users/xuguagua/Documents/C++/mianshi/cmake_demo 中

[root@localhost cmake]# tree
.
├── build
├── CMakeLists.txt
└── src
    ├── CMakeLists.txt
    └── main.cpp

在每一个文件夹下都要创建 CMakeLists.txt,所以在 src 中也要创建一个 CMakeLists.txt 文件

1.在最外层 CMakeLists.txt 写的内容

这里会单独创建一个 bin 目录,即使 bin 中没有创建文件夹也会自动创建,并将可执行文件放入

cmake_minimum_required(VERSION 3.23)
project(cmake_demo)
set(CMAKE_CXX_STANDARD 14)
ADD_SUBDIRECTORY(src bin) # 将最外层的 cmake 关联到 src 的 cmake。bin 文件夹如果不存在则自动创建

2.在 src CMakeLists.txt 中

这个文件只需要指明可执行文件是谁,叫啥

ADD_EXECUTABLE(hello main.cpp) # hello 是生成的可执行软件的名字

2.5 cmake install 方法

将项目生成的库文件、头文件、可执行文件或相关文件等安装到指定位置(系统目录,或发行包目录)。在cmake中,这主要是通过install方法在CMakeLists.txt中配置,make install命令安装相关文件来实现的。

S1:首先我们创建下面的工程目录

├── build
├── CMakeLists.txt
├── COPYRIGHT
├── doc
│   └── hello.txt
├── README
├── runhello.sh
└── src
    ├── CMakeLists.txt
    └── main.cpp
image-20220824223944846

安装文件COPYRIGHT和README

INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)

FILES:文件

DESTINATION:

1、写绝对路径

2、可以写相对路径,相对路径实际路径是:${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>

CMAKE_INSTALL_PREFIX 默认是在 /usr/local/

cmake -DCMAKE_INSTALL_PREFIX=/usr 在cmake的时候指定CMAKE_INSTALL_PREFIX变量的路径

安装脚本runhello.sh

PROGRAMS:非目标文件的可执行程序安装(比如脚本之类)

INSTALL(PROGRAMS runhello.sh DESTINATION bin)

说明:实际安装到的是 /usr/bin

安装 doc 中的 hello.txt

  • 一、是通过在 doc 目录建立CMakeLists.txt ,通过install下的file

  • 二、是直接在工程目录通过

    INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)

DIRECTORY 后面连接的是所在 Source 目录的相对路径

注意:abc 和 abc/有很大的区别

目录名不以/结尾:这个目录将被安装为目标路径下的

目录名以/结尾:将这个目录中的内容安装到目标路径

安装过程

cmake ..
make
sudo make install

最终文件

那么在 CMakeLists.txt 中的显示就是如下

INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/) # 安装这两个文件到这个网址
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)

在执行完安装后相应的路径下就会看到这几个文件

image-20220825063147457

2.6 创建动态库

有时候我们想让自己的库生成 .so 文件或者 .dll 文件,供外面使用,那么我们就要为该 pro 创建一个共享库,步骤如下

文件目录如下:

[root@localhost cmake2]# tree
.
├── build
├── CMakeLists.txt
└── lib
    ├── CMakeLists.txt
    ├── hello.cpp
    └── hello.h

其中 hello.cpp 的内容

#include "hello.h"
#include <iostream>
void HelloFunc(){
    std::cout << "Hello World" << std::endl;
}

hello.h 的内容

#ifndef HELLO_H
#define Hello_H
void HelloFunc();
#endif

然后就在 CMakeLists.txt 文件中放入相应的链接

项目中的cmake内容

PROJECT(HELLO)
ADD_SUBDIRECTORY(lib bin)

lib中CMakeLists.txt中的内容

SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})

ADD_LIBRARY

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})

  • hello:就是正常的库名,生成的名字前面会加上lib,最终产生的文件是libhello.so
  • SHARED,动态库 STATIC,静态库
  • ${LIBHELLO_SRC} :源文件

2.7 同时生成静态库和动态库

S1:在上一步创建好 hello.cpp 文件之后需要同时生成 .so 和 .a 文件

在 lib 的 CMake 中执行指令

SET(LIBHELLO_SRC hello.cpp)

ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
//对hello_static的重名为hello
SET_TARGET_PROPERTIES(hello_static PROPERTIES  OUTPUT_NAME "hello")
//cmake 在构建一个新的target 时,会尝试清理掉其他使用这个名字的库,因为,在构建 libhello.so 时, 就会清理掉 libhello.a
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)

ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello PROPERTIES  OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)


最后生成效果如下图所示,.so 是动态库,.a 是静态库

image-20220825072808421

动态库的版本号

一般动态库都有一个版本号的关联

libhello.so.1.2
libhello.so ->libhello.so.1
libhello.so.1->libhello.so.1.2

CMakeLists.txt 插入如下

SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)

VERSION 指代动态库版本,SOVERSION 指代 API 版本。

S2:将库安装到 /usr 文件夹下

安装共享库和头文件

本例中我们将 hello 的共享库安装到/lib目录,

将 hello.h 安装到/include/hello 目录

//文件放到该目录下
INSTALL(FILES hello.h DESTINATION include/hello)

//二进制,静态库,动态库安装都用TARGETS
//ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执行目标二进制。
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)

注意:

安装的时候,指定一下路径,放到系统下

cmake -DCMAKE_INSTALL_PREFIX=/usr ..

2.8 使用共享库

刚才我们已经生成了 .so 文件和 .a 文件,并放到了 /usr 的目录下,下面就尝试在工程下调用这两个库

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

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

相关文章

excel散点图怎么每个点添加名称

最终效果图&#xff1a; 添加图标元素->数据标签->其他数据标签选项 选择单元格中的值 手动拖动数据标签&#xff0c;调整到合适的位置。

Spring学习笔记:IOC控制反转、AOP面向切面

挺快的&#xff0c;框架这一部分 文章目录 一、Spring概述入门案例导入依赖包在src下写配置文件创建普通类和测试类 二、IOC&#xff08;控制反转&#xff09;2.1 IOC bean 的XML操作&#xff08;创建对象&#xff0c;注入属性2.2 IOC bean 的 注解 操作 三、AOP&#xff08;面…

《UE5_C++多人TPS完整教程》学习笔记31 ——《P32 角色移动(Character Movement)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P32 角色移动&#xff08;Character Movement&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&#xff08;也是译者&…

用国内版Devin:DevOpsGPT开发一个简易官网

前言&#xff1a; 世界上第一个AI程序员Devin想必已经给大家带来了不小的震撼&#xff0c;这种L4级的技术也许已经昭示着AGI离我们或许真的不远了。 这里先给大家普及一个概念&#xff1a; L4是谷歌对AGI划分的第四个等级&#xff0c;把代码丢给 AI 改这个是 L1 或者 L2 级别的…

CentOS7安装Docker及禅道

https://blog.csdn.net/weixin_46453070/article/details/136183615?ops_request_misc%257B%2522request%255Fid%2522%253A%2522171246925816800222886233%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id171246925816800222886233&biz_i…

基于RTThread的学习(三):正点原子潘多拉 QSPI 通信 W25Q128 实验

1、基于芯片创建工程 2、QSPI配置 2.1、RTThing_setting 设置组件 2.2、配置board.h 文件 2.3、cubemx生成QSPI的硬件初始化代码&#xff1b;HAL_QSPI_MapInit; 这里注意&#xff1a;你所买的开发板对应的qspi 连接的是否是cubemx 上边显示的&#xff0c;如果不是你需要将引脚…

Spring Security——13,认证成功失败注销成功处理器

认证成功&&失败&&注销成功处理器 说明&#xff1a;一、认证成功处理器1.1 自定义成功处理器1.2 配置自定义成功处理器 二、认证失败处理器2.1 自定义失败处理器2.2 配置自定义失败处理器 三、登出成功处理器3.1 自定义登出处理器3.2 配置登出处理器 四、完结撒…

聊一聊,JMeter分布式性能测试!

在做后端服务器性能测试中&#xff0c;我们会经常听到’分布式’。但你是否了解分布式呢&#xff1f;今天&#xff0c;我们就来给大家讲讲&#xff0c;在企业实战中&#xff0c;如何使用分布式进行性能测试&#xff0c;实战过程中&#xff0c;又有哪些地方要特别注意&#xff1…

Inotify

一、关于Inotify linux内核的inotify机制 可以监测文件系统的变动情况&#xff0c;并做出通知响应 二、关于inotify 使用inotify通知接口&#xff0c;可以用来监控文件系统的各种变化情况&#xff0c;可以非常方便地实现文件异动告警、增量备份&#xff0c;并针对目录或文件的…

transformer上手(1) —— transformer介绍

1 起源与发展 2017 年 Google 在《Attention Is All You Need》中提出了 Transformer 结构用于序列标注&#xff0c;在翻译任务上超过了之前最优秀的循环神经网络模型&#xff1b;与此同时&#xff0c;Fast AI 在《Universal Language Model Fine-tuning for Text Classificat…

烤羊肉串引来的思考--命令模式

1.1 吃羊肉串&#xff01; 烧烤摊旁边等着拿肉串的人七嘴八舌地叫开了。场面有些混乱&#xff0c;由于人实在太多&#xff0c;烤羊肉串的老板已经分不清谁是谁&#xff0c;造成分发错误&#xff0c;收钱错误&#xff0c;烤肉质量不过关等。 外面打游击烤羊肉串和这种开门店做烤…

Windows系统下安装java开发环境所需的JDK开发工具包

目录 一、JDK开发工具包下载二、安装三、环境变量配置3.1 添加安装包路径3.2 添加lib路径3.3 添加bin目录 四、检查是否安装成功五、总结 一、JDK开发工具包下载 官网地址&#xff1a;JDK下载 打开网址后有多个版本的JDK&#xff0c;学者根据自己电脑需求选择对应版本下载。如…

类,构造,this,static

第1关&#xff1a;什么是类&#xff0c;如何创建类 100 任务要求参考答案 任务描述相关知识 什么是类怎么定义类创建对象并且使用对象的属性和方法编程要求测试说明 任务描述 本关任务&#xff1a;创建一个类和一个对象&#xff0c;调用这个对象的属性和方法。 相关知识 …

34-5 CSRF漏洞 - CSRF分类

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 1)GET 类型 传参: 参数连接在URL后面 POC构造及执行流程: 构造URL,诱导受害者访问点击利用利用标签进行攻击: 构造虚假URL,在链接上添加payload抓包获取数据包,通过CSRF POC…

解决Xshell连接Linux虚拟机速度慢问题

我们频繁更换网络环境时&#xff0c;可能会发现xshell连接Linux虚拟机的速度变得很慢 为什么呢&#xff1f; 因为ssh的服务端在连接时会自动检测dns环境是否一致导致的 我们把它修改为不检测即可 修改文件位置&#xff1a; vi /etc/ssh/sshd_config 把 #UseDNS yes 修改…

Redis7(二)数据类型及其用法

一、概述 命令不区分大小写&#xff0c;key区分大小写 数据类型针对value String List Set Hash ZSet bitmap GEO HyperLogLog Stream bitfield 二、String <K,V> 1、设值/取值 getrange key index1 index2 getrange key 0 -1//获取所有的值 SETRANGE KEY_N…

代码随想录第34天| 1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果

1005.K次取反后最大化的数组和 1005. K 次取反后最大化的数组和 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 贪心算法&#xff0c;这不就是常识&#xff1f;还能叫贪心&#xff1f;LeetCode&#xff1a;1005.K次取反后最大化的数组和_哔哩哔…

如何选择苹果iOS系统的企业签名分发平台

哈喽&#xff0c;大家好呀&#xff0c;淼淼有和大家见面啦&#xff0c;前两期讲了分发内测的一些相关知识&#xff0c;这一期咱们来聊聊企业签名分发平台的相关知识。最近移动应用市场的竞争一天比一天要激烈&#xff0c;许多做开发的小伙伴们都在为此发愁&#xff0c;愁着该怎…

不要再使用 @Builder 注解了!有深坑呀!

曾经&#xff0c;我在《千万不要再随便使用 lombok 的 Builder 了&#xff01;》 一文中提到 Builder 注解的其中一个大坑会导致默认值失效&#xff01; 最近阅读了 《Oh !! Stop using Builder》 发现 Builder 的问题还不止一个&#xff0c;Builder 会让人误以为是遵循构建器…

java的封装

在Java中&#xff0c;封装是面向对象编程中的一种重要概念&#xff0c;它指的是将数据和方法打包在一个单一的单位&#xff08;类&#xff09;中&#xff0c;并对外部隐藏对象的内部细节。封装通过将类的成员变量声明为私有的&#xff0c;并提供公共的方法来访问和修改这些变量…