linux下makefile的编写
默认的编译器是linux下的GCC和CC
makefile phony
make编译的步骤:
源⽂件⾸先会⽣成中间⽬标⽂件(⼀般为.o⽂件),再由中间⽬标⽂件⽣成执⾏⽂件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出⼀个警告,但可以⽣成Object File。⽽在链接程序时,链接器会在所有的Object File中寻函数的实现。
1、Makefile的规则
< : prerequisites ...
[tab键] command
target也就是⼀个⽬标⽂件,可以是Object File,也可以是执⾏⽂件。
prerequisites就是,要⽣成那个target所需要的⽂件或是⽬标。
command也就是make需要执⾏的命令。
这是⼀个⽂件的依赖关系,也就是说,target这⼀个或多个的⽬标⽂件依赖于prerequisites中的⽂件,其⽣成规则定义在command中。说⽩⼀点就是说,prerequisites中如果有⼀个以上的⽂件⽐target⽂件要新的话,command所定义的命令就会被执⾏。
2、⼀个例⼦
正如前⾯所说的,如果⼀个⼯程有3个头⽂件,和8个C⽂件,我们为了完成前⾯所述的那三个规则,我们的Makefile应该是下⾯的这个样⼦的。
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
反斜杠(\)是换⾏符的意思。这样⽐较便于Makefile的易读。我们可以把这个内容保存在⽂件为“Makefile”或“makefile”的⽂件中,然后在该⽬录下直接输⼊命令“make”就可以⽣成执⾏⽂件edit。如果要删除执⾏⽂件和所有的中间⽬标⽂件,那么,只要简单地执⾏⼀下“make clean”就可以了。
在这个makefile中,⽬标⽂件(target)包含:执⾏⽂件edit和中间⽬标⽂件(*.o),依赖⽂件(prerequisites)就是冒号后⾯的那些 .c ⽂件和 .h⽂件。每⼀个 .o ⽂件都有⼀组依赖⽂件,⽽这些 .o ⽂件⼜是执⾏⽂件 edit 的依赖⽂件。依赖关系的实质上就是说明了⽬标⽂件是由哪些⽂件⽣成的,换⾔之,⽬标⽂件是哪些⽂件更新的。
在定义好依赖关系后,后续的那⼀⾏定义了如何⽣成⽬标⽂件的操作系统命令,⼀定要以⼀个Tab键作为开头。记住,make并不管命令是怎么⼯作的,他只管执⾏所定义的命令。make会⽐较targets⽂件和prerequisites⽂件的修改⽇期,如果prerequisites⽂件的⽇期要⽐targets⽂件的⽇期要新,或者target不存在的话,那么,make就会执⾏后续定义的命令。
这⾥要说明⼀点的是,clean不是⼀个⽂件,它只不过是⼀个动作名字,有点像C语⾔中的lable⼀样,其冒号后什么也没有,那么,make就不会⾃动去⽂件的依赖性,也就不会⾃动执⾏其后所定义的命令。要执⾏其后的命令,就要在make命令后明显得指出这个lable的名字。这样的⽅法⾮常有⽤,我们可以在⼀个makefile中定义不⽤的编译或是和编译⽆关的命令,⽐如程序的打包,程序的备份,等等。
3、make是如何⼯作的
1、make会在当前⽬录下名字叫“Makefile”或“makefile”的⽂件。
2、如果到,它会⽂件中的第⼀个⽬标⽂件(target),在上⾯的例⼦中,他会到“edit”这个⽂件,并把这个⽂件作为最终的⽬标⽂件。
3、如果edit⽂件不存在,或是edit所依赖的后⾯的 .o ⽂件的⽂件修改时间要⽐edit这个⽂件新,那么,他就会执⾏后⾯所定义的命令来⽣成edit这个⽂件。
4、如果edit所依赖的.o⽂件也存在,那么make会在当前⽂件中⽬标为.o⽂件的依赖性,如果到则再根据那⼀个规则⽣成.o⽂件。(这有点像⼀个堆栈的过程)
5、当然,你的C⽂件和H⽂件是存在的啦,于是make会⽣成 .o ⽂件,然后再⽤ .o ⽂件声明make的终极任务,也就是执⾏⽂件edit了。
这就是整个make的依赖性,make会⼀层⼜⼀层地去⽂件的依赖关系,直到最终编译出第⼀个⽬标⽂件。在寻的过程中,如果出现错误,⽐如最后被依赖的⽂件不到,那么make就会直接退出,并报错,⽽对于所定义的命令的错误,或是编译不成功,make根本不理。make只管⽂件的依赖性,即,如果在我了依赖关系之后,冒号后⾯的⽂件还是不在,那么对不起,我就不⼯作啦。
于是在我们编程中,如果这个⼯程已被编译过了,当我们修改了其中⼀个源⽂件,⽐如file.c,那么根据我们的依赖性,我们的⽬标file.o会被重编译(也就是在这个依性关系后⾯所定义的命令),于是file.o的⽂件也是最新的啦,于是file.o的⽂件修改时间要⽐edit要新,所以edit也会被重新链接了(详见edit⽬标⽂件后定义的命令)。
⽽如果我们改变了“command.h”,那么,kdb.o、command.o和files.o都会被重编译,并且,edit会被重链接。
4、makefile使⽤变量
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
我们可以看到[.o]⽂件的字符串被重复了两次,如果我们的⼯程需要加⼊⼀个新的[.o]⽂件,那么我们需要在两个地⽅加(应该是三个地⽅,还有⼀个地⽅在clean中)。当然,我们的makefile并不复杂,所以在两个地⽅加也不累,但如果makefile变得复杂,那么我们就有可能会忘掉⼀个需要加⼊的地⽅,⽽导致编译失败。所以,为了makefile的易维护,在makefile中我们可以使⽤变量。makefile的变量也就是⼀个字符串,理解成C语⾔中的宏可能会更好。
于是,我们就可以很⽅便地在我们的makefile中以“$(objects)”的⽅式来使⽤这个变量了,于是我们的改良版makefile就变成下⾯这个样⼦:
objects = main.o kbd.o command.o display.o \
insert.osearch.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit $(objects)
于是如果有新的 .o ⽂件加⼊,我们只需简单地修改⼀下 objects 变量就可以了。
5、让make⾃动推导
GNU的make很强⼤,它可以⾃动推导⽂件以及⽂件依赖关系后⾯的命令,于是我们就没必要去在每⼀个[.o]⽂件后都写上类似的命令,因为,我们的make会⾃动识别,并⾃⼰推导命令。
只要make看到⼀个[.o]⽂件,它就会⾃动的把[.c]⽂件加在依赖关系中,如果make到⼀个whatever.o,那么whatever.c,就会是whatever.o的依赖⽂件。并且 cc -c whatever.c 也会被推导出来,于是,我们的makefile再也不⽤写得这么复杂。我们的是新的makefile ⼜出炉了。
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
.PHONY : clean
clean :
rm edit $(objects)
这种⽅法,也就是make的“隐晦规则”。上⾯⽂件内容中,“.PHONY”表⽰,clean是个伪⽬标⽂件。
6、另类风格的makefile
即然我们的make可以⾃动推导命令,那么我看到那堆[.o]和[.h]的依赖就有点不爽,那么多的重复的[.h],
能不能把其收拢起来,好吧,没有问题,这个对于make来说很容易,谁叫它提供了⾃动推导命令和⽂件的功能呢?来看看最新风格的makefile吧。
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
.PHONY : clean
clean :
rm edit $(objects)
这种风格,让我们的makefile变得很简单,但我们的⽂件依赖关系就显得有点凌乱了。鱼和熊掌不可兼得。还看你的喜好了。我是不喜欢这种风格的,⼀是⽂件的依赖关系看不清楚,⼆是如果⽂件⼀多,要加⼊⼏个新的.o⽂件,那就理不清楚了。
每个Makefile中都应该写⼀个清空⽬标⽂件(.o和执⾏⽂件)的规则,这不仅便于重编译,也很利于保持⽂件的清洁。⼀般的风格都是:
clean:
rm edit $(objects)
更为稳健的做法是:
.PHONY : clean
clean :
-rm edit $(objects)
前⾯说过,.PHONY意思表⽰clean是⼀个“伪⽬标”,。⽽在rm命令前⾯加了⼀个⼩减号的意思就是,
也许某些⽂件出现问题,但不要管,继续做后⾯的事。当然,clean的规则不要放在⽂件的开头,不然,这就会变成make的默认⽬标,相信谁也不愿意这样。
到这⾥其实就已经满⾜我们⽇常使⽤makefile了,还有⼀些通配符的使⽤,
可以参考该链接,本⽂也是参考这个经典的链接
给出:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。