从0开始教你编写Makefile文件

article/2025/9/26 21:17:32

目录

编写Makefile第一步,了解什么是Makefile?

编写Makefile第二步,明白编译链接过程

编写Makefile第三步,实现简单的Makefile(生成单个可执行文件)

解释一下一个简简单单的make都干了什么?

 编写Makefile第四步,编写生成多个可执行文件的Makefile文件

编写Makefile第五步,Makefile的进阶(格式优化)


编写Makefile第一步,了解什么是Makefile?

        每当我们写大型项目时,一般需要很多源文件,源文件会在不同的目录中的文件夹里面包含着,这样我们所有的源文件不会在一个文件中包含,用gcc -o main 所有的.c文件来编译,就很麻烦了,你需要记住所有的.c文件,那么为了方便编译链接,makefile就此诞生。

        在学习Makefile之前,首先介绍一下make命令,make命令是GNU的工程化编译工具,它用于编译大量互相关联的源代码,使用它可以实现项目的工程化管理,提高开发效率。那么对于一个项目,该如何让它按照我们预想的规则去编译链接执行呢?这就要用到我们要学习的Makefile了。Makefile的作用就是在执行make命令的时候指定编译和链接的规则,包括源代码文件之间的链接关系、依赖关系等。它关系到整个项目工程的编译规则:哪些文件需要先编译,哪些要后编译,哪些需要重新编译等复杂的操作。Makefile文件就像shell脚本一样,在其中也可以执行操作系统的命令。

Makefile好处:

  • 自动化编译,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
  • 每次只会编译修改的和依赖于修改的那些文件,提高了编译效率。

往期基础回顾: Linux常用命令及操作演示(细节满满)linux命令演示 

编写Makefile第二步,明白编译链接过程

对于程序的编译,无论是哪种编译器,首先都需要将源代码文件编译成中间代码(Win下.obj文件,UNIX/LINUX下.o文件),即Object文件,然后再将大量的Object文件链接在一起进行执行。

在编译时,编译器检查程序代码中语法的正确性,函数以及变量的声明是否正确,以及进行预处理(例如头文件的所在位置以及宏替换等),此步骤只要语法没有问题,编译就不会出错,便可以生成中间目标文件。

在链接时,主要是链接函数与全局变量,链接器不用管函数在哪一个源文件当中,它关心的是函数所在的中间目标文件。大多数情况下,编译生成的中间目标文件比较多,在链接时需要明确地指出中间目标名,这对于编译很不方便,解决方法是给中间目标文件打包生成一个库文件(Win下是.lib,UNIX/LINUX是.a)。

综上,编译链接的过程是:

  • 首先源文件会讲过编译阶段生成中间目标文件,再由中间目标文件生成执行文件。
  • 编译时编译器只检测程序的语法、函数,变量是否声明以及一些预处理。如果函数只是声明但是未实现则编译器只会警告(不是所有的编译器都是这样),仍然可以生成中间目标文件。
  • 在链接程序时,链接器会在所有的中间目标文件中寻找函数的实现,如果找不到,那就会提示链接错误。

编写Makefile第三步,实现简单的Makefile(生成单个可执行文件)

明白Makefile文件的实现思想:就是把你需要的目标文件,.o文件,.c文件如何实现的规则(指令)按照格式写进去就可以。

(一)前提:

  所有文件均在统一目录下。

(二)格式规则:

target ... : prerequisites ...command......
  • target也就是一个目标文件,可以是Object 文件,也可以是执行文件。还可以是一个标签(Label)。
  • prerequisites就是,要生成那个target所需要的文件或是目标。
  • command也就是make需要执行的命令。(任意的Shell命令)
    这个规则可以这么看,目标文件target的生成需要依赖prerequisites中的一些文件,而target文件的生成规则是在command中定义的。

下面我进行更为通俗的解释:

1、首先对可执行文件的生成建立规则

第一行   最后要生成的可执行文件名:需要依赖的文件(.o)
第二行	(Tab键)生成可执行文件的指令(gcc -o main main.o ……)

2、然后对.o文件的生成建立规则

第三行   .o文件:需要依赖的文件(.c)
第四行	(Tab键)生成.o文件的指令(gcc -c main.c)

如果还有.o文件继续重复3,4行,只需改变文件名即可。

我们最后只需要一个可执行文件,所以中间的.o文件我们不需要,可以把他们删了,下面这个标识就是用来干这个事情的。

第n行    clean:(一个标识,需要手动调)
第n+1行  rm *.o  main(删除.o,main文件)

(三)简单的示例:

我们写三个方法文件(add.c,sub.c,max.c)一个头文件(my_head.h),一个主函数文件(main.c)。
三个方法分别为加、减、返回两个数的最大值

add.c

#include"my_head.h"int add(int x,int y)
{return x+y;
}

sub.c

#include"my_head.h"int sub(int x,int y)
{return x-y;
}

max.c

#include"my_head.h"int max(int x,int y)
{return x>y?x:y;
}

my_head.h

int add(int x,int y);
int sub(int x,int y);
int max(int x,int y);

main.c

#include<stdio.h>
#include"my_head.h"int main()
{int a=10,b=5;printf("a+b=%d\n",add(a,b));printf("a-b=%d\n",sub(a,b));printf("max=%d\n",max(a,b));}

完成准备工作后,正常我们使用gcc会这样编译多文件

gcc -o main  main.c add.c  sub.c  max.c

然而一旦任何一个.c文件改变,那么就需要重新全部编译,不能只编译改变的.c文件,这就是很大的弊端。下面我们根据上面的Makefile规则进行编写,看看Makefile如何处理:

main:main.o add.o sub.o max.o//main生成所需要的.o文件gcc -o main main.o add.o sub.o max.o //生成main的规则
main.o:main.c //mian.o文件生成所需要的mian.c文件(程序员写的,所以不用声明它的规则)gcc -c main.c
add.o:add.cgcc -c add.c
sub.o:sub.cgcc -c sub.c
max.o:max.cgcc -c max.c
clean:      //需要手动调用rm *.o main

完成后我们make,程序就会自动运行,产生相对应的可执行文件

正常运行./main即可

我们进行ls显示就会发现目录中存在一些我们不需要的.o文件

 这时我们在编写Makefile最后一句clean的作用就体现了,需要我们手动调用,删除.o文件 因为我上面在.o文件 后面还写了main 说明也要删除可执行文件main  ,如果光删.o 就不用写那么main了

clean的调用: 需要我们手动调用,因为它不是文件而是一个标签,所以make无法生成它的依赖关系和决定它是否要执行,只能通过显示指定这个目标才可以 ,通过make clean的指令。

这样一个基础的Makefile文件就编写完成了,所以当我们多个文件时要考虑Makefile,就算你更改了其中一个文件的内容,也只需要make一下就好。

解释一下一个简简单单的make都干了什么?

  1. make会在当前目录下找名字为“Makefile"或”makefile"的文件。
  2. 如果找到,从文件中的第一个目标文件,上面例子中的main文件,并把这个文件作为最终的目标可执行文件。
     
  3. 如果main不存在,或者main所依赖的后面.o文件的文件修改时间比main文件新,那么它就会执行后面所定义的命令来生成main这个文件。(这个就是makefile自动检测更新的原因)
  4. 如果mian所依赖的.o文件也存在,那么make会在当前文件中找到目标文件为.o文件的依赖性,如果找到则根据那一个规则生成.o文件(类似堆栈),就像上面的例子中,main依赖main.o add.o……,它就会先找main.o,找到main.o,发现main.o依赖main.c,然后根据规则生成main.o,其他.o文件类似,直到生成所有.o,然后生成mian。
  5. 这样就生成了可执行文件main,就可以运行了。

 编写Makefile第四步,编写生成多个可执行文件的Makefile文件

(一)格式:

其实格式与上面的基本一致,唯一的区别就是在第一行加了一句all:文件1 文件2 ..,后面按照规则正常编写即可

all:文件1 文件2 ……
文件1:需要依赖的.o文件
文件2:需要依赖的.o文件
……
.o文件生成的规则
……

(二)示例:

我们再编写一个test.c文件,只调用max.c方法,此时我们就需要生成两个可执行文件,一个test,一个main,那么Makefile的编写就如下

all:main test //顺序无所谓
main:main.o add.o sub.o max.o//main生成所需要的.o文件gcc -o main main.o add.o sub.o max.o //生成main的规则
test:test.o max.ogcc -o test test.o max.o
main.o:main.c //mian.o文件生成所需要的mian.c文件(程序员写的,所以不用声明它的规则)gcc -c main.c
test.o:test.cgcc -c test.c
add.o:add.cgcc -c add.c
sub.o:sub.cgcc -c sub.c
max.o:max.cgcc -c max.c
clean:      //需要手动调用rm *.o main test

编译运行:

删除多余的.o文件

编写Makefile第五步,Makefile的进阶(格式优化)

如果按照我上面的步骤敲完编写完成后,那么就可以对于刚刚编写的Makefile进行优化了

优化1:省略命令

  1. 就只用先写一行可执行文件需要依赖的.o,再将.o文件单独列出。make和我们约定好了用C编译器“cc”生成[.o]文件的规则,这就是隐含规则。
  2. 不过它是通过cc编译器编译的,如果要使用gcc/g++,需要在前面加cc=gcc,或者cc=g++;
    我以上面的例子进行优化
cc=gcc  //不写这个前面为cc
all:main test
main:main.o add.o sub.o max.o	
test:test.o max.o
main.o:
add.o:
sub.o:
max.o:
clean:rm *.o  main test

 优化2:引入变量

这里的引用变量有点类似于宏替换,别名的意思

经过上面的优化后,我们发现还是会有许多.o需要我们编写,此时我们就可以引入变量代替这些.o文件,只需要在用到这些.o文件的地方更改为新的$(变量名),$是格式

//假如变量名设为XX=main.o test.o add.o .....$(X)

 对于上述例子我们接着优化:

cc=gcc
all:main test
X=main.o  add.o sub.o max.o
Y=test.o max.o
main:$(X)
test:$(Y)
$(X):
$(Y):
clean:rm *.o main test

  优化3:解决子目录的问题

上面我们说了所有的.c文件必须在同一目录下,如果不在那么基本make无法处理,会报错,那么如何处理呢:

  • 引入makefile文件中的特殊变量“VPATH”。
  • 如果没有这个变量,make就只会再当前的目录中寻找依赖文件和目标文件。
  • 如果定义了这个变量,make就会在当前目录找不到的情况下,到指定目录下去找文件。
  • VPATH使用格式:目录用”:“分割,会先搜寻当前目录,再按这个目录寻找。

对于上述例子我们将max.c文件移动到aabb文件夹中,再次运行就会出错

这时我们引入变量VPATH,目录在当前目录和aabb文件夹中去找:就会成功

cc=gcc
all:main test
X=main.o  add.o sub.o max.o
Y=test.o max.o
VPATH=.:./aabb
main:$(X)
test:$(Y)
$(X):
$(Y):
clean:rm *.o main test

优化4:makefile自动清理中间文件

我们上面写的基本makefile不能自动清理中间文件,为了让可以自动清理:

  • 我们需要引入cleanobj这个可执行文件,把它和可执行文件一起写到all:后面,那么生成可执行文件后就会自动执行它。
  • 它没有依赖文件,直接写指令,可执行文件执行后就执行这个指令
cc=gcc
all:main test cleanobj //main和test执行完自动执行cleanobj
X=main.o  add.o sub.o max.o
Y=test.o max.o
VPATH=.:./aabb
main:$(X)
test:$(Y)
$(X):
$(Y):
clean:rm *.o main test
cleannobj:rm *.o //删除所有.o文件


这样中间产生的.o文件就会自动删除了。

看到这里相信对于Makefile文件就有了一定的了解!加油!

博主创作不易感谢支持!


http://chatgpt.dhexx.cn/article/njKD6be1.shtml

相关文章

C语言makefile文件

文章目录 一、makefile的编写二、make命令三、makefile文件中的变量四、应用经验五、课后作业六、版权声明 在软件的工程中的源文件是很多的&#xff0c;其按照类型、功能、模块分别放在若干个目录和文件中&#xff0c;哪些文件需要编译&#xff0c;那些文件需要后编译&#xf…

【Linux】Makefile文件

1.Makefile的作用 Makefile文件定义了一系列的规则来指定哪些文件需要先编译&#xff0c;哪些文件需要后编译&#xff0c;哪些文件需要重新编译&#xff0c;可以实现自动化编译&#xff0c;一旦写好&#xff0c;只需要一个make命令&#xff0c;整个工程完全自动编译&#xff0…

计算一幅图像的信噪比

计算一幅图像的信噪比 适用情况计算步骤附matlab代码example计算结果计算峰值信噪比 大家好&#xff0c;这是我两年前写的一篇博客修正版本&#xff0c;本来打算删掉了&#xff0c;但是阅读量很多&#xff0c;可能对大家会有一点小小的帮助&#xff0c;就重新放上来吧。 适用情…

两种常用的全参考图像质量评价指标——峰值信噪比(PSNR)和结构相似性(SSIM)

原文&#xff1a;https://blog.csdn.net/zjyruobing/article/details/49908979 1.PSNR&#xff08;Peak Signal to Noise Ratio&#xff09;峰值信噪比&#xff1a; MSE表示当前图像X和参考图像Y的均方误差&#xff08;Mean Square Error&#xff09;&#xff0c;H、W分别为图…

图像信噪比的理解

图像的信噪比和图像的清晰度一样&#xff0c;都是衡量图像质量高低的重要指标。图像的信噪比是指视频信号的大小与噪波信号大小的比值&#xff0c;其公式为&#xff1a; S/N&#xff08;信噪比&#xff09; 20 log (信号/噪声)dB信噪比大&#xff0c;图像画面就干净&#xff0…

相机的信噪比

在图像传感器的成像过程中&#xff0c;真实的信号是无法探测到的理想值。在成像过程中理想值被引入了一系列的不确定性&#xff0c;最终形成读出信号也即图像。此过程中的不确定性被统一称为噪声。而信号与噪声的比值被定义为信噪比&#xff08;Signal-to-NoiseRatio, SNR&…

信噪比(SNR)

信噪比&#xff08;Signal-to-noise ratio&#xff0c;缩写为 SNR 或 S/N&#xff09;&#xff0c;也称作信杂比或讯杂比。 信噪比&#xff0c;为有用信号功率(Power of Signal)与噪声功率(Power of Noise)的比。因此为幅度&#xff08;Amplitude&#xff09;比的平方&#xff…

信干噪比、信噪比

信干噪比 名词定义 信干噪比SINR&#xff08;Signal to Interference plus Noise Ratio&#xff09;&#xff0c;指的是系统中信号与干扰和噪声之和的比。 信号是指来自设备外部需要通过设备进行处理的电子信号。 干扰是指系统本身以及异系统带来的干扰&#xff0c;如同频干扰…

图像的峰值信噪比(peak signal to noise ratio, PSNR)

峰值信噪比&#xff08;PSNR&#xff09;是一个表示信号最大可能功率和影响它的表示精度的破坏性噪声功率的比值的工程术语。由于许多信号都有非常宽的动态范围&#xff0c;峰值信噪比常用对数分贝单位来表示。 在图像处理中&#xff0c;要对图像进行客观的评价&#xff0c;常…

图像处理随笔之峰值信噪比(peak signal to noise ratio)

图像处理随笔之峰值信噪比&#xff08;peak signal to noise ratio&#xff09; the definition from WIKI&#xff1a;an engineering term for the ratio between the maximum power of a signal and the power of corrupting noise that affects the fidelity of its repre…

python求不同分辨率图像的峰值信噪比,一文搞懂

可以使用 Python 的 NumPy 和 OpenCV 库来实现这个任务。提前准备一张图片作为素材。 文章目录 什么是峰值信噪比PSNR 峰值信噪比补充说明 使用 OpenCV 库来实现这个任务PSNR 的计算值受图像的亮度影响计算不同分辨率图像的 PSNRpython 求不同分辨率图像的峰值信噪比 | 其他知识…

【深度学习】图像去雾,去噪里常用的相似评价指标:PSNR(峰值信噪比) SSIM(结构相似度)MSE(均方误差)

文章目录 一、PSNR&#xff08;峰值信噪比&#xff09;二、SSIM&#xff08;结构相似度&#xff09;三、MSE&#xff08;均方误差&#xff09;小插曲&#xff1a;plt.savefig&#xff08;&#xff09;保存的图片为空白 一、PSNR&#xff08;峰值信噪比&#xff09; 公式直接抄我…

信噪比

fft原理 能量谱 &#xff08;5&#xff09;能量信号频谱通常既含有幅度也含有相位信息&#xff1b;幅度谱的平方&#xff08;二次量纲&#xff09;又叫能量谱&#xff08;密度&#xff09;&#xff0c;它描述了信号能量的频域分布&#xff1b;功率信号的功率谱&#xff08;密…

PSNR峰值信噪比matlab实现

PSNR&#xff0c;峰值信噪比&#xff0c;通常用来评价一幅图像压缩后和原图像相比质量的好坏&#xff0c;当然&#xff0c;压缩后图像一定会比原图像质量差的&#xff0c;所以就用这样一个评价指标来规定标准了。PSNR越高&#xff0c;压缩后失真越小。这里主要定义了两个值&…

psnr--峰值信噪比

psnr是“Peak Signal to Noise Ratio”的缩写&#xff0c;即峰值信噪比&#xff0c;是一种评价图像的客观标准&#xff0c;它具有局限性&#xff0c;一般是用于最大值信号和背景噪音之间的一个工程项目。 中文名 PSNR 外文名 Peak Signal to Noise Ratio” 意 义 峰值…

信噪比的定义及计算方法

1.信噪比的定义 英文名称叫做SNR或S/N&#xff08;SIGNAL-NOISE RATIO)&#xff0c;又称为讯噪比。是指一个电子设备或者电子系统中信号与噪声的比例。这里面的信号指的是来自设备外部需要通过这台设备进行处理的电子信号&#xff0c;噪声是指经过该设备后产生的原信号中并不存…

PSNR峰值信噪比(python代码实现+SSIM+MSIM)

一、原理 psnr是“Peak Signal to Noise Ratio”的缩写&#xff0c;即峰值信噪比&#xff0c;是一种评价图像的客观标准 用来表示信号最大可能功率和影响它的表示精度的破坏性噪声功率的比值&#xff0c;可以显示图像画质损失的程度。峰值信噪比越大&#xff0c;表示画质损失…

opencv图像处理学习(五十七)——峰值信噪比和结构相似性

1.峰值信噪比 峰值信噪比是常用的衡量信号失真的指标。该参数是基于图像像素灰度值进行统计分析&#xff0c;但是由于人类视觉特性的差异性&#xff0c;通常出现的评价结果与人的主管感觉不一致&#xff0c;但其仍然是一个有参考价值的评价指标。对于两幅图像I与K&#xff0c;…

用户自定义函数UDF

SQL DDL&#xff1a;用户自定义函数UDF 什么是UDF&#xff1f; Hive支持的函数除了内置函数&#xff0c;允许编写用户自定义函数&#xff08;User Define Function&#xff09;来扩充函数的功能。 用户自定义函数需要使用Java语言进行编写&#xff0c;完成的UDF可以打包成Ja…

hive笔记八:自定义函数-自定义UDF函数/自定义UDTF函数

目录 自定义函数 自定义UDF函数 自定义UDTF函数 自定义函数 Hive自带一些函数&#xff0c;比如&#xff1a;max/min等&#xff1b;当Hive提供的内置函数无法满足你的业务处理需要时&#xff0c;此时就可以考虑使用用户自定义函数。 UDF&#xff1a;user-defined function …