首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

makefile 入门指南范例——深度优先迷宫搜索

2013-09-08 
makefile 入门指南实例——深度优先迷宫搜索摘要,本文将以深入优先,搜索迷宫为例,讲解makefile的用法,基本规

makefile 入门指南实例——深度优先迷宫搜索

摘要,本文将以深入优先,搜索迷宫为例,讲解makefile的用法,基本规则与隐含规则,模式规则;makefile的处理过程与原理;变量的定义,如何自动生成头文件依赖关系等。


本文来源:http://blog.csdn.net/trochiluses/article/details/11179191
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中第一条规则的目标clean:@echo "cleanning project"-rm main *.o@echo "clean completed"

        把这条规则添加到我们的Makefile末尾,然后执行这条规则:


    $ make clean cleanning projectrm main *.oclean completed

        如果在.PHONY: clean

        这条规则没有命令列表。类似clean:@echo "cleanning project"-rm main *.o@echo "clean completed".PHONY: clean2.3makefile的处理过程与原理:(附录2)


    3.隐含规则和模式规则


    3.1问题:main.o依赖于main.c是一个common sense,有无方法来来解决这个冗余?

    答案:

        如果一个目标拆开写多条规则,其中只有一条规则允许有命令列表,其它规则应该没有命令列表,否则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: clean

        这不是比原来更繁琐了吗?现在可以把提出来的三条规则删去,写成:

    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.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

        现在解释一下前三条编译命令是怎么来。如果一个目标在Makefile中的所有规则都没有命令列表,# 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.c


    4.变量的语法规则


    1)= 延迟定义

    foo = $(bar) bar = Huh? all: @echo $(foo)
    输出:Huh?

    2):=立即展开

    y := $(x) barx := fooall:        @echo $(y)
    ouput: bar

    3)?= 条件赋值

    objects = main.oobjects += $(foo)foo = foo.o bar.o

    objects := 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.h

        接下来的问题是怎么把这些规则包含到Makefile中,GNU all: 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 $@.$$$$    关于上述语法的含义,可以参见附录3


    附录


    附录3:

    include main.d stack.d maze.d

    类似于C语言的$ 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 main

    一开始找不到set -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.$$

    注意,虽然在Makefile中这个命令写了四行,但其实是一条命令,

      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.h

      如果我在main.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 $@.$$$$

      因此

        首先从前到后读取所有规则,建立起一个完整的依赖关系图,例如:

        图 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.html