我已经为单元测试创建了一个Makefile,该文件使用GCC和参数在编译期间创建性能分析文件(gcno)。这是其中进行编译和链接的简化部分:
UTEXE = $(UTOBJSDIR)\$(UTUNIT).exe
UTOBJS = $(UTUUTSRC:.c=.o) $(UTUTSRC:.c=.o) $(UTCSRC:.c=.o)
UTOBJSFULL = $(addprefix $(UTOBJSDIR)\,$(UTOBJS))
UTOBJSGCNO = $(addprefix $(UTOBJSDIR)\,$(UTOBJS:.o=.gcno))
$(UTOBJS): %.o: %.c $(UTMAKEDEP)
$(call report,Compiling $(*F).c)
$(MKDEP) $(MKDFLAGS) -o.o -f$(UTOBJSDIR)\$(*F).dep $(subst /,\,$<)
$(CC) -c $(CFLAGS) $(subst /,\,$<) -o $(UTOBJSDIR)/$@
$(UTOBJSGCNO): $(UTOBJS) $(UTMAKEDEP)
utbuild: $(UTEXE) $(UTOBJSGCNO) $(UTOBJS) $(UTMAKEDEP)
$(UTEXE): $(UTOBJSGCNO) $(UTOBJS) $(UTMAKEDEP)
$(call report,Linking to $(UTUNIT).exe)
$(LINK) $(UTOBJSFULL) $(LNKFLAGS) -o $(UTEXE)
它编译所有对象和配置文件,并将它们链接成一个二进制文件。但是,当我删除一些配置文件(gcno)并再次调用“ utbuild”时,它将不会重新编译以恢复.gcno文件。它尝试再次进行链接,因为gcno是它的先决条件,但它不会进行编译。
我不知道如何命名此案,因此无法从互联网上找到解决方案。基本上,一个配方会创建两个文件,即使只需要重新创建一个文件,我也不知道如何编写重新运行配方的规则。
我希望获得一些链接或提示。
感谢所有评论。我尝试了无操作“;”和“:=”的结果相同。
我认为我需要退后一步,并解释为什么我问这个问题。这不仅仅在于手动删除或不删除gcno文件,还在于对如何编写可恢复任何丢失或过时文件的Makefile的一般理解。我的Makefile在少数地方有类似的情况,并且正在使用并行构建,因此当某些文件丢失时,会产生很多奇怪的错误。通常,它可以通过“清理”和“全部”解决,但我希望Makefile完美并很好地处理丢失的文件问题。
由于上面的示例在没有所有其余Makefile的情况下并不清楚,因此我进行了一个新的简单测试。
hello.c
#include <stdio.h>
int main()
{
printf("Hello world\n");
}
Makefile
CCDIR = C:\tools\MinGW
CCBINDIR = $(CCDIR)\bin
CCINCDIR = $(CCDIR)\include;$(CCDIR)\lib\gcc\mingw32\4.8.1\include
CCLIBDIR = $(CCDIR)\lib;$(CCDIR)\lib\gcc\mingw32\4.8.1
# Overcome "missing dll file" messages on Windows
CC = set PATH=%PATH%;$(CCBINDIR)& $(CCBINDIR)\gcc.exe
LINK = set PATH=%PATH%;$(CCBINDIR)& $(CCBINDIR)\gcc.exe
# Compile and link for code coverage
CFLAGS = -fprofile-arcs -ftest-coverage -g3 -O0 $(addprefix -I,$(CCINCDIR))
LNKFLAGS = -fprofile-arcs -ftest-coverage -static -static-libgcc $(addprefix -L,$(CCLIBDIR))
OBJECTS = hello.o
EXE = hello.exe
$(OBJECTS): %.o: %.c
$(CC) -c $(CFLAGS) $(subst /,\,$<) -o $@
$(EXE): $(OBJECTS)
$(LINK) $(OBJECTS) $(LNKFLAGS) -o $(EXE)
build: $(EXE)
“ make build”创建以下文件:
现在,如果我删除“ hello.gcno”并再次运行build,它将告诉我:
mingw32-make: Nothing to be done for 'build'.
目标是更新Makefile,以便make重新创建“ hello.gcno”。可能还会在该过程中重新创建“ hello.o”和“ hello.exe”,但这不是问题。
编辑:只是要清楚一点:在真正的Makefile中,我真的真的需要.gcno文件。这不仅仅是附加信息,还是可以避免或选择执行的操作。 Makefile生成单元测试可执行文件,运行它们并执行gcov以生成代码覆盖率信息,并且gcovr创建所有.gcov文件的报告。如果.gcno文件丢失,它将无法正常工作。另外-由于它是并行构建,因此依赖项应该是绝对正确的,以避免某些进程更早开始,并且这很棘手,因为覆盖率报告的依赖项来自两个“分支”-编译阶段的.gcno文件和执行阶段的.gcda文件。这就是为什么我需要它是正确的。
这里您唯一的选择是:
((如果您可以更改规则)]
$(EXE): $(OBJECTS)
$(LINK) $(OBJECTS) $(LNKFLAGS) -o $(EXE)
至此:
%.exe %.gnco: $(OBJECTS)
$(LINK) $(OBJECTS) $(LNKFLAGS) -o $(EXE)
$(GENERATE_GNCO) $<
我认为正确的答案是,不要自己删除任何.gcno
文件。如果您必须“清理”,请使用make clean
,但不要仅仅删除文件。
“内部版本”是状态机,所有文件均构成“状态”。不要破坏国家!
有人说,一个人应该能够删除任意文件,并且应该恢复构建。我的答案是,如果您用手破坏了某些.o
文件,例如,添加了0和1,使它无法使用(感谢user3629249指出这一点需要澄清,我说的是破坏,而不是有意编辑) )。构建也应该从中恢复吗?显然没有-如果您以此方式触摸.o
文件,世界上任何编译系统都将无法恢复。那么,为什么允许删除文件,却不允许修改文件?您在哪里划界线?
简而言之,任何腐败都不应允许。仅使用make clean
,或者更好的方法是,正确地编写Makefile,因此您无需清理周期。
整个Makefile存在许多问题,这是它的外观(我假设这是在Windows / DOS上:]
.SUFFIXES:
UTEXE := $(UTOBJSDIR)\$(UTUNIT).exe
UTOBJSFULL := $(addprefix $(UTOBJSDIR)\,$(subst /,\, $(UTUUTSRC:.c=.o) $(UTUTSRC:.c=.o) $(UTCSRC:.c=.o)))
UTOBJSGCNO := $(UTOBJSFULL:.o=.gcno)
.PHONY: utbuild all
all: utbuild
utbuild: $(UTEXE) $(UTOBJSGCNO) $(UTMAKEDEP)
$(UTOBJSGCNO): %.gcno: %.o $(UTMAKEDEP) ;
.SECONDARY: %\.
%\.: Makefile
mkdir $*
.SECONDEXPANSION:
$(UTOBJSFULL): $(UTOBJSDIR)\%.o: %.c $(UTMAKEDEP) | $$(@D)\.
$(call report,Compiling $<)
$(MKDEP) $(MKDFLAGS) -o.o -f$(UTOBJSDIR)\$(*F).dep $<
$(CC) -c $(CFLAGS) $< -o $@
$(UTEXE): $(UTOBJSFULL) $(UTMAKEDEP) | $$(@D)\.
$(call report,Linking to $(@F))
$(LINK) $(UTOBJSFULL) $(LNKFLAGS) -o $@