3.C++ Make

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.txtPROJECT (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.mzph.cn/news/800862.shtml

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

相关文章

Java-Tomcat

一、web补充技术 ①&#xff1a;B/S架构 主流的方式&#xff0c;只要有浏览器即可。编程方式直接基于socket即可 ②&#xff1a;javascript 简称js&#xff0c;早期只是实现在客户端的浏览器的动态效果&#xff0c;但服务端不会解释运行&#xff0c;所以本质上是静态资源。 …

UE4_动画基础_角色的缩放

以第三人称模板进行制作。 一、首先为角色缩放新建粒子效果 1、新建niagara system&#xff0c;重命名为NS_Shrink。 2、双击打开设置参数&#xff1a; 发射器重命名&#xff1a; Emitter State&#xff1a; 发射器一次喷发数量&#xff1a; 粒子初始大小&#xff0c;生命周…

Go协程池gopool源码解析

1、gopool简介 Repository&#xff1a;https://github.com/bytedance/gopkg/tree/develop/util/gopool gopool is a high-performance goroutine pool which aims to reuse goroutines and limit the number of goroutines. It is an alternative to the go keyword. gopool的…

【Linux进阶之路】地址篇

文章目录 一、ipv4地址1. 基本概念2. 分类3.CIDR4.特殊的ip地址 二、IP协议1. 协议字段2.分片与重组3.路由 三、NAT技术1.公有和私有2.NAT3.NAPT 四、ARP协议1.MAC地址2.ARP 五、DHCP协议六、DNS协议尾序 一、ipv4地址 1. 基本概念 概念&#xff1a;IP地址&#xff0c;英文全…

从零自制docker-8-【构建实现run命令的容器】

文章目录 log "github.com/sirupsen/logrus"args...go moduleimport第三方包失败package和 go import的导入go build . 和go runcli库log.SetFormatter(&log.JSONFormatter{})error和nil的关系cmd.Wait()和cmd.Start()arg……context.Args().Get(0)syscall.Exec和…

【Leetcode每日一题】 递归 - 验证二叉搜索树(难度⭐⭐)(53)

1. 题目解析 题目链接&#xff1a;98. 验证二叉搜索树 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 中序遍历是二叉树遍历中的一种重要方式&#xff0c;它按照左子树、根节点、右子树的顺序访问每个节点。这种方式…

2024mathorcup妈妈杯数学建模A题思路模型

2024mathorcup妈妈杯数学建模A题思路模型&#xff1a;比赛开始后第一时间更新&#xff0c;更新见文末名片&#xff0c;下面对2022年B题进行介绍&#xff1a; 2022Mathorcup B题题目介绍 ​ B题无人仓的搬运机器人调度问题本题考在无人仓内的仓库管理问题之一&#xff0c;搬运机…

机场数据治理系列介绍(5)民用机场智慧能源系统评价体系设计

目录 一、背景 二、体系设计 1、评价体系设计维度 2、评价体系相关约定 3、评价指标体系框架设计 4、能源利用评价指标 5、环境友好评价指标 6、智慧管控评价指标 7、安全保障评价指标 三、具体落地措施 一、背景 在“双碳”国策之下&#xff0c;各类机场将能源系统建…

深入解析template,掌握C++模板的精髓!

掌握C模板&#xff08;template&#xff09;的优雅之道&#xff01; 一、什么是模板&#xff1f;二、模板如何工作&#xff1f;三、C 中的模板类型3.1、 类模板3.2、 函数模板 四、模板参数推导4.1、模板参数推导示例4.2、函数模板参数推导4.3、类模板参数推导&#xff08;C17 …

2024年MathorCup妈妈杯数学建模思路C题思路解析+参考成品

1 赛题思路 (赛题出来以后第一时间在群内分享&#xff0c;点击下方群名片即可加群) 2 比赛日期和时间 报名截止时间&#xff1a;2024年4月11日&#xff08;周四&#xff09;12:00 比赛开始时间&#xff1a;2024年4月12日&#xff08;周五&#xff09;8:00 比赛结束时间&…

GPU部署ChatGLM3

首先&#xff0c;检查一下自己的电脑有没有CUDA环境&#xff0c;没有的话&#xff0c;去安装一个。我的电脑是4060显卡&#xff0c;买回来就自带这些环境了。没有显卡的话&#xff0c;也不要紧&#xff0c;这个懒人安装包支持CPU运行&#xff0c;会自动识别没有GPU&#xff0c;…

启明智显M4核心板驱动17寸屏 为您打造无与伦比的视觉盛宴

近日&#xff0c;启明智显推出M4核心板驱动17寸屏&#xff0c;8 Link LVDS接口下1280*1024分辨率为用户展现了超强的视觉体验。 M4核心板采用纯国产架构&#xff0c;内置了16位DDR内存&#xff0c;为设备提供强大的数据处理能力和高效的运行速度。无论是处理复杂的任务还是进…

【简单讲解下C++max函数的使用】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

Java | Leetcode Java题解之第13题罗马数字转整数

题目&#xff1a; 题解&#xff1a; class Solution {Map<Character, Integer> symbolValues new HashMap<Character, Integer>() {{put(I, 1);put(V, 5);put(X, 10);put(L, 50);put(C, 100);put(D, 500);put(M, 1000);}};public int romanToInt(String s) {int …

考研数学|刷题用汤家凤《1800》还是张宇《1000》?看完这篇你就懂了

考研数学的复习是一个系统的过程&#xff0c;不同的习题集有各自的特点和适用场景。汤家凤的1800题和张宇的1000题都是非常受欢迎的考研数学复习资料&#xff0c;它们各有侧重点和优势。 汤家凤的1800题以其全面性和基础性著称&#xff0c;题目覆盖了考研数学的各个知识点&…

缓存穿透问题

缓存穿透 &#xff1a;缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远不会生效&#xff0c;这些请求都会打到数据库。 常见的两种解决方案&#xff1a; 1.缓存空对象 优点&#xff1a;实现简单&#xff0c;维护方便 缺点&#xff1a;占用…

【40分钟速成智能风控4】传统风险管理体系

目录 人工审核 纸质材料评估 电话回访 线下走访尽调 专家模型 业务规则库 专家调查权重法 熵权法 随着大数据和机器学习技术的发展与成熟&#xff0c;智能风控已经逐步取代传统风控&#xff0c;成为国内互联网金融机构主流的风险管理模式。一方面&#xff0c;传统风控是…

U盘中病毒了会影响电脑吗 U盘中病毒了怎么恢复数据 easyrecovery数据恢复软件免费版 easyrecovery绿色版破解版激活密钥无需注册

EasyRecovery是世界著名数据恢复公司 Ontrack 的技术杰作&#xff0c;EasyRecovery破解版是一个威力非常强大的硬盘数据恢复工具&#xff0c;能够帮你恢复丢失的数据以及重建文件系统。您只需要按软件提示一步一步操作&#xff0c;就能恢复出你电脑上的文档、表格、图片、音频、…

LeetCode 热题 100 | 多维动态规划(一)

目录 1 多维动态规划 2 62. 不同路径 3 64. 最小路径和 菜鸟做题&#xff0c;语言是 C&#xff08;细品动态规划 ing&#xff09; 1 多维动态规划 目前的感觉&#xff1a;抽象为二维数组。 2 62. 不同路径 题眼&#xff1a;“机器人每次只能向下或者向右移动一步”。…

什么是sso?

SSO&#xff08;Single Sign-On&#xff09;&#xff0c;即单点登录&#xff0c;是一种安全协议&#xff0c;它允许用户在多个应用程序之间使用同一组登录凭据进行身份验证。这意味着用户只需要登录一次&#xff0c;就可以访问多个需要身份验证的应用程序。 SSO的工作原理如下…