为什么“make”命令总是重新构建目标?

13
我的Makefile如下:
OBJS = b.o c.o a.o
FLAGS = -Wall -Werror
CC = gcc

test: $(OBJS)
    $(CC) $(FLAGS) $(OBJS) -o test
b.o: b.c b.h
    $(CC) $(FLAGS) -c b.c
a.o: a.c b.h c.h
    $(CC) $(FLAGS) -c a.c
c.o: c.c c.h
    $(CC) $(FLAGS) -c c.c
clean:
    rm a
    rm *.o
all: test

如果我执行make然后再执行make,它总是会重新构建“test”。为什么会这样?

即使我执行make a.o,它也会重新构建...我使用的是Linux操作系统,如果这有所帮助。

在Windows中,如果我将“test”更改为“test.exe”,并将“-o test”更改为“-o test.exe”,则可以正常工作。因此,我猜测在我的Linux上,由于某种原因,“make”无法检查目录中文件的日期戳。

我已经解决了!.c文件是在Windows中创建的。我在vi中打开了所有.c和.h文件,在不进行任何更改的情况下保存了更改,一切都正常了。我认为通过这样做,日期时间戳问题已得到解决。


你能否制作一个更简单的测试案例?(例如较少的文件,可能是一个hello-world类型的程序,这样你就可以在问题中包含所有内容。)你在最近的编辑中改变了test规则的工作方式,我怀疑与已发布的内容存在其他重大差异。 - Roger Pate
我认为问题出在Linux上。 如果我在那个Makefile中将“test”改为“text.exe”,并在其规则“-o text.exe”中,它就可以正常工作。 - fsdfa
1
作为旁注,你可能不应该将可执行文件命名为“test”。大多数UNIX系统都有一个真正的“test”程序。 - paxdiablo
@pax 但如果他从未将可执行文件放在PATH中的某个位置,那么这就无关紧要了。 - Roger Pate
1
@Roger,这是一个很好的观点。只是我之前遇到过问题,当不在我的路径上时(如果您想要安全,它就不应该在路径上)。然后,当我运行test而不是./test时,我会得到非常意外的结果。现在,我总是将我的测试程序称为tst(或者如果我感到病态高效,我会称其为x)。 - paxdiablo
惯例是使用变量CFLAGS。遵循惯例是一个好主意,这将使您更好地利用默认规则。 - William Pursell
3个回答

17

你的第一个规则是:

test: $(OBJS)

这意味着如果名为“test”的文件不存在,则规则应该运行并创建它。由于“test”从未存在过(该规则实际上不会创建名为“test”的文件),因此每次都会运行。您应该将“test”更改为“a”,即该规则的实际输出。

以后参考,如果一个规则实际上不会创建左侧的内容,您应该标记它:

.PHONY: test

对不起,那是我的笔误。在 Makefile 中应该是那样的。 - fsdfa

3
作为最后的手段,你可以在调试模式下运行Make(make -d),并仔细检查输出内容。在尝试之前,我建议您在“test”规则中添加一行代码以查看是否有任何前提条件需要重新构建测试(并使用一些自动变量在编译命令中,这是一个好习惯)。
test: $(OBJS)
    @echo prereqs that are newer than test: $?
    $(CC) $(FLAGS) $^ -o $@

2
您需要指定test虚拟目标(phony target):
OBJS = b.o c.o a.o
CFLAGS = -Wall -Werror
CC = gcc

.PHONY: all test clean
# 'all' and 'clean' are also phony

all: test
test: a

a: $(OBJS)
    $(CC) $(CFLAGS) $(OBJS) -o a

b.o: b.c b.h
a.o: a.c b.h c.h
c.o: c.c c.h

clean:
    rm a
    rm *.o

我已将全部放置在首位,以便作为默认目标(这是常见的情况),删除了编译命令(make已经内置了这些命令),并将FLAGS更改为CFLAGS(这是C编译器标志的通用名称,并被make的内置规则所使用)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接