对于Makefile,有位大神写得由浅到深,非常好,如下是链接
https://blog.csdn.net/weixin_42462202/article/details/88584668
当我们仅仅只修改了project中的头文件时,如果引用头文件的.c文件对应的目标文件都已经生成了,直接编译,make并不能发现头文件的修改,所以我们有如下的解决办法
1、把修改的头文件的对应的已经存在的目标文件删掉,重新生成目标文件,我们通常是直接make clean。但是这种方法有个不好的问题,如果我们project非常庞大,比如我们只是修改了内核中的某个文件的头文件,重新编译非常耗费时间,所以我们需要一种更好的办法
2、指明每一个目标文件的依赖关系
1 CC = gcc
2
3 OBJS := a.o
4 OBJS += b.o
5
6 all: $(OBJS)
7 $(CC) -o test $^
8
9 %.o:%.c %.h
10 $(CC) -c -O2 -o $@ $<
我的这个测试程序里a.o和b.o都依赖头文件a.h,所以我在%.o:%.c后面指定 依赖 %.h。
同样如果一个工程非常庞大,而每个.c文件可能会依赖很多个.h文件,所以这也不是最好的解决办法
3、在介绍这种办法之前,我们先了解一下要用到的两个函数
foreach 函数和wildcard函数,我不讲解这个两个函数
回到我们的问题上来,我们面临的问题是make工具在生成目标文件时,只能找到和它同名的.c文件作为依赖,而对于头文件的依赖则不清楚,如果头文件改变了,对应的目标文件已经存在,而我们没有告诉make这其中的依赖关系,目标文件就会忽略头文件的改变。
怎么解决这一问题呢?
gcc知道其中的依赖关系
book@www.100ask.org:~/learning/Makefilelearning$ gcc -M a.c
a.o: a.c /usr/include/stdc-predef.h a.h /usr/include/stdio.h \
/usr/include/features.h /usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-linux-gnu/5/include/stddef.h \
/usr/include/x86_64-linux-gnu/bits/types.h \
/usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-linux-gnu/5/include/stdarg.h \
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
/usr/include/x86_64-linux-gnu/bits/sys_errlist.h
book@www.100ask.org:~/learning/Makefilelearning$ gcc -MM a.c
a.o: a.c a.h
所以我们应该想办法在Makefile中生成依赖文件,然后告诉make
CROSSCOMPILE :=
CC = $(CROSSCOMPILE)gcc
objs := a.o
objs += b.o
all:$(objs)
$(CC) -o test $^
#Makefile通常把看到的第一个目标视为终极目标
depfile := $(foreach f,$(objs),.$(f).d)
depfile := $(wildcard $(depfile))
ifneq ($(depfile),)
include $(depfile)
endif
%.o:%.c
$(CC) -Wp,-MD,.$@.d -c -o $@ $<
.PHONY:clean
clean:
rm *.o
rm test
碰到第一个目标后,会执行 $(CC) -o test $^
$^代表全部依赖,也就是objs,然而它还没有生成。如是会往下找
首先看到的是
depfile := $(foreach f,$(objs),.$(f).d)
depfile := $(wildcard $(depfile))
#第一行foreach从objs中取出来每一个元素,在这里就是a.o和b.o,将它们一次视作f,然后执行.$(f).d这条指令
#所以第一条的结果就是 depfile := .a.o.d .b.o.d 这两个并不是真的生成文件,而只是一个符号
#第二条指令则是。。。
ifneq ($(depfile),)
include $(depfile)
endif
#如果depfile对于的不是空,我们就包含,因为第一次编译的时候,在这里还没有依赖文件,所以我们必须要这样条件判断
#同时,第一次编译的时候也不要头文件依赖的指定。
#但是当第一次编译过了,不仅生成了头文件,还生成了依赖文件,所以就可以找到头文件变更
接着有
%.o:%.c
$(CC) -Wp,-MD,.$@.d -c -o $@ $<
#除了生成了$@,还通过-Wp,-MD,标志位生成了名为.$@.d的依赖文件
#如下是执行的结果
book@www.100ask.org:~/learning/Makefilelearning$ make
gcc -Wp,-MD,.a.o.d -c -o a.o a.c
gcc -Wp,-MD,.b.o.d -c -o b.o b.c
#当objs产生了,就可以执行
all:$(objs)
$(CC) -o test $^
这个过程是链接,如果有动态库就在这里链接
4、我们模仿内核的Makefile体系架构给一个工程写一个Makefile
一个通用型Makefile至少需要三个部分
1、顶层目录的Makefile
2、顶层目录的Makefile.build
3、各个子目录下的Makefile
各个子目录下的Makefile是最容易写的
参考内核的模板,写了一个,如下
obj-y += disp_manager.o
obj-y += fb.o
obj-y += test/
#/test的Makefile
obj-y += test.o
顶层目录的Makefile
内核顶层目录的Makefile用到了如下的gnu工具
283 AS = $(CROSS_COMPILE)as
284 LD = $(CROSS_COMPILE)ld
285 CC = $(CROSS_COMPILE)gcc
286 CPP = $(CC) -E
287 AR = $(CROSS_COMPILE)ar
288 NM = $(CROSS_COMPILE)nm
289 STRIP = $(CROSS_COMPILE)strip
290 OBJCOPY = $(CROSS_COMPILE)objcopy
291 OBJDUMP = $(CROSS_COMPILE)objdump