您的位置:首页 > 其它

makefile学习(一)

2012-09-16 12:53 141 查看
为了使得rtems的源代码在自己的编译器上能够编译通过,不得不写makefile,否则对每个C文件的改动都要手动去编译,没有makefile只会感到越来越麻烦。
一。刚开始,没想那么多,就用sed提取出所有的C文件的文件名,然后借助sed的强大之处,一条sed命令生成冗长的一个makefile,比如有一个xxx.c文件,我就用sed命令把他处理成:

xxx.o: xxx.c
cc -c xxx.c


这样一来,有多少个c文件就有多少个以上操作,想想吧,一个makefile有几十k。
二。这样虽然看起来也简单,但是当我去静下心来看下makefile的相关文章之后,发现了自己的愚蠢之处,一条语句就可以解决的,这就是所谓的静态模式,例如,我们有a.c,b.c这两个源文件,我就可以这样写:

objects = a.o b.o

all: $(objects)

$(objects): %.o: %.c
$(CC) -c  $< -o $@

    上面的规则等价于以下规则:
a.o : a.c
$(CC) -c a.c -o a.o
b.o : b.c
$(CC) -c b.c -o b.o


      这样一来,我们的makefile就一条规则就搞定了,只有1k的大小。解释一下上面的代码:上面的例子中,指明了我们的目标从$objects中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“a.o b.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“a b”,并为其加下“.c”的后缀,于是,我们的依赖目标是“a.c
b.c”。而命令中的“$<”和“$@”则是自动化变量,“$<”表示所有的依赖目标集(也就是“a.c b.c”),“$@”表示目标集(也就是“a.o b.o“)。
三。以上所述部分,我没有提及头文件。我们现在要修改a.c,make一下,makefile会很智能的去执行命令

$(CC) -c a.c -o a.o


而不会去管其他没有被修改的文件。但是,在我们写的程序中,特别是一个操作系统中,头文件肯定是少不了的,如果a.c中包含a.h,b.c中包含b.h,现在修改a.h或者b.h试试看,make一下,结果会发现makefile啥都没做,但是我明明修改了文件啊,为什么make它没反应呢??对!!可以,让a.o和b.o分别依赖各自的头文件啊:

a.o : a.c a.h
$(CC) -c a.c -o a.o
b.o : b.c b.h
$(CC) -c b.c -o b.o

      确实,再试试,修改一个头文件,make一下,makefile会去做我们想要它做的工作了。

      然而,这样些会有很多的麻烦。
      首先,这样写岂不是又回到了,我第一条说的,又把一个makefile没必要的写成了冗长冗长的样子。有人可能会说,可以用隐含规则去减少行数,比如将以上代码修改如下:

a.o : a.c a.h
b.o : b.c b.h

这样真的很简洁明了,代码量又少了。
      那么,第二个麻烦,恐怕你就没办法解决了。如果有成千上百个c文件,每个c文件里都会包含不同的头文件,你要一个c文件一个c文件的去找头文件吗?这个不靠谱。。。
      有问题就有解决之道:
      比如,有如下的依赖关系存在

main.o: main.c main.h stack.h maze.h
maze.o: maze.c maze.h main.h
stack.o: stack.c stack.h main.h

      GNU make的官方手册建议这样写:(这个解决之道来自”linux c编程一站式学习“,至于现在的官方手册是不是还是这样写,我就不清楚了)

all: main

main: main.o stack.o maze.o
gcc $^ -o $@

clean:
-rm main *.o

.PHONY: clean

sources = main.c stack.c maze.c

include $(sources:.c=.d)

%.d: %.c
set -e; rm -f $@; \
$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$

      用如上的代码,我在ubuntu下试了下,确实是可以行的,能够解决头文件的依赖关系,也就是说,我修改一个头文件,那些依赖这个头文件的c文件都要重新编译。
上述语句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一个替换,把变量$(sources)所有[.c]的字串都替换成[.d]。当然,你得注意次序,因为include
是按次来载入文件,最先载入的[.d]文件中的目标会成为默认目标。
     
解释:(也是来自linux c编程一站式学习)
这个命令写了四行,但其实是一条命令,make只创建一个Shell进程执行这条命令,这条命令分为5个子命令,用分号隔开,并且为了美观,用续行符\拆成四行来写。执行步骤为:
1.   set -e命令设置当前Shell进程为这样的状态:如果它执行的任何一条命令的退出状态非零则立刻终止,不再执行后续命令。
2.   把原来的maze.d删掉。
3.   重新生成maze.c的依赖关系,保存成文件maze.d.1234(假设当前Shell进程的id是1234)。注意,在Makefile中$有特殊含义,如果要表示它的字面意思则需要写两个$,所以Makefile中的四个$传给Shell变成两个$,两个$在Shell中表示当前进程的id,一般用它给临时文件起名,以保证文件名唯一。
4.   这个sed命令比较复杂,就不细讲了,主要作用是查找替换。maze.d.1234的内容应该是maze.o: maze.c maze.h main.h,经过sed处理之后存为maze.d,其内容是maze.o maze.d: maze.c maze.h main.h。
5.   最后
ad7a
把临时文件maze.d.1234删掉。
不管是Makefile本身还是被它包含的文件,只要有一个文件在make过程中被更新了,make就会重新读取整个Makefile以及被它包含的所有文件,现在main.d、stack.d和maze.d都生成了,就可以正常包含进来了(假如这时还没有生成,make就要报错而不是报警告了),相当于在Makefile中添了三条规则:

main.o main.d: main.c main.h stack.h maze.h
maze.o maze.d: maze.c maze.h main.h
stack.o stack.d: stack.c stack.h main.h

如果我在main.c中加了一行#include "foo.h",那么:
1.   main.c的修改日期变了,根据第一条规则要重新生成main.o和main.d。
2.   现在main.d的内容更新为main.o main.d: main.c main.h stack.h maze.h foo.h。
3.   由于main.d被Makefile包含,main.d被更新又导致make重新读取整个Makefile,把新的main.d包含进来,于是新的依赖关系生效了。
这个所谓的官方提供的方案中的sed命令跟我平时用的语法有点不同,看起来很不爽,我就把它修改了我自己习惯的sed语句了:


 
四。悲催的是,我们自己的编译器是跑在windows下的,虽然支持makefile,但是不支持sed。
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: