我的 makefile 不断自我编译;我究竟做错了什么?
My makefile keeps compiling over itself; what am I doing wrong?
我不确定是否有一些我不知道的内置变量或规则,或者 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
make
构建 BINS 列表中的所有内容。
起初这一切正常,但由于某种原因,在我编辑源文件后行为发生了变化。
编辑源文件之前:
$ 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
我可以一遍又一遍地这样做,而且效果很好。在我对 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.
make clean
不仅尝试编译程序,它还编译 basic_render
并在 Makefile
中设置输出,覆盖 Makefile
本身。
编辑 basic_render.cpp
后,我查看了 Makefile
,它没有改变,所以我的编辑器没有更改 makefile 或其他东西。
那么,我做错了什么?
我建议 %
目标以某种方式匹配 makefile
文件,因此将其用作目标(1).
我的建议是将该行更改为:
$(BINS): $(OBJS)
这应该有望防止 make
认为它应该在对象更改时创建一个新的 makefile
。
(1) 除了您提供的显式规则外,make
还有相当多的隐式 规则。
如果甚至其中一个决定它依赖于makefile
(这通常是配置的,因为对makefile
的更改通常意味着应该完成完全重建,因为规则很可能已经改变),然后 %
目标可以用于 makefile
.
而且,由于对象已经改变,依赖于它们的 makefile
将被重建。
我个人 从未 看到 %
目标,因为我认为这意味着规则可能匹配 任何 目标,包括您可能不想覆盖的源文件。
如果您有兴趣查看所有这些隐式规则,make -d
应该会为您提供相当多的信息,例如使用的规则和检查文件是否需要重建的条件 - 请做好准备涉猎大量输出。
这是您的问题的 MCVE:
$ 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
暂停以注意 10.4 Chains of Implicit Rules 的不良后果:
rm bar.o main.o
程序链接后所有目标文件都被自动删除,违背了目的
制作。应归咎于我们自己的隐含规则:
%: $(OBJS)
$(CC) -o $@ $(OBJS)
加上内置的隐式规则1:
%.o: %.c
# recipe to execute (built-in):
$(COMPILE.c) $(OUTPUT_OPTION) $<
它们一起构成一个隐含的规则链,导致所有目标文件
成为 intermediate files.
继续,让我们更新一个源文件:
$ touch main.c
再做一次:
$ 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
手册中解释了这种混乱3.5 How Makefiles Are Remade:
Sometimes makefiles can be remade from other files, such as RCS or SCCS files.
If a makefile can be remade from other files, you probably want make to get an
up-to-date version of the makefile to read in.
To this end, after reading in all makefiles, make will consider each as a goal
target and attempt to update it. If a makefile has a rule which says how to update it
(found either in that very makefile or in another one) or if an implicit rule applies to it
(see Using Implicit Rules), it will be updated if necessary.
After all makefiles have been checked, if any have actually been changed,
make starts with a clean slate and reads all the makefiles over again.
(It will also attempt to update each of them over again, but normally this will
not change them again, since they are already up to date.)
(强调我的)。是否存在适用于 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
...
我们可以避免这个结果,也可以避免弄巧成拙的自动删除
我们的目标文件,通过不使用匹配任何隐式规则来执行我们的
连锁。通常的做法是通过以下方式从其目标文件制作程序
明确的规则,例如
生成文件(2)
OBJS := main.o bar.o
BIN := prog
.PHONY: all clean
all: $(BIN)
$(BIN): $(OBJS)
$(CC) -o $@ $(OBJS)
clean:
$(RM) $(OBJS) $(BIN)
看来您很珍惜 BINS
成为多个列表的选项
程序:
I want to simply be able to run
make clean
make
to build everything in the BINS list.
但是考虑一下:
BINS := prog1 prog2
和食谱:
%: $(OBJS)
$(CC) $(LFLAGS) -o $@ $(OBJS)
按照您制作 BINS
列表中所有内容的方式,您将制作相同的程序
两次,有两个不同的名字。即使你想要这样做,做的方式
它将是:
生成文件(3)
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
[1] 你可以让 GNU Make 向你展示它所有的内置规则,以及所有其他的
特定版本的规则,make --print-data-base ...
我不确定是否有一些我不知道的内置变量或规则,或者 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
make
构建 BINS 列表中的所有内容。
起初这一切正常,但由于某种原因,在我编辑源文件后行为发生了变化。
编辑源文件之前:
$ 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
我可以一遍又一遍地这样做,而且效果很好。在我对 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.
make clean
不仅尝试编译程序,它还编译 basic_render
并在 Makefile
中设置输出,覆盖 Makefile
本身。
编辑 basic_render.cpp
后,我查看了 Makefile
,它没有改变,所以我的编辑器没有更改 makefile 或其他东西。
那么,我做错了什么?
我建议 %
目标以某种方式匹配 makefile
文件,因此将其用作目标(1).
我的建议是将该行更改为:
$(BINS): $(OBJS)
这应该有望防止 make
认为它应该在对象更改时创建一个新的 makefile
。
(1) 除了您提供的显式规则外,make
还有相当多的隐式 规则。
如果甚至其中一个决定它依赖于makefile
(这通常是配置的,因为对makefile
的更改通常意味着应该完成完全重建,因为规则很可能已经改变),然后 %
目标可以用于 makefile
.
而且,由于对象已经改变,依赖于它们的 makefile
将被重建。
我个人 从未 看到 %
目标,因为我认为这意味着规则可能匹配 任何 目标,包括您可能不想覆盖的源文件。
如果您有兴趣查看所有这些隐式规则,make -d
应该会为您提供相当多的信息,例如使用的规则和检查文件是否需要重建的条件 - 请做好准备涉猎大量输出。
这是您的问题的 MCVE:
$ 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
暂停以注意 10.4 Chains of Implicit Rules 的不良后果:
rm bar.o main.o
程序链接后所有目标文件都被自动删除,违背了目的 制作。应归咎于我们自己的隐含规则:
%: $(OBJS)
$(CC) -o $@ $(OBJS)
加上内置的隐式规则1:
%.o: %.c
# recipe to execute (built-in):
$(COMPILE.c) $(OUTPUT_OPTION) $<
它们一起构成一个隐含的规则链,导致所有目标文件 成为 intermediate files.
继续,让我们更新一个源文件:
$ touch main.c
再做一次:
$ 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
手册中解释了这种混乱3.5 How Makefiles Are Remade:
Sometimes makefiles can be remade from other files, such as RCS or SCCS files. If a makefile can be remade from other files, you probably want make to get an up-to-date version of the makefile to read in.
To this end, after reading in all makefiles, make will consider each as a goal target and attempt to update it. If a makefile has a rule which says how to update it (found either in that very makefile or in another one) or if an implicit rule applies to it (see Using Implicit Rules), it will be updated if necessary. After all makefiles have been checked, if any have actually been changed, make starts with a clean slate and reads all the makefiles over again. (It will also attempt to update each of them over again, but normally this will not change them again, since they are already up to date.)
(强调我的)。是否存在适用于 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
...
我们可以避免这个结果,也可以避免弄巧成拙的自动删除 我们的目标文件,通过不使用匹配任何隐式规则来执行我们的 连锁。通常的做法是通过以下方式从其目标文件制作程序 明确的规则,例如
生成文件(2)
OBJS := main.o bar.o
BIN := prog
.PHONY: all clean
all: $(BIN)
$(BIN): $(OBJS)
$(CC) -o $@ $(OBJS)
clean:
$(RM) $(OBJS) $(BIN)
看来您很珍惜 BINS
成为多个列表的选项
程序:
I want to simply be able to run
make clean
make
to build everything in the BINS list.
但是考虑一下:
BINS := prog1 prog2
和食谱:
%: $(OBJS)
$(CC) $(LFLAGS) -o $@ $(OBJS)
按照您制作 BINS
列表中所有内容的方式,您将制作相同的程序
两次,有两个不同的名字。即使你想要这样做,做的方式
它将是:
生成文件(3)
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
[1] 你可以让 GNU Make 向你展示它所有的内置规则,以及所有其他的
特定版本的规则,make --print-data-base ...