*.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.c
、b.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 不会更新