一、基础知识

在进行下一步之前,首先需要了解make的执行步骤:

1、读入Makefile
2、读入被include的其它Makefile
3、初始化Makefile中的变量
4、推导隐晦规则,并分析所有规则
5、为所有目标创建依赖关系链
6、根据依赖关系,决定哪些目标需要重新生成
7、执行生成命令

二、解决头文件依赖的Makefile模版

源文件目录结构:

[root@localhost hello]# tree
.
|-- Makefile
|-- hello.c
|-- hello.h
`-- main.c

其中main.c中依赖hello.h

Makefile模版

CC      = gcc
CFLAGS  = -Wall -O
INCLUDEFLAGS =
LDFLAGS =
OBJS    = main.o hello.o
TARGETS = hello

.PHONY:all
all : $(TARGETS)

encrypt:$(OBJS)
    $(CC) -o $@ $^ $(LDFLAGS)

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

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

-include $(OBJS:.o=.d)

.PHONY:clean
clean:
    rm -f $(TARGETS) *.o *.d *.d.*

三、依赖说明

主要处理头文件依赖的代码为

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

-include $(OBJS:.o=.d)

举例说明:

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

-include seq.d

生成规则中的执行命令解释

(1)第一个命令@set -e。@关键字告诉make不输出该行命令;set -e的作用是,当后面的命令的返回值非0时,立即退出。

那么为什么要把几个命令写在”同一行“(是对于make来说,因为的作用就是连接行),并用分号隔开每个命令?因为在Makefile这样做才能使上一个命令作用于下一个命令。这里是想要set -e作用于后面的命令。

(2)第二个命令gcc -MM $< > $@.$$$$, 作用是根据源文件生成依赖关系,并保存到临时文件中。内建变量$<的值为第一个依赖文件(那seq.c),$$$$为字符串"$$",由于makefile中所有的$字符都是特殊字符(即使在单引号之中!),要得到普通字符$,需要用$$来转义; 而$$是shell的特殊变量,它的值为当前进程号;使用进程号为后缀的名称创建临时文件,是shell编程常用做法,这样可保证文件唯一性。

(3)第三个命令作用是将目标文件加入依赖关系的目录列表中,并保存到目标文件。关于正则表达式部分就不说了,唯一要注意的是内建变量$*,$*的值为第一个依赖文件去掉后缀的名称(这里即是seq)。

(4)第四个命令是将该临时文件删除。

如果把内建变量都替换成其值后,实际内容是这样子:

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

-include seq.d