Makefile - section执行两次的问题

Issue with Makefile - section executed twice

本文关键字:两次 问题 section 执行 Makefile      更新时间:2023-10-16

我可能不是一个makefile大师,但我过去对它们有一些经验。然而,我有一个(相当不重要的)问题,它仍然困扰着我。

这是我的Makefile:

#-------------------------
# Definitions
#-------------------------
APP   = lgm
# Tools & commands
CC    = gcc
LEX   = lex
YACC  = /usr/local/bin/bison
RM    = rm
CP    = cp
MV    = mv
DMD   = dmd
# Paths
SRC     = src
BIN     = bin
TEST    = test
LIB     = lib
# C stuff
CC_HEADERS  = logramm.tab.h
CC_SOURCES  = lex.yy.c logramm.tab.c
CC_OBJECTS  = lex.yy.o logramm.tab.o
CC_LEXER    = logramm.l
CC_PARSER   = logramm.y
# D stuff
D_SOURCES   = main.d
D_OBJECTS   = main.o
D_LFLAGS    = -m64
#-------------------------
# Main Functions
#-------------------------
all: ${APP} clean
${APP}: ${CC_OBJECTS} ${D_OBJECTS}
    ${DMD} ${CC_OBJECTS} ${D_OBJECTS} -of${APP} ${D_FLAGS}
    ${MV} ${APP} ${BIN}
${D_OBJECTS}:
    ${DMD} -c ${D_SOURCES}
${CC_OBJECTS}: ${CC_SOURCES}
    ${CC} -g -c ${CC_SOURCES}
${CC_SOURCES}: setup
    ${LEX} ${CC_LEXER}
    ${YACC} -d ${CC_PARSER}
setup:
    ${CP} ${SRC}/*.d .
    ${CP} ${SRC}/*.l .
    ${CP} ${SRC}/*.y .
clean:
    ${RM} *.d *.y *.l *.o *.hh *.c *.h

问题是:

当我看到make all的输出时,${CC_SOURCES}部分执行了两次(2个lex, 2个bison命令)。例如:输出:

cp src/*.d .
cp src/*.l .
cp src/*.y .
lex logramm.l
/usr/local/bin/bison -d logramm.y
lex logramm.l
/usr/local/bin/bison -d logramm.y
gcc -g -c lex.yy.c logramm.tab.c
dmd -c main.d
dmd lex.yy.o logramm.tab.o main.o -oflgm 
mv lgm bin
rm *.d *.y *.l *.o *.hh *.c *.h
rm: *.hh: No such file or directory
make: *** [clean] Error 1

为什么?我做错了什么?


更新:

我刚刚设法修复它,通过采取setup${CC_SOURCES},并把它放在all部分,如:

all: setup ${APP} clean

所以,没关系。然而,我还是不明白为什么。我是否误解了makefile的结构方式?欢迎任何意见!: -)

您对make如何解释具有多个目标的规则有一个基本的误解:这个make文件实际上不起作用。让我们展开运行lex/yacc的规则:

lex.yy.c logramm.tab.c: setup
        ${LEX} ${CC_LEXER}
        ${YACC} -d ${CC_PARSER}

Make将其解释为(在显式规则中,多个目标为每个目标创建单独的规则):

lex.yy.c: setup
        ${LEX} ${CC_LEXER}
        ${YACC} -d ${CC_PARSER}
logramm.tab.c: setup
        ${LEX} ${CC_LEXER}
        ${YACC} -d ${CC_PARSER}

现在你可能看到你的问题了。Make想要构建lex.yy.o而这取决于lex.yy.c,所以Make尝试构建它。它看到它依赖于不存在的setup(因为没有创建它,但如果有人在您的目录中创建该文件,它会严重破坏)。因此,它运行lex和yacc步骤(两者都运行,因为这是配方)来构建lex.yy.c

然后它经过完全相同的事情来构建logramm.tab.c…因此,规则运行两次。

你的目标文件也有同样的问题;展开该规则,我们看到:

lex.yy.o logramm.tab.o: lex.yy.c logramm.tab.c
        ${CC} -g -c lex.yy.c logramm.tab.c

等于:

lex.yy.o: lex.yy.c logramm.tab.c
        ${CC} -g -c lex.yy.c logramm.tab.c
logramm.tab.o: lex.yy.c logramm.tab.c
        ${CC} -g -c lex.yy.c logramm.tab.c

这显然是不对的

此外,您的setup规则(因为它从不存在)将导致每次都重新构建所有内容。

您的解决方案是更好的WRT设置,因为源文件不依赖于它,所以它们不会总是被重建。对于目标文件,您仍然有不符合的规则。此外,如果你想启用并行构建,你的解决方案将不起作用,因为安装可能与其他目标并行运行:你真的希望依赖关系确保顺序。

一般来说,当你写makefile时,你应该(a)有一个规则来构建一个目标,(b)确保规则构建的目标完全符合你告诉make它要构建的目标。

试着用这个代替:

all: ${BIN}/${APP} clean
${BIN}/${APP}: ${APP}
        ${CP} $< $@
${APP}: ${CC_OBJECTS} ${D_OBJECTS}
    ${DMD} $^ -of$@ ${D_FLAGS}
%.o : %.d
        ${DMD} -c $<
%.o: %.c
        ${CC} -g -c $<
%: ${SRC}/%
        ${CP} $< $@
lex.yy.c: ${CC_LEXER}
        ${LEX} $<
%.tab.c %.tab.h: %.y
        ${YACC} -d $<
clean:
        ${RM} *.d *.y *.l *.o *.hh *.c *.h

问题是CC_SOURCES = lex.yy.c logramm.tab.c所以目标行(加上动作):

${CC_SOURCES}: setup
    ${LEX} ${CC_LEXER}
    ${YACC} -d ${CC_PARSER}

等价于:

lex.yy.c: setup
    ${LEX} ${CC_LEXER}
    ${YACC} -d ${CC_PARSER}
logramm.tab.c: setup
    ${LEX} ${CC_LEXER}
    ${YACC} -d ${CC_PARSER}

setup规则没有依赖关系,但也不是一个(GNU make扩展).PHONY目标(我不确定作为一个虚假的目标会有帮助,要么),所以它不存在。因此,当make试图确保lex.yy.c是最新的,它必须执行setup的规则;logramm.tab.c同上;因此要执行两次。

你的解决方案看起来很合理。如果你正在使用GNU make,并且不介意被绑定到GNU make,你可以探索.PHONY,看看它是否有帮助。

这归结为make处理依赖关系的方式。您总是可以通过传递-d标志来检查make正在做什么(至少对于GNU Make, ymmv,如果您不使用任何内置规则,也可以考虑添加-r,因为它们实质上混淆了输出)。

以下是setup${CC_SOURCES}的依赖项的情况

...
Does lex.yy.c exist? no, okay check its prerequisites
    Does setup exist? no, okay check its prerequisites
        No prerequisites, run the recipe
    Prerequisites for lex.yy.c done, run the recipe
Does logramm.tab.c exist? yes, but check its prerequisites
    We already built setup, so prune this prerequisite, but treat it as newer than logramm.tab.c
    Prerequisites for logramm.tab.c done, run the recipe
...

以下是setupall的依赖项的情况

...
Does setup exist? no, okay check its prerequisites
    No prerequisites, run the recipe
...
Does lex.yy.c exist? no, okay check its prerequisites
    No prerequisites, run the recipe
Does logramm.tab.c exist? yes, but check its prerequisites
    No prerequisites, so done
...