makefile 入门指南实例——深度优先迷宫搜索
摘要,本文将以深入优先,搜索迷宫为例,讲解makefile的用法,基本规则与隐含规则,模式规则;makefile的处理过程与原理;变量的定义,如何自动生成头文件依赖关系等。
1.为什么要写makefile
现在,我们有6个C文件和h文件,它们的详细内容可以参考这里,也可以在我的资源里进行打包下载实验,它们之间的依赖关系如下:
/* main.c */#include <stdio.h>#include "main.h"#include "stack.h"#include "maze.h"
/* main.h */#ifndef MAIN_H#define MAIN_Htypedef struct point { int row, col; } item_t;#define MAX_ROW 5#define MAX_COL 5#endif/* stack.c */#include "stack.h"
/* stack.h */#ifndef STACK_H#define STACK_H#include "main.h" /* provides definition for item_t */extern void push(item_t);extern item_t pop(void);extern int is_empty(void);#endif
/* maze.c */#include <stdio.h>#include "maze.h"
/* maze.h */#ifndef MAZE_H#define MAZE_H#include "main.h" /* provides defintion for MAX_ROW and MAX_COL */extern int maze[MAX_ROW][MAX_COL];void print_maze(void);#endif为了编译这个程序,我们每次都要输出如下命令
$ gcc -c main.c$ gcc -c stack.c$ gcc -c maze.c$ gcc main.o stack.o maze.o -o main问题:但是,如果这些文件之间的依赖关系有所变化,如果我仅仅更改了maize.h,那么我需要更新哪些文件呢?
解决方法:写一个makefile,表明这些文件之间的依赖关系,从而仅仅更新必要的文件,makefile的内容如下:
main: main.o stack.o maze.ogcc main.o stack.o maze.o -o mainmain.o: main.c main.h stack.h maze.hgcc -c main.cstack.o: stack.c stack.h main.hgcc -c stack.cmaze.o: maze.c maze.h main.hgcc -c maze.c
2.基本规则
2.1问题:如果写makefile,它的基本语法?
答案:
main: main.o stack.o maze.ogcc main.o stack.o maze.o -o main
尝试更新Makefile中第一条规则的目标 把这条规则添加到我们的Makefile末尾,然后执行这条规则: 如果在 这条规则没有命令列表。类似 3.1问题:main.o依赖于main.c是一个common sense,有无方法来来解决这个冗余? 答案: 如果一个目标拆开写多条规则,其中只有一条规则允许有命令列表,其它规则应该没有命令列表,否则 这不是比原来更繁琐了吗?现在可以把提出来的三条规则删去,写成: 这就比原来简单多了。可是现在 现在解释一下前三条编译命令是怎么来。如果一个目标在Makefile中的所有规则都没有命令列表, 1)= 延迟定义 2):=立即展开 3)?= 条件赋值 接下来的问题是怎么把这些规则包含到Makefile中,GNU 附录 附录3: 类似于C语言的 一开始找不到 注意,虽然在Makefile中这个命令写了四行,但其实是一条命令, 如果我在 第一条是把规则 因此 首先从前到后读取所有规则,建立起一个完整的依赖关系图,例如: 图 22.1. Makefile的依赖关系图 目标没有生成。 某个条件需要更新。 某个条件的修改时间比目标晚。 在一条规则被执行之前,规则的条件可能处于以下三种状态之一: 需要更新。能够找到以该条件为目标的规则,并且该规则中目标需要更新。 不需要更新。能够找到以该条件为目标的规则,但是该规则中目标不需要更新;或者不能找到以该条件为目标的规则,并且该条件已经生成。 错误。不能找到以该条件为目标的规则,并且该条件没有生成。 执行一条规则A的步骤如下: 检查它的每个条件P: 如果P需要更新,就执行以P为目标的规则B。之后,无论是否生成文件P,都认为P已被更新。 如果找不到规则B,并且文件P已存在,表示P不需要更新。 如果找不到规则B,并且文件P不存在,则报错退出。 在检查完规则A的所有条件后,检查它的目标T,如果属于以下情况之一,就执行它的命令列表: 文件T不存在。 文件T存在,但是某个条件的修改时间比它晚。 某个条件P已被更新(并不一定生成文件) 外部参考:http://learn.akae.cn/media/ch22s04.htmlclean:@echo "cleanning project"-rm main *.o@echo "clean completed"$ make clean cleanning projectrm main *.oclean completed
.PHONY: cleanclean:@echo "cleanning project"-rm main *.o@echo "clean completed".PHONY: clean2.3makefile的处理过程与原理:(附录2)main: main.o stack.o maze.ogcc main.o stack.o maze.o -o mainmain.o: main.h stack.h maze.hstack.o: stack.h main.hmaze.o: maze.h main.hmain.o: main.cgcc -c main.cstack.o: stack.cgcc -c stack.cmaze.o: maze.cgcc -c maze.cclean:-rm main *.o.PHONY: cleanmain: main.o stack.o maze.ogcc main.o stack.o maze.o -o mainmain.o: main.h stack.h maze.hstack.o: stack.h main.hmaze.o: maze.h main.hclean:-rm main *.o.PHONY: clean
$ makecc -c -o main.o main.ccc -c -o stack.o stack.ccc -c -o maze.o maze.cgcc main.o stack.o maze.o -o main# defaultOUTPUT_OPTION = -o $@# defaultCC = cc# defaultCOMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c%.o: %.c# commands to execute (built-in): $(COMPILE.c) $(OUTPUT_OPTION) $<main.o: main.ccc -c -o main.o main.cfoo = $(bar) bar = Huh? all: @echo $(foo)
输出:Huh?y := $(x) barx := fooall: @echo $(y)
ouput: barobjects = main.oobjects += $(foo)foo = foo.o bar.oobjects := main.oobjects += $(foo)foo = foo.o bar.o$ gcc -MM *.cmain.o: main.c main.h stack.h maze.hmaze.o: maze.c maze.h main.hstack.o: stack.c stack.h main.hall: mainmain: main.o stack.o maze.ogcc $^ -o $@clean:-rm main *.o.PHONY: cleansources = main.c stack.c maze.cinclude $(sources:.c=.d)%.d: %.cset -e; rm -f $@; \$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \rm -f $@.$$$$ 关于上述语法的含义,可以参见附录3include main.d stack.d maze.d$ makeMakefile:13: main.d: No such file or directoryMakefile:13: stack.d: No such file or directoryMakefile:13: maze.d: No such file or directoryset -e; rm -f maze.d; \cc -MM maze.c > maze.d.$$; \sed 's,\(maze\)\.o[ :]*,\1.o maze.d : ,g' < maze.d.$$ > maze.d; \rm -f maze.d.$$set -e; rm -f stack.d; \cc -MM stack.c > stack.d.$$; \sed 's,\(stack\)\.o[ :]*,\1.o stack.d : ,g' < stack.d.$$ > stack.d; \rm -f stack.d.$$set -e; rm -f main.d; \cc -MM main.c > main.d.$$; \sed 's,\(main\)\.o[ :]*,\1.o main.d : ,g' < main.d.$$ > main.d; \rm -f main.d.$$cc -c -o main.o main.ccc -c -o stack.o stack.ccc -c -o maze.o maze.cgcc main.o stack.o maze.o -o mainset -e; rm -f maze.d; \cc -MM maze.c > maze.d.$$; \sed 's,\(maze\)\.o[ :]*,\1.o maze.d : ,g' < maze.d.$$ > maze.d; \rm -f maze.d.$$main.o main.d: main.c main.h stack.h maze.hmaze.o maze.d: maze.c maze.h main.hstack.o stack.d: stack.c stack.h main.hmain.o: main.c main.h stack.h maze.h%.o: %.c# commands to execute (built-in): $(COMPILE.c) $(OUTPUT_OPTION) $<main.d: main.c main.h stack.h maze.h%.d: %.cset -e; rm -f $@; \$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \rm -f $@.$$$$