欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

makefile自动生成依赖性

程序员文章站 2024-03-23 10:18:46
...

在Makefile文件中,我们的依赖关系中可能会包含一系列的头文件,当工程比较大时,我们必须搞清楚哪些.c文件包含了哪些头文件,在加入或删除这些头文件时,都要修改Makefile文件。这样的修改很麻烦,而且很容易修改错。

为了避免上面这种繁重又容易出错的工作,我们可以使用C/C++编译器自动寻找源文件中包含的头文件,并生成一个依赖关系 ,命令如下:

gcc -MM main.c 

gcc -M main.c 

使用"-MM"与"-M"生成的依赖关系的区别:
在使用 GNU 的 C/C++ 编译器(如gcc),使用 -M 参数会把一些标准库的头文件也包含进来,使用-MM参数则不会把标准库的头文件包含进来,示例如下。

  • “-MM”:
~/Desktop/testm$ gcc -MM main.c -o main
~/Desktop/testm$ cat main
main.o: main.c defs.h
  • “-M”:
~/Desktop/testm$ gcc -M main.c -o main
~/Desktop/testm$ cat main
main.o: main.c /usr/include/stdc-predef.h /usr/include/stdio.h \
 /usr/include/x86_64-linux-gnu/bits/libc-header-start.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/bits/long-double.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/7/include/stddef.h \
 /usr/include/x86_64-linux-gnu/bits/types.h \
 /usr/include/x86_64-linux-gnu/bits/typesizes.h \
 /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \
 /usr/include/x86_64-linux-gnu/bits/types/FILE.h \
 /usr/include/x86_64-linux-gnu/bits/libio.h \
 /usr/include/x86_64-linux-gnu/bits/_G_config.h \
 /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \
 /usr/lib/gcc/x86_64-linux-gnu/7/include/stdarg.h \
 /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
 /usr/include/x86_64-linux-gnu/bits/sys_errlist.h defs.h

接下来要考虑如下将编译器这个功能与makefile联系在一起。 如何让我们的makefile文件也可以根据源文件来重新生成依赖关系呢?GNU组织建议把编译器为每一个源文件自动生成的依赖关系放到一个文件中,为每一个.c文件,都生成一个.d的makefile文件。那么.d文件中就存放了对应的.c文件的依赖关系。

我们可以写出.c文件与.d文件的依赖关系,并让make自动更新或生成.d文件,并将其包含在我们的主makefile文件中。这样,我们就可以自动化地生成每个文件的依赖关系了,如:
Makefile:

CC = gcc
SRCS := $(wildcard *.c)
-include $(SRCS:.c=.d)

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

命令解释:

  • SRCS := $(wildcard *.c) :代表当前目录下所有的.c文件。
  • %.d:%.c:所有的 .d 文件依赖于 .c 文件,是<target-pattern>: <prereq-patterns>的静态模式。
  • set -e:指定发生错误后立即退出执行。
  • rm -f [email protected]:删除所有的目标(即.d 文件)。
  • $$$$ :意为一个随机编号。
  • $(CC) -MM $< > [email protected]$$$$: 为每个依赖文件 $< ,生成依赖关系文件,并保存到[%.d].xxxx随机文件(形如“name.d.12345”)中,[email protected] 表示模式 %.d 文件。 单独的“>”符号表示将前面的输出重定向到某个地方,如文件。
  • sed 命令做了一个替换操作,再将结果写入没有四位随机数的.d文件中。单独的“<”符号表示后面的内容重定向到前面。
  • 最后一行是删除临时文件。
  • 因为 include 是按次序来载入文件,最先载入的 .d 文件中的目标会成为默认目标。所以你会发现在下面的执行结果中有“gcc -c -o hello.o hello.c”这一个编译。

上面的执行结果:

~/Desktop/testm$ ls
def  hello.c  hello.o  main.c  Makefile  test.c
~/Desktop/testm$ cat Makefile
CC = gcc
SRCS := $(wildcard *.c)
-include $(SRCS:.c=.d)

%.d:%.c
	@echo "Creating [email protected] ..."
	@set -e; rm -f [email protected]; \
	$(CC) -MM $< > [email protected].$$$$; \
	sed 's,\($*\)\.o[ :]*,\1.o [email protected] : ,g' < [email protected].$$$$ > [email protected]; \
	rm -f [email protected].$$$$
~/Desktop/testm$ make
Creating test.d ...
Creating main.d ...
Creating hello.d ...
gcc    -c -o hello.o hello.c
~/Desktop/testm$ ls
def  hello.c  hello.d  hello.o  main.c  main.d  Makefile  test.c  test.d
~/Desktop/testm$ cat main.d
main.o main.d : main.c def/defs.h

谢谢阅读!