C++ 我的makefile不断地自我编译;我做错了什么?

C++ 我的makefile不断地自我编译;我做错了什么?,c++,makefile,gnu-make,C++,Makefile,Gnu Make,我不确定是否有一些我不知道的内置变量或规则,或者make是否有问题,或者我只是疯了 对于我的一个项目,我有一个makefile,如下所示: CC=g++ CFLAGS=-O3 `libpng-config --cflags` LFLAGS=-lm `libpng-config --ldflags` OBJS=basic_render.o render.o mandel.o BINS=basic_render .PHONY: all clean all: $(BINS) clean:

我不确定是否有一些我不知道的内置变量或规则,或者make是否有问题,或者我只是疯了

对于我的一个项目,我有一个makefile,如下所示:

CC=g++
CFLAGS=-O3 `libpng-config --cflags`
LFLAGS=-lm `libpng-config --ldflags`

OBJS=basic_render.o render.o mandel.o
BINS=basic_render

.PHONY: all clean

all: $(BINS)

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

%.o: %.cpp
    $(CC) $(CFLAGS) -c -o $@ $<

%: $(OBJS)
    $(CC) $(LFLAGS) -o $@ $(OBJS)
要构建“垃圾箱”列表中的所有内容

这在一开始可以正常工作,但由于某种原因,在编辑源文件后,行为会发生变化

在编辑源文件之前:

$ make clean
rm -f basic_render basic_render.o render.o mandel.o
$ make
g++ -O3 `libpng-config --cflags` -c -o basic_render.o basic_render.cpp
g++ -O3 `libpng-config --cflags` -c -o render.o render.cpp
g++ -O3 `libpng-config --cflags` -c -o mandel.o mandel.cpp
g++ -lm `libpng-config --ldflags` -o basic_render basic_render.o render.o mandel.o
rm mandel.o basic_render.o render.o
$ touch main.c
我可以一遍又一遍地这样做,效果很好。在我更改了
basic_render.cpp
(实际上只是更改了几个常量)之后,它突然更改为:

$ make clean
g++ -O3 `libpng-config --cflags` -c -o basic_render.o basic_render.cpp
g++ -O3 `libpng-config --cflags` -c -o render.o render.cpp
g++ -O3 `libpng-config --cflags` -c -o mandel.o mandel.cpp
g++ -lm `libpng-config --ldflags` -o makefile basic_render.o render.o mandel.o
rm mandel.o basic_render.o render.o
makefile:1: warning: NUL character seen; rest of line ignored
makefile:1: *** missing separator.  Stop.
makeclean
不仅尝试编译程序,还使用
Makefile
中设置的输出编译了
basic\u render
,覆盖了
Makefile
本身

编辑完
basic\u render.cpp
后,我查看了
Makefile
,但它没有更改,所以我的编辑器没有更改Makefile或其他内容


那么,我在这里做错了什么呢?

我建议,
%
目标以某种方式与
makefile
文件匹配,因此使用它作为目标(1)

我的建议是将该行改为:

$(BINS): $(OBJS)
这将有望防止
make
在对象发生变化时认为它应该创建一个新的
makefile


(1) 除了您提供的显式规则之外,
make
还有很多隐式规则

如果即使其中一条规则决定它依赖于
makefile
(这通常是配置的,因为对
makefile
的更改通常意味着应该进行完全重建,因为规则很可能已经更改),那么
%
目标就可以用于
makefile

而且,由于对象已更改,依赖于它们的
makefile
将被重建

我个人从未见过
%
目标,因为我认为这意味着规则可能匹配任何目标,包括您可能不希望覆盖的源文件


如果您对查看所有这些隐式规则感兴趣,
make-d
应该会为您提供大量信息,例如所使用的规则和检查文件是否需要重建的条件,只需准备好涉过大量输出。

以下是您的问题:

$ ls -R
.:
bar.c  main.c  Makefile

$ cat main.c
extern int bar(void);

int main(void)
{
    bar();
    return 0;
}

$ cat bar.c
int bar(void)
{
    return 42;
}

$ cat Makefile
OBJS := main.o bar.o
BINS := prog

.PHONY: all clean

all: $(BINS)

%: $(OBJS)
    $(CC) -o $@ $(OBJS)

clean:
    $(RM) $(OBJS) $(BINS)
第一次:

$ make
cc    -c -o main.o main.c
cc    -c -o bar.o bar.c
cc -o prog main.o bar.o
rm bar.o main.o
暂停以注意以下不希望出现的后果:

链接程序后,所有目标文件都会自动删除,这不符合目的 当然可以。这要归咎于我们自己的隐含规则:

%: $(OBJS)
    $(CC) -o $@ $(OBJS)
加上内置的隐式规则1:

第二次:

$ make
cc    -c -o main.o main.c
cc    -c -o bar.o bar.c
cc -o Makefile main.o bar.o
rm bar.o main.o
Makefile:1: warning: NUL character seen; rest of line ignored
Makefile:1: *** missing separator. Stop.
我们的Makefile被链接破坏了:

cc -o Makefile main.o bar.o
本手册对这一问题进行了解释:

有时,可以从其他文件(如RCS或SCCS文件)重新生成makefile。 如果可以从其他文件重新生成makefile,则可能希望make获得 要读入的makefile的最新版本

<>强>为此,在阅读完所有的Mag文件后,将把每一个都看作是一个目标。 目标并尝试更新它。如果makefile有一条规则说明如何更新它 (可以在该makefile中找到,也可以在另一个makefile中找到)或者如果隐式规则适用于该makefile (请参见使用隐式规则),必要时将对其进行更新。 在检查了所有makefiles之后,如果有任何makefiles实际发生了更改, make从头开始,重新读取所有make文件。 (它还将尝试重新更新其中的每一个,但通常会 不要再次更改它们,因为它们已经是最新的。)

(我的重点)。是否考虑了适用于
Makefile
的隐式规则 作为目标?是的,它是:

%: $(OBJS)
    $(CC) -o $@ $(OBJS)
因为目标模式
%
匹配任何文件。如果我们恢复我们的打击 Makefile并再次尝试相同的实验,这次是调试:

make -d >debug.log 2>&1
输出将向我们显示:

...
Reading makefiles...
Reading makefile 'Makefile'...
Updating makefiles....
 Considering target file 'Makefile'.
  Looking for an implicit rule for 'Makefile'.
  ...
  ...
  Found an implicit rule for 'Makefile'.
  ...
  ...
  Finished prerequisites of target file 'Makefile'.
  Prerequisite 'main.o' is newer than target 'Makefile'.
  Prerequisite 'bar.o' is newer than target 'Makefile'.
 Must remake target 'Makefile'.
cc -o Makefile main.o bar.o
...
我们可以避免这个结果,也可以避免自动删除 通过不使用匹配任何隐式规则来执行 联系。惯常的做法是通过 明确的规则,例如

Makefile(2)

您似乎很珍惜将
垃圾箱
作为多个垃圾箱列表的选项 计划:

我只想能够跑步

make clean
make
澄清

制造

要构建“垃圾箱”列表中的所有内容

<>但请考虑:

BINS := prog1 prog2
配方是:

%: $(OBJS)
    $(CC) $(LFLAGS) -o $@ $(OBJS)
作为制作
垃圾箱
列表中所有内容的方法,您只需制作相同的程序即可 两次,两个不同的名字。即使你想做这件事 它将是:

Makefile(3)

其运行方式如下:

$ make
cc    -c -o main.o main.c
cc    -c -o bar.o bar.c
cc -o prog1 main.o bar.o
cc -o prog2 main.o bar.o

[1] 您可以让GNU Make向您展示其所有内置规则,以及所有其他规则
它的特定构建的规则,使用
make--print database…

是的,但它应该只匹配给定的目标,在本例中,所有目标都是$(bin)和$(OBJS)。此外,如果$(BIN)包含多个条目,您的解决方案是否有效?@supereater14,如果任何规则(显式或隐式)决定它依赖于
makefile
,它将使用
%
目标“编译”到
makefile
目标。这就是我想在括号中表达的意思(我会澄清),也许你的情况就是这样。我认为,从内存来看,
make-d
将为您提供大量关于正在使用哪些规则的信息,尽管您需要为大量输出做好准备。是的,您可以有多个目标,它将简单地将其视为每个目标的多个相同规则。好的,但是您是否看到这里的内容可能依赖于makefile?我提供的makefile已完成。@supereater14,请参阅更新。我猜这是一个暗示
BINS := prog1 prog2
%: $(OBJS)
    $(CC) $(LFLAGS) -o $@ $(OBJS)
OBJS := main.o bar.o
BINS := prog1 prog2

.PHONY: all clean

all: $(BINS)

$(BINS): $(OBJS)
    $(CC) -o $@ $(OBJS)

clean:
    $(RM) $(OBJS) $(BIN)
$ make
cc    -c -o main.o main.c
cc    -c -o bar.o bar.c
cc -o prog1 main.o bar.o
cc -o prog2 main.o bar.o