我的makefile不断对其自身进行编译;我究竟做错了什么?

时间:2019-02-06 04:11:21

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

在编辑basic_render.cpp之后,我查看了Makefile,但它没有更改,所以这并不意味着我的编辑器正在更改makefile或其他内容。

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

2 个答案:

答案 0 :(得分:3)

这是您问题的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手册对此解释进行了说明:

  

有时可以使用其他文件(例如RCS或SCCS文件)重制makefile。   如果可以从其他文件重制一个makefile,则您可能希望make获得一个   可以读取的最新版本的makefile。

     

为此,在读取所有makefile文件之后,make会将每个文件视为目标   定位并尝试更新它。如果makefile有一条规则说明如何更新它   (在该Makefile或另一个Makefile中找到),或者是否适用隐式规则   (请参阅使用隐式规则),如有必要,将对其进行更新。   在检查完所有makefile之后,如果实际上已进行了更改,   make从干净的开始,然后重新读取所有makefile。   (它还会尝试再次更新它们中的每一个,但是通常这样做会   不要再更改它们,因为它们已经是最新的了。)

(强调我的)。是否存在适用于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为多个列表 程序:

  

我只想简单地运行

     

清洁

     

制作

     

构建BINS列表中的所有内容。

但是考虑一下:

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

的特定构建规则

答案 1 :(得分:0)

我建议%目标与makefile文件匹配,因此将其用作目标(1)

我的建议是将该行更改为:

$(BINS): $(OBJS)

这有望防止make认为当对象更改时它应该创建一个新的makefile


(1)除了您提供的明确规则外,make还具有很多隐式规则。

如果这些规则中甚至有一个都决定它取决于makefile(通常进行配置,因为对makefile进行更改通常意味着应该完全重建,因为规则可能已经更改),则%目标可用于makefile

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

我个人从未看到过%目标,因为我认为这意味着该规则可能与任何目标匹配,包括您可能 不想覆盖。

如果您有兴趣查看所有这些隐式规则,make -d应该为您提供大量信息,例如所使用的规则以及是否需要重建文件的条件检查-只需准备一下即可很多输出。