学会编写Makefile很重要,因为在Linux的环境下要编译C语言程序要使用gcc命令,但是工程中的文件较多时,手动的执行gcc命令很麻烦也很容易出粗,所以学会Makefile的使用可以让我们在以后的linux学习中一劳永逸。
首先我们创建一个文件夹当做我们本次的工程。
进入learnMakefile,生成.c文件和.h文件。
main.c
print.c
print.h
现在有了三个文件,开始编写我们的Makefile。
:前面是目标生成文件,:后面是生成目标文件的依赖文件。比如main是目标生成文件,main.o和print.o都是依赖文件。clean命令用于清空编译时生成的.o文件,前面加.PHONY是因为如果文件夹中如果有个文件名叫clean的话会出现错误,用了之后的clean相当于伪命令。
1 main:main.o print.o
2 gcc main.o print.o
3 main.o:main.c print.h
4 gcc -c main.c
5 print.o:print.c print.h
6 gcc -c print.c
7 .PHONY:clean
8 clean:
9 rm -f main.o print.o
编写完成后输入make来执行Makefile文件。
可以看到执行了make命令后,执行了一系列编译,生成了许多.o文件和a.out执行文件,执行a.out输出hello,说明我们的makefile是正确的。执行make clean来删除刚刚生成的.o和可执行文件。
这样写的makefile可以使用,但是如果再加几个.c文件我们需要对Makefile进行比较大的改动,现在使用自动化变量来对其进行简化。
1 main:main.o print.o
2 gcc $^
3 main.o:main.c print.h
4 gcc -c main.c
5 print.o:print.c print.h
6 gcc -c print.c
7 .PHONY:clean
8 clean:
9 rm -f main.o print.o
仅仅第二行改变了,但是效果是一样的,$^这里指的是所有的依赖文件就是main.o和print.o。
后面再引入我们定义的变量来进一步简化,引用自己的变量格式是$(变量名)。
1 ELF=main
2 object=main.o print.o
3 $(ELF):$(object)
4 gcc $^ -o $@
5 main.o:main.c print.h
6 gcc -c main.c
7 print.o:print.c print.h
8 gcc -c print.c
9 .PHONY:clean
10 clean:
11 rm -f $(object) main a.out
ELF是为了方便以后更改生成的可执行文件的名称,gcc -o就是可以指定生成可执行文件的名称,不指定的话就是默认生成a.out文件。 $@也是自动化变量,他指的是目标文件,这里我们已经指定了生成目标文件的名称是main。变量object存的是我们依赖文件,这样以后要加文件时只需要在object中加就可以了,很方便。下面是运行结果(可以看到生成了.o文件和main可执行文件):
第5-8行还是很多余,继续进行简化。
1 ELF=main
2 object=main.o print.o
3 $(ELF):$(object)
4 gcc $^ -o $@
5 main.o:print.h
6 print.o:print.h
7 .PHONY:clean
8 clean:
9 rm -f $(object) main a.out
他还是可以运行成功的,为什么要删到只剩5.6行呢,因为make有自动推导的功能,可以根据目标文件推到出要依赖的文件。其实后面的.h还是可以简化:
1 ELF=main
2 object=main.o print.o
3 $(ELF):$(object)
4 gcc $^ -o $@
5 $(object):
6 .PHONY:clean
7 clean:
8 rm -f $(object) main a.out
前面的object已经包含了那两个目标文件所以直接第5行代替。
这样虽然很简单了,但是我们还要手动的输入object变量的结果,不能一劳永逸,所以还可以更简化。使用make中的函数wildcard自动推导出工程中的.c文件,在使用$(SRC:.c=.o)把SRC得到的.c文件都改名为.o文件,执行make还是成功的。除了wildcard函数当然也可以使用SHELL命令来找出所有的.c文件,比如ls *.c或者find *.c,写在Makefile的格式是$(shell ls *.c)或$(shell find *.c)。
1 SRC=$(wildcard *.c)
2 ELF=main
3 object=$(SRC:.c=.o)
4 $(ELF):$(object)
5 gcc $^ -o $@
6 $(object):
7 .PHONY:clean
8 clean:
9 rm -f $(object) main a.out
这样以来已经可以说是最简化了,但是还有一个问题,无论是函数wildcard还是shell命令ls、find只能找到同一个目录下的.c文件,但是实际做工程的时候都是多目录,这样肯定会出错。
我们来试试:创建一个文件夹aaa把print.c(也要拷贝一份print.h放入aaa文件夹)文件放入。
已经放入回到Makefile的目录,执行make会报错 :
现在如何解决呢,也很简单,继续用shell命令find,find可以在当前文件夹和子文件夹中查找,更改后的Makefile:
1 SRC=$(shell find -name '*.c')
2 ELF=main
3 object=$(SRC:.c=.o)
4 $(ELF):$(object)
5 gcc $^ -o $@
6 $(object):
7 .PHONY:clean
8 clean:
9 rm -f $(object) main a.out
注意:*.c要使用''括起来,把print.h给aaa也拷贝一份上面忘记了。
可以看到也是正确运行。我们试着再加一个.c文件,看看是否能一劳永逸。
又加了一个aaa.c文件还是打印hello,可以看到还是正确的,正阳这个makefile就实现了一次编写多次使用。这只是简单的使用make工具,可以看看uboot的Makefile,很复杂。