本文主要参考陈皓《跟我一起写 Makefile (PDF 重制版)》
这篇教程讲得还是蛮清楚的,本文主要是实战总结。
参考视频:
初学者,容易搞混Makefile和Shell的语法。。。。。。
基本的内容可直接查阅《跟我一起写Makefile》,本文重点记录容易搞错容易忘记的地方。
为什么需要Makefile
Makefile是用来管理工程的。
在一个正式的软件项目中,由很多个.c和.h文件构成,此时如果直接在命令行编译,就会像这样:gcc a.c b.c c.c d.c e.c f.c g.c -o exe 每次编译都要输入一堆东西很麻烦,这个问题严重影响工作效率,怎么办?Makefile来解决。
makefile 关系到了整个工程的编译规则。
一个工程中的源文件不计数,其按类型、功能、 模块分别放在若干个目录中,makefile 定义了一系列的规则来指定,哪些文件需要先编译, 哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile 就像一个 Shell 脚本一样,其中也可以执行操作系统的命令。
makefile 带来的好处就是——“自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译, 极大的提高了软件开发的效率。
make 是一个命令工具,是一个解释 makefile 中指令的命令工具,一般来说,大多数的 IDE 都有这个命令,比如:Delphi 的 make,Visual C++的 nmake, Linux 下 GNU 的 make。可见,makefile 都成为了一种在工程方面的编译方法。
一般来说,无论是C还是C++,首先要把源文件编译成中间代码文件,在Windows下也就是
.obj
文件,UNIX下是.o
文件,即Object File,这个动作叫做编译(compile),每个源文件都应该对应于一个中间目标文件(.o
文件或.obj
文件)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。附:.o和.a文件是什么关系?
我们知道中间目标文件有很多,需要被链接,当我们把这些.o文件全都一个一个地分散着给别人,显然是不太安全的,并且不大优雅的,所以,通常将这些.o文件打个包,然后发给别人。
【一站式解惑】Linux中.a、.so和.o文件以及-I,-L,LIBRARY_PATH,LD_LIBRARY_PATH等-腾讯云开发者社区-腾讯云 (tencent.com)
链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),链接时,主要是链接函数和全局变量。
在编译时,编译器只检测程序语法和函数、变量是否被声明。而在链接程序时,链接器会在所有的Object File中找寻对应的全局变量以及函数的实现,如果找不到,那到就会报链接错误码(Linker Error)。
bin、hex、exe、elf文件类型到底有何区别?如何解读hex文件和elf文件?...相关内容都在这里!-CSDN博客
gcc命令
gcc命令 – C/C++语言编译器 – Linux命令大全(手册) (linuxcool.com)
gcc常用的两个参数
-c,compile;
-o,output;
另外,用gcc来执行.o文件,会直接链接成可执行文件。
make命令
make命令的参数选项(执行make时可以添加哪些选项)_make 参数-CSDN博客
比较重要的几个参数:
-C,make时,默认的是执行当前目录下的Makefile,但是如果Makefile在别的目录里,可以加-C,在读取 Makefile 之前,进入到目录 DIR,然后执行 make。也可以理解成,执行指定路径目录下的Makefile。一般开启了-C,就会默认开启-w(小写),可以显示进入了目标路径,执行完Makefile后又会回到原处。
-f,make时默认识别名叫Makefile的文件,如果不叫这名字,而是xxx,可以用-f xxx指定文件名。
-n,只打印执行的命令,但是不执行命令。可以让我们看到执行的过程,方便调试。
-s,静默输出,只执行命令,不显示命令。和-n相反。
-w,在 make 进入一个子目录读取 Makefile 之前打印工作目录,这个选项可以帮助我们调试 Makefile,跟踪定位错误。使用 "-C" 选项时默认打开这个选项。
make静默输出
make执行时make 默认会打印每条命令,再执行。这个行为被定义为回声。
make静默输出的两种方式
- 一开始执行make时加-s选项,这样会禁止所有的命令回显
- Makefile里执行命令时在命令前面加上@,这样可以精准到每一条命令;
makefile嵌套
参考:
多级目录的makefile编写及参数传递_不同makefile传递值-CSDN博客
Makefile学习笔记系列3:具有子目录层次结构的makefile写法_makefile 包含子目录-CSDN博客
多层级的makefile编写——递归调用makefile_makefile 循环调用makefile-CSDN博客
每个目录下的makefile只负责本目录下的文件的编译。
嵌套执行makefile,其实就是在上层makefile中执行下层的makefile,有两种方式:
1、先cd进入子路径,然后make;
2、直接在上层makefile中执行make,但是用-C指定子makefile的路径;
有时候我们需要向子 make 传递变量,
这个时候使用“export”来导出要传递给子 make 的变量即可,如果不希望哪个变量传递给子make 的话就使用“unexport”来声明不导出:有两个特殊的变量:“SHELL”和“MAKEFLAGS”,这两个变量除非使用“unexport”声明,export VARIABLE …… //导出变量给子 make 。 unexport VARIABLE…… //不导出变量给子 make。
否则的话在整个make的执行过程中,它们的值始终自动的传递给子make。
makefile可用的环境变量
linux环境下, 执行命令make -p可以查看makefile可用的环境变量。
Ubuntu--查看Makefile内置变量_make 查看变量-CSDN博客
然后ctrl+shift+f打开终端界面的搜索框,搜索目标变量名。
另外注意,我们可以在执行make的时候,给makefile传递全局变量
比如:
make HOSTNAME=admin.cc
Makefile 环境变量 - Makefile中使用系统环境变量 - Makefile 简明教程 | 宅学部落 (zhaixue.cc)
如果传递的变量是系统环境变量,那么系统环境变量就会被这个同名变量覆盖。
一些常见的环境变量
makefile中常见的环境变量_makefile 环境变量-CSDN博客
Makefile中常见的Q是啥?
在makefile,约定俗成地将Q绑定到@,用来实现命令的静默输出,Quiet...
自动化变量(规则变量)
$@:表示目标文件的名称,包含扩展名 $^:表示所有的依赖文件,以空格隔开,不重复 $<:表示第一个依赖文件的名称 $+:表示所有的依赖文件,空格隔开,可以重复 $*:表示目标文件的名称,不包含扩展名 $?:依赖项中,所有比目标文件新的依赖文件
函数
指定头文件路径
在mcu开发中,一般都会在keil上指定头文件所在的位置,那么在linux中怎么指定头文件的位置呢?
一般是通过在Makefile里面将头文件路径添加到CFLAGS这个全局变量里。
系统自带的库里的头文件,一般都不需要指定路径,只需要在Makefile中添加自定义的一些头文件即可。
看一个文件有没有参与编译,就在里面打印一大堆乱七八糟的东西,如果编译时没有报错,就说明没有参与编译。
编译时是报语法错误,比如逗号没写、头文件没找到等等;链接时才会报找不到对应的变量或者函数;
在编译时,编译器只检测程序语法和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成 Object File。而在链接程序时,链接器会在所有的 Object File 中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在 VC 下,这种错误一般是:Link 2001 错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的 Object File。
编译时,所有的头文件都会在源文件中展开,然后编译生成.o文件,链接时,主要是链接函数和全局变量。所以,我们可以使用这些中间目标文件(.o 文件或 .obj 文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File), 在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便。所以,我们要给中间目标文件打个包,在 Windows 下这种包叫“库文件”(Library File),也就是 .lib 文件,在 UNIX 下,是 Archive File,也就是 .a 文件。
问题总结
01
make时遇到一个问题:
百度后,有说是格式的问题,注意Tab的使用,修改后仍然提示。
Makefile missing separator. Stop.怎么解决_limanjihe的博客-CSDN博客_makefile missing separator
其实,这句话的意思就是缺少分隔,通常都是格式的问题,这里还提示了是第1行。
我不存在Tab键的问题,而是在第一行的目标和依赖之间,没有用冒号隔开,而是直接空格。
make.defs
Makefile不能能和shell一样,直接写命令,会报错
makefile并不是命令的集合,makefile的目的只有一件事,那就是生成最终目标。