c++ - 我的 makefile 不断地自行编译;我究竟做错了什么?

标签 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:
    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 不仅尝试编译程序,它还使用 Makefile 中设置的输出编译 basic_render,覆盖 >Makefile 本身。

编辑basic_render.cpp后,我查看了Makefile,它没有改变,所以我的编辑器并没有改变makefile或其他东西。

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

最佳答案

这是您问题的 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
...

我们可以避免这种结果,也可以避免弄巧成拙的自动删除 我们的目标文件,通过不使用匹配任何隐式规则来执行我们的 链式。通常的做法是从目标文件中创建一个程序 明确的规则,例如

Makefile (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 列表中所有内容的方式,您只需制作相同的程序 两次,有两个不同的名字。即使您想要这样做,这样做的方法 它将是:

Makefile (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 ...

关于c++ - 我的 makefile 不断地自行编译;我究竟做错了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54546486/

相关文章:

c++ - 使用 CUDA/自定义语言和 $(eval) 规则创建的 automake

makefile - GNU Makefile "preprocessor"?

c++ - 图标在另一台计算机上消失

C++ decltype 解释

c++ - 如何检查两个指针​​是否指向同一个对象?

c++ - sclite (SCTK) 安装,文件无法识别,文件格式无法识别,Cygwin

c - makefile:4: *** 缺少分隔符。停止

c++ - 如何传递用户数据来比较 std::sort 的功能?

python - 从源代码构建 rabbitmq

powershell - GNU Make会忽略Windows上的环境变量