Makefile及cmake学习

news2024/10/7 10:23:20

Makefile及cmake学习

  • 1. g++,gcc以及cpp的区别
  • 2. Makefile
    • 2.1 介绍一个例子
    • 2.2 避免头文件重复包含的方法
      • 2.2.1 宏定义
      • 2.2.2 #pargma once
    • 2.3 使用Makefile编译文件
      • 2.3.1 手动编译
      • 2.3.2 Makefile编译-版本1
      • 2.3.3 Makefile编译-版本2
      • 2.3.4 Makefile编译-版本3
      • 2.3.5 Makefile编译-版本4
    • 2.4 使用CMake编译

1. g++,gcc以及cpp的区别

有文章将这几个之间的关系理清了,这里直接参考:原文链接。

2. Makefile

下面来学习一下常见的Makefile文件的编写。参考来源【b站于仕琪】

2.1 介绍一个例子

下面的例子是用来介绍基本的Makefile文件的编写:

hello/
├──main.cpp
├──factorial.cpp
├──printhello.cpp
└──functions.h

main.cpp的定义如下:

#include <iostream>
#include "functions.h"
using namespace std;

int main () {
    printhello();
    cout << "This is main:" << endl;
    cout << "The result of factorial(5) is :" << factorial(5) << endl;
    return 0;
}

factorial.cpp定义:

#include "functions.h"

int factorial(int n) {
    if (n == 1) return 1;
    else return n * factorial(n - 1);
}

printhello.cpp定义:

#include <iostream>
#include "functions.h"
using namespace std;

void printhello() {
	int i;
    cout << "Hello World!" << endl;
}

functions.h定义:

#ifndef _FUNCTIONS_H_
#define _FUNCTIONS_H_
void printhello();
int factorial(int n);
#endif

上面的代码中,使用了宏定义防止头文件重复包含,以前没有系统总结过,下面总结一下。

2.2 避免头文件重复包含的方法

2.2.1 宏定义

上面的例子中,我们使用了宏定义方法防止头文件重复包含,具体而言:
在main.cpp中我们首先定义了#define _FUNCTIONS_H_,然后在functions.h中有如下语句:

#ifndef _FUNCTIONS_H_
#define _FUNCTIONS_H_
...
#endif

这是宏定义避免头文件的模板,其中ifndef的意思是:

如果这个宏定义了,就不会执行下面的语句

这样的话,如果因为某种原因,第二次调用了头文件,那么由于第一次已经定义了这个宏,第二次判断这个宏已经存在了,所以就不会执行下面的语句了,也就避免了头文件的重复包含。

2.2.2 #pargma once

宏定于有时候可能会出现变量名冲突,另一种方案就是在头文件中使用#pargma once

//.h文件
#pargma once
...

这个方法更简洁,但是老的编译器可能不支持。

2.3 使用Makefile编译文件

2.3.1 手动编译

对于上述三个文件,我们也可以不使用Makefile进行编译,编译的方法如下:

g++ main.cpp factorial.cpp printhello.cpp -o main

结果:
在这里插入图片描述
使用手动的方法是完全可以的,但是当项目的体量很大,文件很多的时候,每次改动一个文件,就需要对所有的文件编译,非常耗时。手动编译的方法还有一种:先只编译不链接,然后再将所有的编译文件链接到一起:

g++ main.cpp -c
g++ factorial.cpp -c
g++ printhello.cpp -c

使用了上述的语句,就会生成.o的文件:
在这里插入图片描述
然后再链接到一起:

g++ *.o -o main

这个语句就是把所有的.o文件链接到一起,生成编译文件main。以上就是两种手动编译的方法,虽然有缺点,但是使用Makefile进行编译本质上就是把上面的语句写成了脚本而已,所以上面的两种方法就是基础。

2.3.2 Makefile编译-版本1

首先需要创建一个Makefile文件,使用下面的指令

touch Makefile

创建的文件名字最好就是Makefile,这样就可以直接使用make指令,如果是其他的名字,使用make的指令的时候还要指定文件名。下面就是使用Makefile的第一个版本:

main: main.cpp factorial.cpp printhello.cpp
	g++ -o main main.cpp printhello.cpp factorial.cpp

有几个需要注意的地方:

  • 第一句就表示main依赖于这些源文件,main就是目标
  • 第二句开头一定有一个TAB符号,第二行就是链接,来生成这个目标

写好了Makefile之后,就可以编译了。

make
// 如果文件的名字不是Makefile,就需要指定文件的名字
make -f 文件名

到这里看来,版本一的Makefile除了一开始就把几个文件的名字写好了,与手动的并没有什么区别。
其实不然,还是有一点区别的。我们使用Makefile进行编译,如果目标生成的时间比依赖的源文件生成的时间都新的话(就是说文件没有更改过),此时我们make的话,就会提示我们,这是最新的目标,就不会重复编译了。但是这个版本并没有很只能,如果文件太多了,更改一点点,每次都需要重新编译所有,很费时间

2.3.3 Makefile编译-版本2

CXX = g++
TARGET = main
OBJ = main.o factorial.o printhello.o 

$(TARGET): $(OBJ)
	$(CXX) -o $(TARGET) $(OBJ)

main.o: main.cpp
	$(CXX) -c main.cpp

factorial.o: factorial.cpp
	$(CXX) -c factorial.cpp

printhello.o: printhello.cpp
	$(CXX) -c printhello.cpp

这个其实看着复杂,整体的思路就是使用手动编译的第二个思路,先编译不链接,生成.o文件,然后链接。
首先CXX就是编译的方法,这里使用的g++,OBJ就是依赖,跟版本1不同的是,这里的依赖使用的是.o文件,而不是.cpp文件。
紧接着:

$(TARGET): $(OBJ)
	$(CXX) -o $(TARGET) $(OBJ)

这一句其实拆解开来,就是版本1的小小变动的版本了,这里的依赖就变成了.o文件。所以我们接下来就要得到.o文件,这里对应的就是手动编译的第二种方法了。
使用版本二可以继承版本一的优点,同时如果只有1个文件修改了,我们不用再编译所有的文件了,只需要编译修改的文件,大大提高了编译的效率。但面临很多文件的时候,还是需要写很多语句,非常麻烦,下面的版本三就用来解决这个问题。

2.3.4 Makefile编译-版本3

CXX = g++
TARGET = main
OBJ = main.o factorial.o printhello.o 

CXXFLAGS = -c -Wall

$(TARGET): $(OBJ)
	$(CXX) -o $@ $^

%.o: %.cpp
	$(CXX) $(CXXFLAGS) $< -o $@
	
.PHONY: clean
clean:
	rm -f *.o $(TARGET)

版本三前面和版本二是一样的,我们定义了一个变量CXXFLAGS表示g++之后的选项,这里我们增加了一个-Wall,表示在编译的时候把所有的警告也写出来。编译的代码:

$(TARGET): $(OBJ)
	$(CXX) -o $@ $^

的第一行其实就是main: main.o factorial.o printhello.o,第二行出现了$@以及$^,在Makefile文件中,$@就代表目标,也就是分号之前的内容,$^代表依赖列表中所有,具体而言,这里就是三个.o文件,下面还将介绍一个$<,这个表示依赖的第一个。很明显,如果依赖只有一个的话,二者是等价的。
所以上面的代码翻译古来就是下面的意思:

main: main.o factorial.o printhello.o
	g++ -o main.o factorial.o printhello.o

其实就非常好理解了。
下面解释:

%.o: %.cpp
	$(CXX) $(CXXFLAGS) $< -o $@

这个就是将每个cpp文件作为依赖,生成对应名称的.o文件,使用%来替代文件名。下面的$<在上面已经解释了,其实就是%.cpp。所以这句话翻译出来就是:g++ -c -Wall main.cpp -o main.o,此时就可以生成.o文件了。但是根据之前的经验,生成.o文件只需要gcc -c main.cpp即可,后面的要不要都无所谓,所以上面的内容可以简化为:

%.o: %.cpp
	$(CXX) $(CXXFLAGS) $<

最后介绍:

.PHONY: clean
clean:
	rm -f *.o $(TARGET)

我们先不关注第一句话,直接看第二句,第二句的目标是clean,没有依赖,执行的任务是删除全部的.o文件以及之前生成的目标。但是如果文件中正好有clean这个文件,会出现什么问题呢?我们在之前讲过,如果目标文件比依赖都晚的话,就不会再编译目标文件了,所以就会提示:clean已经是最新,这里就达不到我们的要求了。针对这个问题,我们使用了.PHONY: clean,其中.PHONY是伪目标,这个目标永远不会存在,即使你这一次编译了,下一次他还是不存在。我们这里可以这么理解:.PHONY将clean标记为伪目标,此时make clean的时候就不管有没有文件存在了,每一次都会执行。如果我们要使用make对某个目标编译的话,可以直接make 目标

下面一些运行的结果:
在这里插入图片描述
使用了-Wall将这个警告揪出来了。

在这里插入图片描述
目录里面存在clean文件,我们此时使用make clean:
在这里插入图片描述
可以看到,所有的.o文件以及main目标都被删除了。
到这里,我们已经完成的差不多了,编译也比较简单。但是再思考一下,如果我们换一个工程,岂不是还需要依赖,还是有一点麻烦的,我们可以再改进一下。

2.3.5 Makefile编译-版本4

CXX = g++
TARGET = main
SRC = $(wildcard *.cpp)
OBJ = $(patsubst %.cpp, %.o, $(SRC))

CXXFLAGS = -c -Wall

$(TARGET): $(OBJ)
	$(CXX) -o $@ $^

%.o: %.cpp
	$(CXX) $(CXXFLAGS) $^

.PHONY: clean
clean:
	rm -f *.o $(TARGET)

版本四就是我们的终极版本,SRC就是当前路径下所有的cpp文件名,OBJ就是将cpp文件名改为.o文件,后面的就和版本三是一样的了。有个这个版本,即使我们的代码换了个工程,照样也是可以编译的。
以上就是使用Makefile文件对cpp工程进行编译的方法,如果要对c工程进行编译,上面的g++换成gcc即可。

2.4 使用CMake编译

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

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

相关文章

【三维几何学习】网格简化-ModelNet10

网格简化-ModelNet10 引言一、网格的简化1.1 水密网格的简化可视化1.2 非水密网格的简化可视化1.3 核心代码 二、ModelNet10数据集简化三、展望 引言 计算机算力有限&#xff0c;特别是在深度学习领域&#xff0c;撇开网格的输入特征计算&#xff0c;现有条件很难直接训练测试…

JS逆向 -- 某联盟登录密码分析

一、输入账号密码 账号&#xff1a;15836353612 密码&#xff1a;123456 二、F12打开开发者工具&#xff0c;抓包分析&#xff0c;password被加密提交了 三、全局搜索password&#xff0c;定位到关键JS文件&#xff0c;下断调试 四、断下来后&#xff0c;查看formDate的值&…

AspNetCore中的中间件详解【超详细】

1 什么叫做中间件&#xff1f; ASP.NET Core处理请求的方式看做是一个管道&#xff0c;中间件是组装到应用程序管道中用来处理请求和响应的组件。通常是一个可重用的类方法 每个中间件可以&#xff1a; &#xff08;1&#xff09;选择是否将请求传递给管道中的下一个组件。 &a…

第一行代码 第七章 内容提供器

第七章 内容提供器 在上一章中我们学了Android数据持久化的技术&#xff0c;包括文件存储、SharedPreferences存储以及数据库存储。使用这些持久化技术所保存的数据都只能在当前应用程序中访问。 虽然文件和SharedPreferences存储中提供了MODE_WORLD_READABLE和MODE_WORLD_WR…

UU跑腿“跑男失联”:同城即配服务赛道商业逆袭难?

五一假期&#xff0c;人们纷纷走出家门&#xff0c;要么扎堆奔向“远方”&#xff0c;要么、享受本地烟火气息。 据文化和旅游部数据中心测算&#xff0c;劳动节假期&#xff0c;全国国内旅游出游合计2.74亿人次&#xff0c;同比增长70.83%。 五一假日的郑州东站 面对人山人海…

树莓派(主)与STM32(从)使用SPI通信

1.实验目的 2.SPI 简介 SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外设接口&#xff09;是Motorola公司提出的一种同步串行数据传输标准 2.1 接口 SPI接口经常被称为4线串行总线&#xff0c;以主/从方式工作&#xff0c;数据传输过程由主机初始化。如图1…

【干货集】PCBA板边器件布局重要性

电子元器件在PCB板上的合理布局&#xff0c;是减少焊接缺点的极重要一环&#xff01;元器件要尽可能避开挠度值非常大的区域和高内应力区&#xff0c;布局应尽量匀称。 为了最大程度的利用电路板空间&#xff0c;相信很多做设计的小伙伴&#xff0c;会尽可能把元器件靠板的边缘…

机器学习基础知识之数据归一化

文章目录 归一化的原因1、最大最小归一化2、Z-score标准化3、不同方法的应用 归一化的原因 在进行机器学习训练时&#xff0c;通常一个数据集中包含多个不同的特征&#xff0c;例如在土壤重金属数据集中&#xff0c;每一个样本代表一个采样点&#xff0c;其包含的特征有经度、…

《程序员面试金典(第6版)》面试题 16.16. 部分排序(double双指针(多指针),C++)

题目描述 给定一个整数数组&#xff0c;编写一个函数&#xff0c;找出索引m和n&#xff0c;只要将索引区间[m,n]的元素排好序&#xff0c;整个数组就是有序的。注意&#xff1a;n-m尽量最小&#xff0c;也就是说&#xff0c;找出符合条件的最短序列。函数返回值为[m,n]&#xf…

什么是平台工程?如何开始?

平台工程是为开发人员构建和维护自助服务平台的学科。该平台提供了一套云原生工具和服务&#xff0c;帮助开发者快速高效地交付应用。平台工程的目标是通过标准化和自动化软件交付生命周期 (SDLC) 中的大部分任务来改善开发人员体验 (DX)。开发人员可以专注于使用自动化平台编码…

Type-C PD充电器诱骗PD+QC+AFC+FCP全协议快充取电5V9V12V15V20V

Type-C充电器采用的是PD快充协议&#xff0c;支持的电压高&#xff0c;电流大&#xff0c;一般有5V3A、9V3A、12V3A、15V3A、20V5A等等。 因为充电器内部有协议芯片&#xff0c;当外部设备连接时&#xff0c;设备会和充电器进行协议匹配&#xff0c;匹配成功之后&#xff0c;充…

ASEMI代理ADI亚德诺LT8609AJDDM#WTRPBF车规级芯片

编辑-Z LT8609AJDDM#WTRPBF特点&#xff1a; 宽输入电压范围&#xff1a;3.0V 至 42V 超低静态电流突发模式操作&#xff1a; 将 12VIN 调节到 3.3VOUT 时 IQ 为 2.5A 输出纹波 < 10mVP-P 高效 2MHz 同步操作&#xff1a; 1A 时效率为 93%, 12VIN 可获得 5VOUT 最大…

3.1 一个稍微完善的Vue.js响应式系统

前文提要&#xff1a;3.0 响应式系统的设计与实现 1、设置一个合理的effect副作用函数 如上文所说&#xff0c;如果我们直接将简单的effect函数作为副作用函数&#xff0c;如果一个副作用函数不叫effect岂不是找不到了。解决方案也很简单&#xff0c;我们设定一个全局变量用于…

在CRA中配置别名路径并添加别名路径提示

写在前面&#xff1a; 使用React官方脚手架create-react-app[简称CRA]创建react项目&#xff1a;npx create-react-app 项目名称 一、配置别名路径 1.1 写在前面 目的&#xff1a;简化项目中的路径处理&#xff0c;和Vue项目中的类似。 参考文档&#xff1a;自定义CRA的默认…

MySQL基础(十二)数据类型精讲

1. MySQL中的数据类型 类型类型举例整数类型TINYINT、SMALLINT、MEDIUMINT、INT(或INTEGER)、BIGINT浮点类型FLOAT、DOUBLE定点数类型DECIMAL位类型BIT日期时间类型YEAR、TIME、DATE、DATETIME、TIMESTAMP文本字符串类型CHAR、VARCHAR、TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT枚…

一个日期类深度认识operator符号重载

一&#xff1a;概念 在以前的C语言的学习中,如果我们需要比较两个整数并返回它的结果可以直接用与之相关的符号。例如我们可以直接写成A>B或者A<B一类的&#xff0c;但是它的局限性很大&#xff0c;只能比较内置类型&#xff0c;因为计算可以直接转换成对应的汇编代码进…

如何通过国外主机租用服务提高网站SEO排名?

当今的互联网已经成为了商业和社交活动的主要场所之一。在这个快速变化的数字时代&#xff0c;网站的搜索引擎优化(SEO)排名对于任何企业的成功都至关重要。一个好的SEO排名能够帮助企业吸引更多的访客和潜在客户&#xff0c;增加业务的转化率。而国外主机租用服务可以帮助您优…

【C++学习】函数模板

模板的概念 模板就是建立通用的模具&#xff0c;大大提高复用性。 模板的特点&#xff1a; 模板不可以直接使用&#xff0c;它只是一个模型 模板的通用不是万能的 基本语法 C中提供两种模板机制&#xff1a;函数模板和类模板 函数模板作用&#xff1a; 建立一个通用函数&…

C++学习day--05 C++数据类型

1、项目需求&#xff1a;实现黑客攻击系统菜单打印 实现&#xff1a; #include <iostream> #include <Windows.h> int main( void ) { std::cout << "1. 网站 404 攻击 " << std::endl; std::cout << "2. 网站篡改攻击 …

实验四 基于PPTP的远程VPN实现【网络安全】

实验四 基于PPTP的远程VPN实现【网络安全】 前言推荐实验四 基于PPTP的远程VPN实现使用&#xff1a;配置CentOS PPTP服务端配置CentOS PPTP客户端常见问题浏览器无法打开网页 最后 前言 2023-5-7 23:10:12 以下内容源自《【网络安全】》 仅供学习交流使用 推荐 第27节 远程…