make和makefile入门 本文讲述的是简单的make和makefile基础知识,帮助大家了解minix的boot目录下 的makefile文件,还要求大家先了解linux下的一些常见命令。 make的作者:Richard Stallman and Roland McGrath GNU make满足POSIX.2的标准。 如果想执行make命令,首先编写一个makefile文件,该文件主要是描述你的 程序中所有文件的关系以及你所用的命令(编译命令等)。在一个程序中,可执行文 件是从目标文件升级(update)而来。如果你创建了一个合适的makefile文件,当你 改变了部分源文件内容时,你可通过执行make命令来重新编译。 make程序通过使用makefile作为配置文件,通过检测文件的更新时间,来决 定哪些文件需要重新编译。你也可以通过给make命令带命令行参数来决定那些文件 需要重新编译。一般,默认使用makefile文件来告诉make命令如何编译和链接程序。 makefile的常见语法规则: 1. target ... : dependencies ... command ... ... target一般为程序所生成的文件名。例如,是可执行文件名或目标文件名;一 个目标也可以是一个将要执行的动作的名称,见下例中的clean。 dependencies就是作为输入文件,被用来生成目标文件。一个目标文件可以由 好几个输入文件编译而成。 command就是make所执行的动作,可以由好几个命令组成,每个命令独占一行。 注意,每个命令行开头必须有一个Tab键,千万不要忘记。 通常,命令是用来编译输入文件以生成目标文件的。当然,命令也可以不需要 任何依赖(dependencies)。除了上述语法规则模块,一个makefile文件可能还包 含其他内容。 2.一个简单的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 解析,\为续行符。edit目标文件依赖main.o、kbd.o、command.o、 display.o、insert.o、search.o、files.o和utils.o等八个文件,并通 过随后的CC命令编译而成,其它类似。执行make命令即可生成edit可执行文件或 上述八个.o文件。执行make clean命令可删除edit可执行文件和上 述八个.o文件。 make命令并不在乎你使用什么编译命令(它由你来决定),它只在乎文 件之间的依赖关系,并重新编译需要更新的文件。目标clean并不是一个文 件,而是一个动作名,如果你不想使用clean,那么它和其他的规则模块没有任何 关系。 3.make如何处理makefile文件: make一般从第一个规则块(rule)开始(以.开头的目标名不能算作一个 rule),被称作默认目标(default goal),也就是make命令要生成的最终 文件。如上例,edit是最终的目标文件,被作为第一个rule。当你发出make命令 后,make命令查找当前目录中的makefile文件,然后开始执行 第一个rule。如上例,第一个rule就是生成edit可执行文件。在完全执行 该rule之前,它先要处理该rule中所有的依赖(dependencies)。而这些依赖 (dependencies)又必须根据它们自己的rule来生成。如果一个目标文件的依 赖文件比它本身更新,或者目标文件根本就不存在,那么将根据对应的rule 重新编译生成新目标文件。除了第一个rule外,其它的rule也能执行,是因 为第一个rule的依赖(dependencies)与其他的rule有关,否则只有第一个 rule被执行;如果你想执行出第一个rule以外的其他独立rule,那么就必须 通过make命令行参数来指明,例如上面的make clean。 4.makefile文件中变量的使用: 例如,我们常常用objects, OBJECTS, objs, OBJS, obj, 或者OBJ, 来 表示目标文件,例如上例,我们可以用: objects = main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o 那么在makefile文件中,可用$(objects)代替上述八个.o文件。 5.让make命令自己推理: make命令有一个隐含的规则,它能通过查找同名的.c文件和cc -c命令来 自动更新.o文件。也就是在一个rule中,能自动把.c文件添加到依赖 (dependencies)列表中。这样上述例子可简写为: 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) 6.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 但是有人也不喜欢它,因为它不能一次看清某个目标文件的依赖情况。 7.删除目录或文件的rule: .PHONY : clean clean : -rm edit $(objects) 其中,.PHONY : clean 可阻止make命令使用一个名为clean的真实文件,具体见 以后分析。 8.怎样编写一个makefile文件: makefile包含五个方面:explicit rules(显式rule), implicit rules(隐式 rule), variable definitions(变量), directives(指示符), 和comments(注释) 。显式rule,指明目标文件的依赖以及创建或更新目标文件所用的命令;隐式 rule通过同名文件和指定命令来创建和更新目标文件;变量的定义见上面的例子 ;指示符是为了帮助make命令在读取makefile文件时作一些特定的事情(见以后分 析);注释行一般用#开头。 9.命名一个makefile文件: 默认情况下,make命令会寻找下列文件:GNUmakefile, makefile和Makefile 。如果你不指定makefile文件,那么你必须指定一个最终的目标文件,make命令 会自动使用内部隐含的rule来编译。如果你不想用默认的makefile文件名,你也 可以通过 -f filename 或 --file=filename,来告诉make应该读取的文件。如果 你用 -f或 --file指定了多个文件,那么它们将按顺序被联接在一起。如果你用 -f或 --file指定,那么将不自动查找默认的makefile等文件。 10.包含其他的makefile文件: include标识符告在诉make命令,在读makefile文件之前,要先读若干个其他 的makefile文件。include标识符使用方法为:include filenames...。在每行开 头的空格被忽略。但是不能以tab健开头,否则可能会被当作一个命令行。在 include和filename之间必须要用空格隔开。当make处理一个include标识符时, 它先暂缓处理当前的makefile,转而读该标识符所列举的文件。当这些文件被处 理完毕后,make接着读被悬挂的makefile文件。 11.假冒的目标(phony target): 一个假冒目标不是一个真正的文件名,它仅仅表示某些特定make命令的扩展 ,用假冒的目标文件有两个原因:避免和同名文件发生冲突。例如: clean: rm *.o temp 因为rm命令不能创建一个clean文件,可能永远都没有那样的文件存在。因此,每 当你执行make clean命令时,rm命令都会被执行。但是如果有一个clean文件存在 了,那么这个假冒的目标将不会工作。因为它没有依赖,所以所存在的clean文件 被认为是最新的,而导致rm命令不能被执行。为了避免这个问题,可以显式申明 该目标是phony类型,即: .PHONY: clean clean: rm *.o temp 申明以后,就可以知道clean并非一个真正的文件,在每次执行make clean时,它 都必须被执行。当然,Phony targets也可以有依赖。当一个目录包含几个程序时 ,在一个makefile文件中描述所有这些程序也很方便。用all假冒目标来表示,例 如: all : prog1 prog2 prog3 .PHONY : all prog1 : prog1.o utils.o cc -o prog1 prog1.o utils.o prog2 : prog2.o cc -o prog2 prog2.o prog3 : prog3.o sort.o utils.o cc -o prog3 prog3.o sort.o utils.o 现在只需要用make命令就可以重新编译这三个文件,也可用参数来指定编译特定 的文件,例如make prog1 prog3。 12.主要定义变量: $* 不包含扩展名的目标文件名称。 $+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的 依赖文件。 $< 第一个依赖文件的名称。 $? 所有依赖文件,以空格分开,这些依赖文件的修改日期比目标文件的创 建日期晚。 $@ 目标的完整名称。 $^ 所有的依赖文件,以空格分开,不包含重复的依赖文件。 $% 如果目标是归档成员,则该变量表示目标的归档成员名。 13.用户的标准目标: 所有的GNU程序都应该有些列目标(部分略): all: 编译全部程序。它应该是缺省的目标。 install: 编译全部程序,并把可执行文件,库文件等文件拷贝到它应该在的 目录。 clean: 删除文件。 欢迎访问FREOS项目主页 *********************minix的boot目录下makefile解析******************* # Makefile for the boot monitor package. 注释 # CC= exec cc 命令 CC86= exec cc -mi86 -Was-ncc CFLAGS= -I.. -m -O LD= $(CC) -s -.o LD86= $(CC86) -.o BIN= /usr/bin MDEC= /usr/mdec all: bootblock boot masterboot extboot installboot edparams 目标文件 installboot.o bootimage.o: image.h boot.o bootimage.o: boot.h rawfs.o rawfs86.o installboot.o boot.o bootimage.o: rawfs.h 申明依赖关系 bootblock: bootblock.s $(LD86) bootblock.s -o bootblock masterboot: masterboot.s $(LD86) masterboot.s -o masterboot extboot: extboot.s $(LD86) extboot.s -o extboot boot.o: boot.c $(CC86) $(CFLAGS) -c boot.c bootimage.o: bootimage.c $(CC86) $(CFLAGS) -c bootimage.c rawfs86.o: rawfs.c rawfs.o ln -f rawfs.c rawfs86.c $(CC86) $(CFLAGS) -c rawfs86.c rm rawfs86.c -cmp -s rawfs.o rawfs86.o && ln -f rawfs.o rawfs86.o boot: boothead.s boot.o bootimage.o rawfs86.o $(LD86) boothead.s -i boot.o bootimage.o rawfs86.o -o boot install -S 12kb boot installboot: installboot.o rawfs.o $(CC) $(STRIP) -i -o installboot installboot.o rawfs.o install -S 6kw installboot edparams: edparams.c $(CC) $(CFLAGS) $(STRIP) -i -o edparams edparams.c install -S 16kw edparams install: $(MDEC)/bootblock $(MDEC)/boot $(MDEC)/masterboot \ $(MDEC)/extboot $(BIN)/installboot $(BIN)/edparams 执行make install命令才执行? $(MDEC)/bootblock: bootblock install -cs -o bin -m 644 $? $@ $(MDEC)/boot: boot install -cs -o bin -m 644 $? $@ $(MDEC)/masterboot: masterboot install -cs -o bin -m 644 $? $@ $(MDEC)/extboot: extboot install -cs -o bin -m 644 $? $@ $(BIN)/installboot: installboot install -cs -o bin $? $@ $(BIN)/edparams: edparams install -cs -o bin $? $@ clean: rm -f *.bak *.o rm -f bootblock installboot boot masterboot extboot edparams |
|