一般来说,无论是C还是C++,首先要把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。编译时,编译器需要的是语法的正确,函数与变量的声明的正确。只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件( .o 文件或 .obj 文件)。链接时,主要是链接函数和全局变量。所以,我们可以使用这些中间目标文件( .o 文件或 .obj 文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File)
基本写法:
target ... : prerequisites..
command
核心规则:执行make时,make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。makefile只有一个最终目标,第一条规则的目标为最终目标
示例:一个包含3个头文件和8个c文件的的工程的Makefile内容
# 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
注:clean不是文件,它是一个动作,冒号后面什么也没有
“伪目标”并不是一个文件,只是一个标签(label),由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显式地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义。为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显式地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”
稳健的make clean做法
.PHONY : clean
clean :
-rm edit $(objects)
.PHONY 表示 clean 是一个“伪目标”。而在 rm 命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然, clean 的规则不要放在文件的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”。只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只有“make clean”这样
伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第一个。 如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,可以使用伪目标的特性
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
解释:Makefile中第一个目标是默认目标,即all
。make通过隐式规则推导出all
是一个伪目标,伪目标是一个标签不会生成文件,所以不会产生all
文件,但是all
依赖于其它三个目标,于是,其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。
这里的显式.PHONY : all
不写的话一般情况也可以正确的执行,这样make可通过隐式规则推导出, “all” 是一个伪目标,执行make不会生成“all”文件,而执行后面的多个目标。建议:显式写出是一个好习惯。
伪目标也可以成为依赖
如
.PHONY : cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
make cleanall
将清除所有要被清除的文件。cleanobj
和cleandiff
这两个伪目标有点像“子程序”的意思。我们可以输入make cleanall
和make cleanobj
和make cleandiff
命令来达到清除不同种类文件的目的
如果要使用隐含规则生成你需要的目标,你所需要做的就是不要写出这个目标的规则。那么,make会试图去自动推导产生这个目标的规则和命令,如果 make可以自动推导生成这个目标的规则和命令,那么这个行为就是隐含规则的自动推导。
如
foo : foo.o bar.o
cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
等价于
foo : foo.o bar.o
cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
foo.o : foo.c
cc –c foo.c $(CFLAGS)
bar.o : bar.c
cc –c bar.c $(CFLAGS)
常用隐含规则
$(CC) –c $(CPPFLAGS) $(CFLAGS)
$(CXX) –c $(CPPFLAGS) $(CXXFLAGS)
。(建议使用 .cc 作为C++源文件的后缀,而不是 .C )$(CC) $(LDFLAGS) .o $(LOADLIBES) $(LDLIBS)
。这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不同的源文件生成)的也有效还含有其它规则,这些规则在make执行中是有顺序也就是优先级的
隐含规则总使用的变量
在隐含规则中的命令中,基本上都是使用了一些预先设置的变量。你可以在你的makefile中改变这些变量的值,或是在make的命令行中传入这些值,或是在你的环境变量中设置这些值,无论怎么样,只要设置了这些特定的变量,那么其就会对隐含规则起作用。
例如,第一条隐含规则——编译C程序的隐含规则的命令是 $(CC) –c $(CFLAGS) $(CPPFLAGS)
。Make默认的编译命令是 cc
,如果你把变量 $(CC)
重定义成 gcc
,把变量 $(CFLAGS)
重定义成 -g
,那么,隐含规则中的命令全部会以 gcc –c -g $(CPPFLAGS)
的样子来执行了。
变量分为命令相关(如CC),参数相关(CFLAGS),无默认值则为空