使用标头跟踪Makefile的额外构建/缺少目标文件

时间:2013-05-05 14:26:38

标签: g++ makefile dependencies gnu-make

我编写了一个(GNU make)Makefile,用于在header include中执行自动依赖跟踪。一切都很好除了,第二次输入make时,整个代码库重建。只有第三次和连续时间键入make才会显示无需执行任何操作的消息。

SRCDIR := src
INCDIR := inc
ifeq ($(DEBUG),1)
    OBJDIR := debug_obj
    BINDIR := debug_bin
else
    OBJDIR := obj
    BINDIR := bin
endif

BINS := prog1 prog2 prog3 prog4
SRCS := $(wildcard $(SRCDIR)/*.cpp)
OBJS := $(patsubst $(SRCDIR)/%,$(OBJDIR)/%,$(SRCS:.cpp=.o))
DEPS := $(OBJS:.o=.d)

CC := g++
COMMON_FLAGS := -Wall -Wextra -Werror -std=c++11 -pedantic
ifeq ($(DEBUG),1)
    CXX_FLAGS := $(COMMON_FLAGS) -Og -g
else
    CXX_FLAGS := $(COMMON_FLAGS) -O3 -D NDEBUG
endif

all: $(addprefix $(BINDIR)/,$(BINS)) | $(BINDIR)

$(OBJDIR) $(BINDIR):
    @ mkdir -p $@;

$(BINDIR)/%: $(OBJDIR)/%.o | $(BINDIR)
    $(CC) $(CPP_FLAGS) $< -o $@;

$(OBJDIR)/%.o: $(SRCDIR)/%.cpp | $(OBJDIR)
    $(CC) $(CPP_FLAGS) -MMD -MP -c $< -o $@;

-include $(DEPS)

.PHONY: all clean

clean:
    - rm -f $(OBJS);
    - rm -f $(DEPS);
    - rm -f $(addprefix $(BINDIR)/,$(BINS));
    - rmdir $(OBJDIR) $(BINDIR) 2> /dev/null || true

显然,某些依赖项已更改,因此我尝试在make -n -d | grep 'newer'的第一次调用后运行make,其中显示了这一点:

  

先决条件obj/prog1.o' is newer than target bin / prog1'。
    先决条件obj/prog2.o' is newer than target bin / prog2'。
    先决条件obj/prog3.o' is newer than target bin / prog3'。
    先决条件obj/prog4.o' is newer than target bin / prog4'。

ls -la obj/*

显示存在依赖项(* .d)但不存在对象(* .o)文件。我假设这与g++ -MMD -MP的工作方式有关,但尽管明显缺少对象文件,但在第一个make之后会出现二进制文件。

this question的答案表明两者都是同时生成的,man g++对此我并不反对。

我已经阅读了其他一些与自动依赖关系跟踪相关的问题和答案,但我没有看到这个问题。为什么会这样?你能建议修复吗?

更新

更仔细地看一下make的第一次调用,最后显示了这个意外的(对我而言):

  

rm obj / prog1.o obj / prog2.o obj / prog3.o obj / prog4.o

这回答了一个问题但提出了另一个问题。

更新

我也在调试输出中找到了这个。

Considering target file `prog1'.
 File `prog1' does not exist.
make: *** No rule to make target `prog1'.  Stop.
 No implicit rule found for `prog1'.
 Finished prerequisites of target file `prog1'.
Must remake target `prog1'.

我注意到prog1缺少bin /前缀。没有什么能解释为什么第一次运行会删除目标文件,但第二次运行会离开它们。这似乎是问题的核心。

2 个答案:

答案 0 :(得分:0)

make将对象文件视为中间件并相应地删除它们。添加:

.SECONDARY: $(OBJS)

解决了这个问题。我不知道为什么它是第一次调用而不是第二次调用。欢迎提出意见。

答案 1 :(得分:0)

.o文件不存在的原因是它们被视为中间文件,因此请删除它们。但是,这不应该导致您的构建中出现任何问题,因为只要make可以设想中间文件,它就会意识到如果它的先决条件比其父级更早,则不需要重建它(在这种情况下,只要prog1比例如prog1.cpp更新。

我无法重现您的第二次构建重建所有内容的体验。将需要更多细节。您显示的输出并不有趣,因为这只是说make不需要重建.o文件(它比先决条件更新)。您需要在输出中找到解释为什么make 需要重建.o文件的行。如果您提供该信息,我们可能会提供帮助。

对你的makefile只做了几点评论:首先,我认为迫使mkdir规则始终成功并不是一个好主意。如果mkdir失败,你想要你的构建失败。可能你这样做了所以如果目录已经存在就不会有问题,但这不是必需的,因为mkdir -p调用永远不会因为目录存在而失败(但如果目录无法创建则会失败)出于其他原因,如权限)。您也可以将这些组合成一个包含多个目标的规则:

$(BINDIR) $(OBJDIR):
        @mkdir -p $@

接下来,您不需要在命令行中使用分号,事实上,添加它们会导致构建稍慢。

最后,小编,但编译行中正确的选项顺序是-c -o $@ $<;源文件不是(这是一个常见的误解)-c选项的参数。 -c选项(如-E-s等)告诉编译器要创建哪个输出;在-c的情况下,它意味着编译成目标文件。这些选项不带参数。文件名是一个单独的参数。