一、为什么要有Makefile
Makefile可以很好的组织和管理程序和文件。特别是对一个大型工程而言,Makefile 关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,Makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为Makefile 就像一个 shell 脚本一样,其中也可以执行操作系统的命令。
Makefile 带来的好处是“自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。
一、Makefile的基本规则
目标:依赖1 依赖2
[tab]命令
上面就是Makefile的基本语法规则
当依赖比目标新时,它就会执行下面的命令。
二、使用
下面我以arm的交叉编译工具为例来说明make的使用
假设我们有连个文件start.c和main.c。现在我们要使用arm的交叉编译工具编译他们
1.先看一下我们的最初版本,没有makefile时我们怎么做?
#预处理+编译+汇编 其中gcc的-c参数是指明,只编译不连接 -o参数是指明输出文件的名字
arm-none-linux-gnueabi-gcc -c start.S -o start.o
arm-none-linux-gnueabi-gcc -c main.c -o main.o
#连接,生成可执行文件 -Ttext 是指明.text段的起始地址
arm-none-linux-gnueabi-ld -Ttext 0xd0020010 -o main start.o main.o
直接命令行敲,每次更改文件编译都要敲这么多命令,这还是在只有两个文件的情况下,如果有成百上千个文件,这明显不可能让人做。
2.把上面的命令行写成一个shell脚本过makefile文件,每次执行前都执行一次shell脚本或make指令
用shell脚本的实现
为了满足make规则,编写Makefile文
main.elf:start.o main.o
arm-none-linux-gnueabi-gcc -c start.S -o start.o
arm-none-linux-gnueabi-gcc -c main.c -o main.o
arm-none-linux-gnueabi-ld -Ttext 0xd0020010 -o main start.o main.o
可以对比看到shell脚本和make文件都可以生成最终的main.elf文件。
但唯一有一点可以从上面看到,如果main.elf的依赖是最新的话,再次执行make不会再执行命令。
而shell则会无论是否是最新的都执行命令。
当文件 特别多的时候,make会编译有改动过的文件,而shell是 所有都编译,通常我们该软件,都只会该很小一部分,这样就能节约大量的时间。
突然发现,即使改了一个文件,也会编译两个文件。所以我们的makefile文件继续改进。
改进为如下:每个文件都有自己的依赖,当它的依赖没有改动时,则不指行命令。
main.elf: start.o main.o
arm-none-linux-gnueabi-ld -Ttext 0xd0020010 -o main start.o main.o
start.o:start.S
arm-none-linux-gnueabi-gcc -c start.S -o start.o
main.o: main.c
arm-none-linux-gnueabi-gcc -c main.c -o main.o
3.接下来我们增加两个文件
main.elf:start.o main.o a.o b.o
arm-none-linux-gnueabi-ld -Ttext 0xd0020010 -o main start.o main.o a.o b.o
start.o:start.S
arm-none-linux-gnueabi-gcc -c start.S -o start.o
main.o:main.c
arm-none-linux-gnueabi-gcc -c main.c -o main.o
a.o:a.S
arm-none-linux-gnueabi-gcc -c a.c -o a.o
b.o:b.S
arm-none-linux-gnueabi-gcc -c b.c -o b.o
可以发现,随着文件数量的增加,我们增加命令,又要增加依赖关系,还得增加 ld后面的链接个数
能不能有更简便的方法,减少重复性的工作呢?
肯定是有的。
4.自动化变量
4.1、% 作为统配符,可以表示所有和它文件一类的文件如 %.c代表当前目录下的所有.c文件
make中可以有如下命令
%.o: %.c
即.o文件依赖于当前文件下的所有.c文件
类似的也有.S
%.o : %.S
4.2、变量
简单变量:如下,即A的值即刻被确定,比如下一行使用了A,则A的值就是xxx
A := xxx
延时变量:如下,A的值在整Makefile文件浏览一遍后,使用最后一个定义的即下面的使用A的值为ddd
A = xxx
使用A
A = ddd
通常绝大多数都是使用简单变量
4.3、变量的使用
A := xxx
C = $A
C = $(A)
4.4、目标的通配符
@
4.5、第一个依赖的通配符
<
4.6上面变量和通配符的结合
%.o : %.c
arm-linux-gcc -o $@ $< -c
即用过gcc命令,根据输入的依赖所有的.c文件生成对应的.o文件
相应的也有.S
%.o : %.S
arm-linux-gcc -o $@ $< -c
5.伪目标
即该目标不存在,但可以方便使用,比如下面的all:
all: start.o main.o a.o b.o
arm-none-linux-gnueabi-ld -Ttext 0xd0020010 -o main start.o main.o a.o b.o
%.o : %.c
arm-linux-gcc -o $@ $< -c
%.o : %.S
arm-linux-gcc -o $@ $< -c
增加.PHONY声明为伪目标
.PHONY:all
all: start.o main.o a.o b.o
arm-none-linux-gnueabi-ld -Ttext 0xd0020010 -o main start.o main.o a.o b.o
%.o : %.c
arm-linux-gcc -o $@ $< -c
%.o : %.S
arm-linux-gcc -o $@ $< -c
增加清除操作
.PHONY:all
all: start.o main.o a.o b.o
arm-none-linux-gnueabi-ld -Ttext 0xd0020010 -o main start.o main.o a.o b.o
%.o : %.c
arm-none-linux-gnueabi-gcc -o $@ $< -c
%.o : %.S
arm-none-linux-gnueabi-gcc -o $@ $< -c
.PHONY:clean
clean:
*.o *.elf -f
6.上面的已经比较完美了,但任然有缺点,比如all的依赖和ld命令后的东西重复了,每增加一个文件,都要改两个地方。
所以继续优化:
使用$^表示,它前面最近的目标的所有依赖即:all后面的start.o main.o a.o b.o
.PHONY:all
all: start.o main.o a.o b.o
arm-none-linux-gnueabi-ld -Ttext 0xd0020010 -o main $^
%.o : %.c
arm-none-linux-gnueabi-gcc -o $@ $< -c
%.o : %.S
arm-none-linux-gnueabi-gcc -o $@ $< -c
.PHONY:clean
clean:
*.o *.elf -f
7.方便增加文件,继续优化
增加变量obj方便增加文件或增加其它目录下的文件
obj += start.o main.o a.o
obj += b.o
obj += ./xxxx/cccc.o
obj += ./aaaa/dddd.o
.PHONY:all
all: $(obj)
arm-none-linux-gnueabi-ld -Ttext 0xd0020010 -o main $^
%.o : %.c
arm-none-linux-gnueabi-gcc -o $@ $< -c
%.o : %.S
arm-none-linux-gnueabi-gcc -o $@ $< -c
.PHONY:clean
clean:
*.o *.elf -f
8.上面的已经算可以常规使用了
发现增加伪目标后,连接命令每次都要执行。
所以看来还有可以继续优化的地方。
分析上面的Makefile,发现,all是一个伪目标,所以它下面的命令一点会被执行,而向想要ld命令不被执行,即它的目标必须不是伪目标,我们让这里的真实目标main做目标。
如下:all并不包含任何命令,make后,all它会首先检查main,是不是最新的,进而mian的依赖obj检查他们的依赖是不是最新的。
obj += start.o main.o a.o
obj += b.o
obj += ./xxxx/cccc.o
obj += ./aaaa/dddd.o
.PHONY:all
all: main
main:$(obj)
arm-none-linux-gnueabi-ld -Ttext 0xd0020010 -o main $^
%.o : %.c
arm-none-linux-gnueabi-gcc -o $@ $< -c
%.o : %.S
arm-none-linux-gnueabi-gcc -o $@ $< -c
.PHONY:clean
clean:
*.o *.elf -f
9.标准化,增加可移植性
CC := arm-none-linux-gnueabi-gcc
LD := arm-none-linux-gnueabi-ld
obj += start.o main.o a.o
obj += b.o
obj += ./xxxx/cccc.o
obj += ./aaaa/dddd.o
.PHONY:all
all: main
main:$(obj)
$(LD) -Ttext 0xd0020010 -o main $^
%.o : %.c
$(CC) -o $@ $< -c
%.o : %.S
$(CC) -o $@ $< -c
.PHONY:clean
clean:
*.o *.elf -f
————————————————
版权声明:本文为CSDN博主「to_run_away」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_16777851/article/details/81588193