规则
target ... : prerequisites ...
recipe
...
...
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
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)
命令的Tab键位不可用空个代替。
在我们的 makefile 中以 $(objects) 的方式来使用这个变量
反斜杠(\ )是换行符的意思。
.PHONY 表示 clean 是一个“伪目标”。
一个小减号的意思就是,也 许某些文件出现问题,但不要管,继续做后面的事
不成文的规矩是——“clean 从来都是放在文件的最 后”。
显式规则、隐式规则、变量定义、指令和注释。
另给一个变量使用通配符的例子:
列出一确定文件夹中的所有 .c 文件。
objects := $(wildcard *.c)
列出 (1) 中所有文件对应的 .o 文件,在(3)中我们可以看到它是由 make 自动编译出的:
$(patsubst %.c,%.o,$(wildcard *.c))
由 (1)(2) 两步,可写出编译并链接所有 .c 和 .o 文件
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)文件搜寻
vpath
为符合模式
vpath
清除符合模式
vpath
清除所有已被设置好了的文件搜索目录。
vpath 使用方法中的
vpath %.h ../headers
当然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显式地指明 一个目标是“伪目标”,向 make 说明,不管是否有这个文件,这个目标就是“伪目标”。 .PHONY : clean 只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只有“make clean”这样。 于是整个过程可以这样写:
.PHONY : clean
clean :
rm *.o temp
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
等价于
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
上面的例子中,指明了我们的目标从 $object 中获取,%.o 表明要所有以 .o 结尾的目标,也就是 foo.o bar.o ,也就是变量 $object 集合的模式,而依赖模式 %.c 则取模式 %.o 的 % ,也就是 foo bar ,并为其加下 .c 的后缀,于是,我们的依赖目标就是 foo.c bar.c 。而命令中的 $< 和 @ 则是自动化变量, @ 则是自动化 变量, @则是自动化变量,< 表示第一个依赖文件,$@ 表示目标集(也就是“foo.o bar.o”)。于是,上面的规则展开后等价 于下面的规则:
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
试想,如果我们的 %.o 有几百个,那么我们只要用这种很简单的“静态模式规则”就可以写完一堆 规则,实在是太有效率了。“静态模式规则”的用法很灵活,如果用得好,那会是一个很强大的功能。再 看一个例子:
files = foo.elc bar.o lose.o
$(filter %.o,$(files)): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $<
( f i l t e r (filter %.o, (filter(files)) 表示调用 Makefile 的 filter 函数,过滤“$files”集,只要其中模式为“%.o”的 内容。其它的内容,我就不用多说了吧。这个例子展示了 Makefile 中更大的弹性。
在 Makefile 中,我们的依赖关系可能会需要包含一系列的头文件,比如,如果我们的 main.c 中有一 句 #include “defs.h” ,那么我们的依赖关系应该是:
main.o : main.c defs.h
但是,如果是一个比较大型的工程,你必需清楚哪些 C 文件包含了哪些头文件,并且,你在加入或 删除头文件时,也需要小心地修改 Makefile,这是一个很没有维护性的工作。为了避免这种繁重而又容 易出错的事情,我们可以使用 C/C++ 编译的一个功能。大多数的 C/C++ 编译器都支持一个“-M”的 选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。例如,如果我们执行下面的命令:
cc -M main.c
其输出是:
main.o : main.c defs.h
于是由编译器自动生成的依赖关系,这样一来,你就不必再手动书写若干文件的依赖关系,而由编 译器自动生成了。需要提醒一句的是,如果你使用 GNU 的 C/C++ 编译器,你得用 -MM 参数,不然,-M 参数会把一些标准库的头文件也包含进来。
gcc -M main.c 的输出是
main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \
/usr/include/bits/sched.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/include/bits/wchar.h /usr/include/gconv.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \
/usr/include/bits/stdio_lim.h
gcc -MM main.c 的输出则是:
main.o: main.c defs.h