Makefile 生成.d 头文件依赖规则

下面是以我写的实际项目中的测试程序的Makefile为例进行说明

我们可以把要生成.d文件的gcc指令单独做成一个规则,如下$(OBJS_DIR)/%.d:%.c

CC:=gcc
CFLAGS:=-std=gnu99 -g
LDFLAGS:= -lpthread

OBJS_DIR:=./objs

MEIHE_SRCS:= mt_test_meihe.c ry_hj_frame.c CL_PduCtrl.c OSAL_Error.c mt_tcp_parse.c

MEIHE_OBJS:=$(addprefix ${OBJS_DIR}/, $(MEIHE_SRCS:.c=.o))

MEIHE_DEPS=$(patsubst %.o, %.d, $(MEIHE_OBJS))

MEIHE_OUTPUT:=./mt_meihe

.PHONY: clean

build: mk-dirs $(MEIHE_OUTPUT)

$(MEIHE_OUTPUT):$(MEIHE_OBJS)
	$(CC) -o $@  $^ $(LDFLAGS)

$(OBJS_DIR)/%.o: %.c
	$(CC) -I./ -c $< -o $@ $(CFLAGS)

$(OBJS_DIR)/%.d:%.c
	$(CC) $< -MM -MF $(OBJS_DIR)/$*.d -MP

mk-dirs:
	if [ ! -d $(OBJS_DIR) ] ; then mkdir -p $(OBJS_DIR); fi

clean:
	rm -rf $(OBJS_DIR)/* $(MEIHE_OUTPUT)

include $(MEIHE_DEPS)

运行结果为:

test_mk:30: objs/mt_test_meihe.d: No such file or directory
test_mk:30: objs/ry_hj_frame.d: No such file or directory
test_mk:30: objs/CL_PduCtrl.d: No such file or directory
test_mk:30: objs/OSAL_Error.d: No such file or directory
test_mk:30: objs/mt_tcp_parse.d: No such file or directory
gcc mt_tcp_parse.c -MM -MF ./objs/mt_tcp_parse.d -MP
gcc OSAL_Error.c -MM -MF ./objs/OSAL_Error.d -MP
gcc CL_PduCtrl.c -MM -MF ./objs/CL_PduCtrl.d -MP
gcc ry_hj_frame.c -MM -MF ./objs/ry_hj_frame.d -MP
gcc mt_test_meihe.c -MM -MF ./objs/mt_test_meihe.d -MP
if [ ! -d ./objs ] ; then mkdir -p ./objs; fi
gcc -I./ -c mt_test_meihe.c -o objs/mt_test_meihe.o -std=gnu99 -g
gcc -I./ -c ry_hj_frame.c -o objs/ry_hj_frame.o -std=gnu99 -g
gcc -I./ -c CL_PduCtrl.c -o objs/CL_PduCtrl.o -std=gnu99 -g
gcc -I./ -c OSAL_Error.c -o objs/OSAL_Error.o -std=gnu99 -g
gcc -I./ -c mt_tcp_parse.c -o objs/mt_tcp_parse.o -std=gnu99 -g
gcc -o mt_meihe  objs/mt_test_meihe.o objs/ry_hj_frame.o objs/CL_PduCtrl.o objs/OSAL_Error.o objs/mt_tcp_parse.o -lpthread

可以看到,include刚开始没有找到各个.d文件,然后发现Makefile中有生成.d文件的规则,所以调用规则生成.d文件。
生成规则的指令如下:

gcc mt_tcp_parse.c -MM -MF ./objs/mt_tcp_parse.d -MP
gcc OSAL_Error.c -MM -MF ./objs/OSAL_Error.d -MP
gcc CL_PduCtrl.c -MM -MF ./objs/CL_PduCtrl.d -MP
gcc ry_hj_frame.c -MM -MF ./objs/ry_hj_frame.d -MP
gcc mt_test_meihe.c -MM -MF ./objs/mt_test_meihe.d -MP

-MM 参数用于生成文件的依赖关系,和 -M 类似,但不包含标准库的头文件

-MF 参数用于指定,依赖关系输出到哪个文件,

-MP 生成的依赖文件里面,依赖规则中的所有.h依赖项都会在该文件中生成一个伪目标,其不依赖任何其他依赖项。该伪规则将避免删除了对应的头文件而没有更新 “Makefile” 去匹配新的依赖关系而导致make出错的情况出现。任意找一个.d文件,

CL_PduCtrl.d文件如下;

CL_PduCtrl.o: CL_PduCtrl.c CL_PduCtrl.h OSAL_Comdef.h OSAL.h

CL_PduCtrl.h:

OSAL_Comdef.h:

OSAL.h:

我自己的理解,这些伪目标是否除了上述的用途,还可以用于防止Makefile寻找隐式规则去匹配头文件,提高效率,但参考其他开源工程(这里参考u-boot顶层Makefile,332行)里面防止隐式规则起作用的语法,应该在后面加分好格式如下:

scripts/Kbuild.include: ;
include scripts/Kbuild.include

另外,此时的gcc命令,只用于生成.d文件,一般用的时候可以.d文件和.o文件一起生成,不用将生成.d文件的目标单独列出来,你可能会想,那第一次编译的时候,还没有生成.d文件,会不会不严谨,其实第一次编译的时候完全可以不用.d文件,.d文件是给后续修改头文件后,再去编译工程使用的。 另外仔细想想,其实如果.c文件中#include语句,增加新的头文件或者删除旧的头文件(  -MP参数,生成的头文件伪目标可以保证不出错)也不会出错。将上述Makefile修改如下:

CC:=gcc
CFLAGS:=-std=gnu99 -g
LDFLAGS:= -lpthread

OBJS_DIR:=./objs

MEIHE_SRCS:= mt_test_meihe.c ry_hj_frame.c CL_PduCtrl.c OSAL_Error.c mt_tcp_parse.c

MEIHE_OBJS:=$(addprefix ${OBJS_DIR}/, $(MEIHE_SRCS:.c=.o))

MEIHE_DEPS=$(patsubst %.o, %.d, $(MEIHE_OBJS))

MEIHE_OUTPUT:=./mt_meihe

.PHONY: clean

build: mk-dirs $(MEIHE_OUTPUT)

$(MEIHE_OUTPUT):$(MEIHE_OBJS)
	$(CC) -o $@  $^ $(LDFLAGS)

$(OBJS_DIR)/%.o: %.c
    $(CC) -I./ -c $< -o $@ $(CFLAGS) -MD -MF $(OBJS_DIR)/$*.d -MP


mk-dirs:
	if [ ! -d $(OBJS_DIR) ] ; then mkdir -p $(OBJS_DIR); fi

clean:
	rm -rf $(OBJS_DIR)/* $(MEIHE_OUTPUT)

-include $(MEIHE_DEPS)

将规则$(OBJS_DIR)/%.d:%.c删除,$(OBJS_DIR)/%.o: %.c修改如下

$(OBJS_DIR)/%.o: %.c
    $(CC) -I./ -c $< -o $@ $(CFLAGS) -MD -MF $(OBJS_DIR)/$*.d -MP

include $(MEIHE_DEPS)修改为-include $(MEIHE_DEPS)

输出结果如下:

if [ ! -d ./objs ] ; then mkdir -p ./objs; fi
gcc -I./ -c mt_test_meihe.c -o objs/mt_test_meihe.o -std=gnu99 -g -MD -MF ./objs/mt_test_meihe.d -MP
gcc -I./ -c ry_hj_frame.c -o objs/ry_hj_frame.o -std=gnu99 -g -MD -MF ./objs/ry_hj_frame.d -MP
gcc -I./ -c CL_PduCtrl.c -o objs/CL_PduCtrl.o -std=gnu99 -g -MD -MF ./objs/CL_PduCtrl.d -MP
gcc -I./ -c OSAL_Error.c -o objs/OSAL_Error.o -std=gnu99 -g -MD -MF ./objs/OSAL_Error.d -MP
gcc -I./ -c mt_tcp_parse.c -o objs/mt_tcp_parse.o -std=gnu99 -g -MD -MF ./objs/mt_tcp_parse.d -MP
gcc -o mt_meihe  objs/mt_test_meihe.o objs/ry_hj_frame.o objs/CL_PduCtrl.o objs/OSAL_Error.o objs/mt_tcp_parse.o -lpthread

可以看到此时的gcc命令是同时生成.o文件以及.d文件,此时gcc的参数从 -MM换成了-MD,还增加了其他用于生成.o文件的选项。

-MD参数也用于生成文件的依赖关系,但不会阻止编译过程,-M 或者-MM会在预编译阶段结束后自动停止,但是-MD不会停止,会继续编译成成.o文件。其实这里最好用-MMD,-MD参数和-M参数一样也会将标准头文件也列入依赖关系。

详细的gcc参数说明,参见https://blog.csdn.net/nawenqiang/article/details/83381237

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值