本次笔记是看了陈皓的《跟我一起写Makefile》而作的整理,原版链接地址如下
http://pan.baidu.com/s/1skBumRF
第一部分
一、Makefile介绍
make命令编译和链接文件的的规则是:
1、如果这个工程没有编译过,那么我们所有的C文件都要编译并被链接。
2、如果这个工程的某几个C文件被修改,那么我们只需编译被修改的C文件,并链接目标文件。
3、如果这个工程的头文件被修改了,那我我们要编译引用这个头文件的几个C文件,并链接目标程序
二、Makefile的规则
Makefile规则如下:
target...:prerequisites...
command
target也就是目标文件,可以是.obj文件也可以是执行文件,还可以是一个标签(Label)
prerequisites就是要生成那个target所需要的文件或是目标,一般称为“依赖”
command也就是make需要执行的命令(需要注意的是,command命令一定要以tab键作为开头)
根据make的编译规则,如果prerequisites中有一个以上的文件比target文件要新的haul,command命令会被执行,这就是Makefile的核心内容。
make命令会根据文件的依赖性一层一层地寻找对应的文件,直到最终编译出第一个目标文件。在寻找的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make会直接退出,并报错。而对于所定义的命令的错误,或是编译不成功,make根本不去理会。
三、Makefile如何工作
在Makefile中可以使用变量,使其更容易维护,Makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。实现如下:
假如我们有 main.o key.o command.o search.o insert.o file.o init.o display.o这几个.obj文件,那么我们在Makefile一开始就这样定义:
obj=main.o key.o command.o search.o insert.o file.o init.o display.o
在Makefile中就以“$(obj)”的方式来使用这个变量了,而假如我们需要添加新的obj文件,只需要在定义中添加即可,灰常方便
四、Makefile自动推导
GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没有必要去在每个.o文件后面写上依赖的命令,make能自动识别,因此在.o文件下只需要写所依赖的.h文件即可。一个例子如下:
原先的规则如下:
main.o: main.c defs.h
cc -c main.c
由于make的自动推导,可以简化写为
main.o: defs.h
五、清空文件的规则
每个Makefile中都应该写一个清空目标文件(.o和执行文件)的规则,这不仅便于编译,也有利于保持文件的清洁。
比较良好的清理规则如下:
.PHONY:clean
clean:
-rm edit $(obj)
.PHONY的意思表示clean是一个伪目标,而在rm参数前加一个减号的意思是,也许某些文件出现问题,但不要管,继续做后面的事情。当然,clean规则不要放在开头,否则被当成第一个目标文件。其中一个潜规则是——将clean放在文件的最后。
第二部分
一、Makefile里面有什么?
Makefile主要包含这5个东西:显示规则、隐晦规则、变量定义、文件指示和注释
1、显示规则
显示规则说明了如何生成一个或者多个目标文件。这是有Makefile书写者明显指出,要生成的文件,文件的依赖文件,生成的命令
2、隐晦规则
由于我们的make有自动推导的功能,所以隐晦规则可以让我们较为粗糙简略地书写Makefile
3、变量的定义
在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个类似于C语言的宏。
4、文件指示
其包括三个部分,1)在一个Makefile中引用另一个Makefile,就像C语言中的include一样;2)指根据某些情况制定Makefile中的有效部分,就像C语言中的预编译#if一样;3)定义一个多行命令
5、注释
Makefile中只有行注释,用#字符执行
二、引用其他的makefile
在makefile中使用关键字include可以把别的makefile文件包含进来,语法和C语言的一样:include<filename>
filename可以是当前操作系统shell的文件模式,(可以包含路径和通配符),在include前面可以有一些空格字符,但是绝不能是tab键开始。
如果make命令找不到所包含的文件,不会第一时间出现致命的错误,此时会发出一条警告,接着继续寻找其他被包含的文件,过最后还是没有找到那个文件,make才会出现一条致命信息。
为了解决上述问题,可以在include前面加一个减号,表示无论include过程中出现什么错误,都不要报错,继续执行。
三、make工作方式
GNU的meke工作时执行的步骤如下:(其他的make也类似)
1、读入所有的makefile
2、读入被include的其他makefile
3、初始化文件中的变量
4、推到隐晦规则,并分析所有规则
5、为所有的目标文件创造依赖关系链
6、根据依赖关系,决定哪些目标需要重新生成
7、执行生成命令
1-5步骤为第一阶段,6-7步骤为第二阶段
第三部分 书写规则
一、规则语法
target...:prerequisites...
command
或是target...:prerequisites...;command
若command和它们在同一行,那么应该以分号(英文输入格式)作为分隔,若不在同一行,则应该以tab键开头。
二、在规则中使用通配符
make支持三种通配符分别是 * ? [...]
通配符代替了一系列的文件,比如*.c表示所有后缀为c的文件
而需要注意的一个特殊情况是前面说道的定义变量,如下:
obj=*.o 然而使用了通配符之后,obj的值就是*.o,并不会展开,为了解决这个问题,可以这样定义:obj:=$(wildcard *.o)
三、伪目标
在前面提到的clean这个命令是一个伪目标,因为我们并不生成clean这个文件,仅仅是一个标签。由于它不是一个文件,所以make命令无法生成它的依赖关系和决定它是否要执行,我们只有通过显示地指明这个“”目标“”才能让其生效。看下面的例子
.PHONY : clean
只要有这个声明,不管是否有clean这个文件,要运行clean这个目标,只有“”make clean“”这样的命令,于是可以写为:
.PHONY:clean
clean:
-rm edit $(obj)
第四部分 补充
一、编译命令
arm-linux-objcopy 把一种目标文件中的内容复制到另一种类型的目标文件中。
arm-linux-objdump 显示一个或者更多目标文件的信息。使用选项来控制其显示的信息,它所显示的信息通常只有编写编译工具的人才感兴趣。
例子:
arm-linux-objdump -D -m arm boot_elf > boot.dis
-m后面跟的是cpu的架构,例子中表示arm架构,
> 表示将反汇编语言写入boot.dis中而不在终端显示。
二、编译参数的解释
-O优化参数,后面可跟数字来表示优化的级别。-O0表示不优化,大部分平台最多使用-O3,建议使用到-O2,另外-Os表示对代码的大小进行优化,生成尽可能小的机器码
-E仅仅对代码进行预处理,不编译。实际上是将头文件包含和宏定义展开。
-c紧急汇编及编译代码,不进行链接。也就是将源代码编译成.o文件
-S仅仅汇编而不进行编译及链接。也就是将源代码编译成汇编指令。
-o filename 指明输出文件名。一般配-E -c -S三个命令来使用。例如:gcc -c a.c -o a.o
-w关闭所有告警提示
-Wall打开大部分告警提示,而不是全部。
-W对某些告警显示更详细的信息
-D定义宏。