*.c 在依赖 x.h 更改时不会反抗,使用自动生成的 *.d 文件

Posted

技术标签:

【中文标题】*.c 在依赖 x.h 更改时不会反抗,使用自动生成的 *.d 文件【英文标题】:*.c not rebult when dependency x.h changed, using auto-generated *.d file 【发布时间】:2019-01-03 15:55:30 【问题描述】:

我在编译期间处理依赖文件时遇到了一个问题。我只是给你我在项目中遇到的场景。

我有两个名为a.cb.c 的C 源文件,其中包括一个名为c.h 的头文件。我运行了 makefile ,其中包含编译这两个文件的说明。我可以成功编译a.c 文件,但是我在编译b.c 时看到了一些失败,这需要在c.h 中进行一些更改以解决该问题。在我对c.h 进行更改并触发构建(增量构建)之后,a.c 文件也应该再次编译,对吧?因为a.c 也依赖于c.h 文件。

我遵循所有依赖机制(创建自动依赖文件和包括.d 文件等)

DEPSALL := $(wildcard $(patsubst %,%.d,$(basename $(TGTFILES)/*.c)))
-include $(DEPSALL)
$(TGTFILES)/%.o: $(TGTFILES)/%.c
        mkdir -p $(@D)
        $(CC64) -MT $@ -MMD -MP -MF $(patsubst %,%.d,$(basename $@)) -o $(@) -c $(CFLAGS64) $<
  ...
  ...

我在这里遗漏了什么吗?我想重建所有 .c 文件,其中包括我更改的特定头文件。

【问题讨论】:

您是否有理由不使用$(wildcard $(TGTFILES)/*.d) 而不是复杂的第一行?和$(TGTFILES)/$*.d,而不是你的食谱中的$(patsubst %,%.d,$(basename $@))?你的默认目标是什么(它不是你展示的 Makefile 的一部分)? 使用“通配符”函数与我分配 DEPSALL 变量的方式有什么不同吗?我是新手,我想知道这一点。是的,默认目标不在我上面显示的 makfile 部分。 您可以通过$(info $(DEPSALL)) 行查看该变量包含的内容,并验证它是否包含a.d。您可以检查a.d 以验证它是否包含a.o: c.h @RenaudPacalet:使用这些编译的 patsubst+basename 调用的目的是将所有 .d 文件放在当前目录而不是 $(TGTFILES) 中。当然,为什么 OP 想要这样做是一个谜。更常见的是,您希望它们与 .o 文件位于同一目录中(这是 -MMD 的默认设置——不需要额外的 -MF 选项)。 @ChrisDodd:也不确定:basename 保留目录部分。它只删除后缀部分。 【参考方案1】:

您的问题不完整:您并没有真正描述您面临的问题(但我们可能会猜测目标文件没有在应该重建的时候重建)并且您显示的 Makefile 部分不足以理解您的目标是。

不管怎样,首先,这个表达式:

DEPSALL := $(wildcard $(patsubst %,%.d,$(basename $(TGTFILES)/*.c)))

是无用的复杂。它相当于更简单,更容易理解:

DEPSALL := $(wildcard $(TGTFILES)/*.d))

同样,您可以在编译配方中替换:

$(patsubst %,%.d,$(basename $@))

作者:

$(TGTFILES)/$*.d

但是让我们回到您的主要问题(至少我猜是您的主要问题):修改头文件时,一些目标文件没有重建,而它们应该重建。

我的猜测是你认为:

DEPSALL := $(wildcard $(patsubst %,%.d,$(basename $(TGTFILES)/*.c)))

将分配给DEPSALL 一个依赖文件列表,每个源文件一个,就像其他形式一样:

DEPSALL := $(patsubst %.c,%.d,$(wildcard $(TGTFILES)/*.c))

如果这是你的想法,那你就错了。当您调用 make 时,您的版本将在 $(TGTFILES)当前存在的依赖文件列表分配给 DEPSALL。如果某些(或全部)丢失,某些目标文件将不会被重建...

我建议你仔细阅读this excellent post about Auto-Dependency Generation。如果你调整它以适应你的设置,你最终应该会得到类似的结果:

TGTFILES := tgtfiles
SRCS     := $(wildcard $(TGTFILES)/*.c)
OBJS     := $(patsubst %.c,%.o,$(SRCS))
DEPS     := $(patsubst %.c,%.d,$(SRCS))
INCLUDES := include
CFLAGS   += -I$(INCLUDES)

.PHONY: objs clean

objs: $(OBJS)

%.o: %.c
%.o: %.c %.d
    $(CC) -MT $@ -MMD -MP -MF $*.Td $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
    @mv -f $*.Td $*.d && touch $@

%.d: ;
.PRECIOUS: %.d

clean:
    rm -f $(OBJS) $(DEPS)

include $(DEPS)

有些方面可能看起来很奇怪、无用甚至完全错误。但是,如果您仔细阅读上述帖子,您会发现它完全有道理。演示:

$ tree
.
├── Makefile
├── include
│   └── c.h
└── tgtfiles
    ├── a.c
    └── b.c

2 directories, 4 files
$ make
cc -MT tgtfiles/b.o -MMD -MP -MF tgtfiles/b.Td -Iinclude  -c -o tgtfiles/b.o tgtfiles/b.c
cc -MT tgtfiles/a.o -MMD -MP -MF tgtfiles/a.Td -Iinclude  -c -o tgtfiles/a.o tgtfiles/a.c
$ tree
.
├── Makefile
├── include
│   └── c.h
└── tgtfiles
    ├── a.c
    ├── a.d
    ├── a.o
    ├── b.c
    ├── b.d
    └── b.o

2 directories, 8 files
$ cat tgtfiles/a.d
tgtfiles/a.o: tgtfiles/a.c include/c.h

include/c.h:
$ make
make: Nothing to be done for 'objs'.
$ touch include/c.h 
$ make
cc -MT tgtfiles/b.o -MMD -MP -MF tgtfiles/b.Td -Iinclude  -c -o tgtfiles/b.o tgtfiles/b.c
cc -MT tgtfiles/a.o -MMD -MP -MF tgtfiles/a.Td -Iinclude  -c -o tgtfiles/a.o tgtfiles/a.c

【讨论】:

【参考方案2】:

wildcard 函数应用程序看起来不正确。应该是:

DEPSALL := $(patsubst %,%.d,$(basename $(wildcard $(TGTFILES)/*.c)))

【讨论】:

这是我最初的想法,但我不确定。原始版本扩展为$(TGTFILES) 目录中的.d 文件列表,这可能是正确的。为什么 OP 不简单地使用 $(wildcard $(TGTFILES)/*.d) 对我来说是个谜,但他的版本有效。

以上是关于*.c 在依赖 x.h 更改时不会反抗,使用自动生成的 *.d 文件的主要内容,如果未能解决你的问题,请参考以下文章

c# - 在运行时更改 App.Config 后,实体框架 ConnectionString 不会更新

8——对象的作用域,生存期,……

状态更新没有立即反映。如何使用 useEffect 的依赖数组来显示更改?

添加nuget包时不会自动安装依赖项

使用rpm命令安装软件时不会自动安装所依赖的其它软件包。

PayPal Express Checkout 调用不会在布局 C 中产生 BAID