makefile学习(一)


为了使得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.   最后把临时文件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。

 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值