【问题标题】:Makefile always rebuildingMakefile 总是在重建
【发布时间】:2015-09-13 21:02:56
【问题描述】:

我正在使用以下简单的 Makefile 来构建一些简单的文件。我对制作 Makefile 真的很陌生。我不知道为什么它一直在重建,即使文件是在第一次 make 之后构建的并且我没有编辑任何文件。

EXE    = nextgenrsm
CC     = gcc
LIBS   = StarterWare_Files/
CFLAGS = -c 

INCLUDE_PATH =  StarterWare_Files/

MAIN_SRC = $(wildcard *.c)
MAIN_OBS = $(patsubst %.c,%.o,$(MAIN_SRC))

LIB_SRC = $(wildcard StarterWare_Files/*.c)
LIB_OBS = $(patsubst StarterWare_Files/%.c,%.o,$(LIB_SRC))

output: $(EXE)

$(EXE): $(MAIN_OBS) $(LIB_OBS)
    $(CC) $(MAIN_OBS) $(LIBS)$(LIB_OBS) -o $(EXE) 

$(MAIN_OBS): $(MAIN_SRC) 
    $(CC) $(CFLAGS) *.c -I$(INCLUDE_PATH)

$(LIB_OBS): $(LIB_SRC) 
    cd $(LIBS); \
    $(CC) $(CFLAGS) *.c -I../

clean:
    rm -rf $(LIBS)*.o $(EXE) *.o

新编辑的 MAKEFILE

EXE    = nextgenrsm
CC     = gcc
LIBS   = StarterWare_Files/
CPPFLAGS = _IStarterWare_Files/


MAIN_OBS = $(patsubst %.c,%.o,$(wildcard *.c))
LIB_OBS  = $(patsubst %.c,%.o,$(wildcard StarterWare_Files/*.c))

all: $(EXE)

$(EXE): $(MAIN_OBS) $(LIB_OBS)
   $(CC) -o $@ $(LDFLAGS) $(MAIN_OBS) $(LIB_OBS) $(LDLIBS)

%.o: %.c 
   $(CC) -o $@ -MD -MP $(CPPFLAGS) $(CFLAGS) -c $^

ALL_DEPS = $(patsubst %.o,%.d,$(MAIN_OBS), $(LIB_OBS))
-include $(ALL_DEPS)

clean:
   rm -f $(LIB_OBS) $(EXE) $(MAIN_OBS) $(ALL_DEPS)

.PHONY: all clean

每当我触摸 StarterWare_Files/example.h 并尝试再次进行 make 时,它​​都会向我抛出错误,即 gcc 无法将 -o 指定为 -c 或 -S 指定多个文件。基本上命令变成这样 gcc -o main.o -IStarterWare_Files -c main.c StarterWare_Files/test.h StarterWare_Files/add.h..

【问题讨论】:

  • 正在重建$(MAIN_OBS)$(LIB_OBS)? $(EXE)?他们都是?运行make -d 并查看输出,它会告诉您它认为需要重建的内容以及原因。你的makefile也不正确。它告诉 make 每个 .o 文件都有 every .c 文件作为先决条件,而不仅仅是它的匹配文件。因此,更改 any .c 文件将重新编译该集合中的 every .o 文件。
  • @EtanReisner 是的,它正在构建所有这些。如何做出这样的改变?它只引用它自己的 C 文件。
  • 它可能只是再次构建$(LIB_OBS) 文件。那是因为您在设置 $(LIB_OBS) 的值时从 .c 文件中删除了目录前缀,但是您随后让目标在目录中创建 .o 文件,因此 make 无法匹配这些文件(因为 foo.oStarterWare_Files/foo.o不同)。
  • @EtanReisner 感谢您指出这一点
  • @EtanReisner $^ 的意义是什么?

标签: makefile gnu-make


【解决方案1】:

您的默认目标是 output,但您的 makefile 从不生成这样的文件,因此每次您调用 make 它都会尝试构建 output 文件。

解决方案是将output 目标标记为phony target。还有clean目标:

.PHONY: output clean

您可以利用 built-in rules.o.cautomatic variables 编译为:

EXE    := nextgenrsm
CC     := gcc
LIBS   := StarterWare_Files/
CPPFLAGS := -IStarterWare_Files

MAIN_OBS := $(patsubst %.c,%.o,$(wildcard *.c))
LIB_OBS  := $(patsubst %.c,%.o,$(wildcard StarterWare_Files/*.c))

all: $(EXE)
$(EXE) : $(MAIN_OBS) $(LIB_OBS)
    $(CC) -o $@ $(LDFLAGS) $^ $(LDLIBS)

clean:
    rm -f $(MAIN_OBS) $(LIB_OBS) $(EXE)

.PHONY: all clean   

如果您想自动生成头文件依赖,请添加:

# This rule produces .o and also .d (-MD -MP) from .c.
%.o : %.c
    $(CC) -o $@ -MD -MP $(CPPFLAGS) $(CFLAGS) -c $<

# This includes all .d files produced along when building .o.
# On the first build there are not going to be any .d files.
# Hence, ignore include errors with -.
ALL_DEPS := $(patsubst %.o,%.d,$(MAIN_OBS) $(LIB_OBS))
-include $(ALL_DEPS)

clean:
    rm -f $(MAIN_OBS) $(LIB_OBS) $(EXE) $(ALL_DEPS)

【讨论】:

  • 虽然这不会导致它重新编译任何东西,假设其他目标正确地创建了他们的目标文件(他们似乎这样做)。哦,不,$(LIB_OBS) 的名称中没有目录。
  • $&lt;(第一个先决条件)是$(EXE) : $(MAIN_OBS) $(LIB_OBS) 配方中的错字。它应该是$^(所有先决条件)。我刚刚编辑修复它。
  • 是的,我必须经常自己查一下。我相信 BSD make 使用命名变量来避免这种混淆。
  • %.o 目标中的$^ 不正确。它必须是$&lt;
  • @EtanReisner,我对$&lt; 的记忆是它是一个左箭头,其中第一个先决条件是“最左边”的箭头。所以在视觉上我把它读作“那里的那个,一直到左边”:) 而$^ 是“那里所有的文件,在我上面”。它足以让我记忆犹新……但对其他特殊变量没有帮助。
【解决方案2】:

问题是这样的:

$(MAIN_OBS): $(MAIN_SRC) 

您已声明每个目标文件都依赖于每个源文件。图书馆的也一样。上述规则扩展为:

foo.o bar.o xyzzy.o .... : foo.c bar.c xyzzy.c ...

因此,如果任何源文件的时间戳发生变化,每个目标文件都将被重新构建。

你所要做的就是拥有单独的依赖关系:

# foo.o depends only on foo.c, not on bar.c
foo.o: foo.c

# bar.o depends only on bar.c, not on foo.c
bar.o: bar.c

等等。通常,不这样做。相反,我们使用 GNU Make 的模式规则来编写一个通用规则:

%.o: %c
        # build commands to make .o from .c

已经有一个内置的规则通常就足够了,并且可以通过改变变量来定制它,比如CCCFLAGS

由于内置规则,简单的 makefile 通常只需要声明可执行对象和对象文件之间的依赖关系。 Make 将通过尝试各种可能性自动从目标文件中推断出先决条件。当被要求评估foo.o时,它会自动发现存在foo.c文件,这必须是foo.o的先决条件。然后,如果foo.o 早于foo.c 或不存在,Make 将搜索其规则数据库并发现此组合与%.o: %.c 模式规则匹配,它将运行其配方主体以更新foo.o

您应该使用:= 样式的立即变量赋值,而不是延迟的= 赋值。因为您在 = 赋值的右侧有 $(wildcard ...),所以每次替换变量时,都会评估 $(wildcard ...) 语法,并遍历文件系统以查找文件。

首先不要使用$(wildcard ...) 来收集源文件。除非您以一种非常干净的方式工作,否则这将引入不需要的文件,例如您碰巧创建的一些随机的test.c,因此test.o 会链接到您的程序(可能是成功的)。显式列出所有目标文件并不需要太多努力:

OBJS := foo.o bar.o ...

对于子目录中的对象,你可以使用$(addprefix ...)来缩短它:

OBJS := foo.o bar.o ... $(addprefix subdir/,stack.o parser.o ...)

【讨论】:

  • 这是a 问题,而不是the 问题。 问题LIB_OBS变量值。
猜你喜欢
  • 2014-07-20
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-21
  • 1970-01-01
  • 2020-05-04
  • 1970-01-01
相关资源
最近更新 更多