关于 GNU make 依赖文件 *.d

About the GNU make dependency files *.d

在程序的 makefile 中,必须编写定义每个目标文件的依赖关系的规则。考虑目标文件 fileA.o。很明显,这个目标文件依赖于源文件fileA.c。但它也将取决于此源文件包含的所有头文件。因此,应将以下规则添加到 makefile 中:

    # This rule states that fileA.o depends on fileA.c (obviously), but also
    # on the header files fileA.h, fileB.h and fileC.h
    fileA.o: fileA.c fileA.h fileB.h fileC.h

请注意,该规则没有配方。可以向其中添加配方,但严格来说没有必要,因为 GNU make 可以依靠隐式规则(带有配方)将 *.c 文件编译成 *.o 文件。

无论如何,手动编写这样的规则是一项艰巨的任务。想象一下使 makefile 规则与源代码中的 #include 语句保持同步的工作。

GNU make 手册在第 4.14 章中描述了 "Generating Prerequisites Automatically" 一种自动执行此过程的方法。该过程从为每个源文件生成一个 *.d 文件开始。我引用:

For each source file name.c there is a makefile name.d which lists what files the object file name.o depends on.

手册进行:

Here is the pattern rule to generate a file of prerequisites (i.e, a makefile) called name.d from a C source file called name.c :

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

遗憾的是,手册并未详细解释此规则的实际运作方式。是的,它给出了所需的 name.d 文件,但为什么呢?规则很混乱..

看这条规则,感觉它的配方只会运行顺利Linux。我对吗?有没有办法在 Windows 上也正确地制作这个食谱 运行?

非常感谢任何帮助:-)

出现所有错误时退出

@set -e;

删除现有的 dep 文件 ($@ = target = %.d)

rm -f $@;

让编译器生成 dep 文件并输出到以 shell pid 为后缀的临时文件($< = 第一个先决条件 = %.c$$$$ -> $$ -> pid)

$(CC) -M $(CPPFLAGS) $< > $@.$$$$;

抓取目标匹配$*.o($*=match stem=%),替换为目标后跟依赖文件本身,输出到dep文件

sed 's,\($*\)\.o[ :]*,.o $@ : ,g' < $@.$$$$ > $@; \

删除临时文件

rm -f $@.$$$$

让我们插入 fooCC = gccCPPFLAGS = '',看看 make 完成扩展后会发生什么:

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

shell 本身会扩展 $$ 到 pid,dep 文件中的最终规则看起来像

foo.o foo.d : foo.c foo.h someheader.h

请注意,这是一种非常过时的生成依赖项的方式,如果您使用的是 GCC 或 clang,您可以 generate them 作为编译本身的一部分 CPPFLAGS += -MMD -MP

假设您有一个名为 foo:

的程序
objs := foo.o bar.o
deps := $(objs:.o=.d)

vpath %.c $(dir $(MAKEFILE_LIST))

CPPFLAGS += -MMD -MP

foo: $(objs)

.PHONY: clean
clean: ; $(RM) foo $(objs) $(deps)

-include $(deps)

这就是您所需要的,内置规则将完成剩下的工作。显然,如果您希望将目标文件放在不同的文件夹中,或者如果您想在源代码树之外构建,事情会稍微复杂一些。

vpath 指令允许您 运行 在不同的目录中创建文件,例如make -f path/to/source/Makefile.

回复 K 穆里尔: 尝试:

gmake -C $workdir ... 
这设置了
CURDIR = ${workdir}
但单独留下了 $PWD(来自 env)。 所有 temp/output 个文件都在 ${workdir} 中创建。 另外,我设置了变量 $。 = ${PWD} 并使用它来指定与 gmake 所在目录相关的任何内容 运行 (即源目录)。 所以,而不是

CPPFLAGS += -I../include

CPPFLAGS += -I$.  -I$./../include