【问题标题】:My makefile keeps compiling over itself; what am I doing wrong?我的makefile一直在自己编译;我究竟做错了什么?
【发布时间】:2019-02-06 04:11:21
【问题描述】:

我不确定是否有一些我不知道的内置变量或规则,或者 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什么的。

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

【问题讨论】:

    标签: c++ makefile gnu-make


    【解决方案1】:

    这是您的问题的 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中找到)或者如果一个隐式规则适用于它 (请参阅使用隐式规则),如有必要会更新。 在检查了所有 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
    ...
    

    我们可以避免这种结果,也可以避免弄巧成拙的自动删除 我们的目标文件,通过不使用 match-anything 隐式规则来执行我们的 连锁。习惯的做法是从它的目标文件中制作一个程序 明确的规则,例如

    生成文件 (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 ...

    【讨论】:

      【解决方案2】:

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

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

      $(BINS): $(OBJS)
      

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


      (1) 除了你提供的显式规则外,make 还有相当多的隐式规则。

      如果这些规则中的 一个 决定它依赖于 makefile(这通常是配置的,因为更改 makefile 通常意味着应该完成完整的重建,因为规则很可能已经改变了),那么% 目标就可以用于makefile

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

      我个人从未见过%目标,因为我相信这意味着该规则可能匹配任何目标,包括您可能不使用的源文件t 想要覆盖。

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

      【讨论】:

      • 是的,但它应该只匹配给定的目标,在这种情况下是 $(BINS) 和 $(OBJS) 。另外,如果 $(BINS) 包含多个条目,您的解决方案会起作用吗?
      • @supereater14,如果 any 规则(显式或隐式)决定它依赖于makefile,那么它将使用% 目标“编译”到@ 987654336@目标。这就是我在括号中想要表达的意思(我会澄清),并且可能在你的情况下发生了什么。我认为,从记忆中,make -d 会为您提供有关正在使用哪些规则的大量信息,尽管要为大量输出做好准备。而且,是的,您可以有多个目标,它只会将其视为每个目标的多个相同规则。
      • 好的,但是你有没有看到这里的某些东西可能依赖于makefile?我给的makefile是完整的。
      • @supereater14,查看更新。我的猜测是,当makefile 更改时,有一个隐含的规则来重建所有内容。但即使我错了,make -d 的建议也应该能让你找出它发生的原因。
      猜你喜欢
      • 2013-08-06
      • 1970-01-01
      • 2016-07-18
      • 2019-12-23
      • 2014-06-15
      • 1970-01-01
      • 2015-08-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多