Make每次编译所有文件,包括未更改的文件

时间:2017-05-02 15:30:47

标签: c++ makefile gnu-make

我不知道为什么每次重新编译包含未更改文件的所有文件,都需要很长时间。我搜索了Stack Overflow并找到了一些解决方案,但我仍然无法解决我的问题。

我应该如何更改此makefile,以便它不会编译所有文件,包括未更改的文件?

这是我的make文件:

CXX = g++
CXXFLAGS = -g -Wall -O2 -std=c++11
BIN = bin
SRC = src
OBJ = obj
COMPILER = $(BIN)/compiler

LEX_CPP = $(SRC)/lex.cpp
UTIL_CPP = $(SRC)/util.cpp
TOKEN_CPP = $(SRC)/token.cpp
MAIN_CPP = $(SRC)/main.cpp
TEST_CPP = $(SRC)/test.cpp
PARSER_CPP = $(SRC)/parser.cpp
COMPILER_CPP = $(SRC)/compiler.cpp

COMPILER_OBJS = parser.o compiler.o test.o token.o lex.o util.o main.o 

dir_guard=@mkdir -p $(OBJ)

compiler: $(COMPILER_OBJS)
    $(CXX) $(CXXFLAGS) -o $(COMPILER) $(OBJ)/lex.o $(OBJ)/compiler.o $(OBJ)/parser.o $(OBJ)/test.o $(OBJ)/token.o $(OBJ)/util.o $(OBJ)/main.o 

main.o:
    $(dir_guard)
    $(CXX) $(CXXFLAGS) -o $(OBJ)/main.o -c $(MAIN_CPP)
lex.o: 
    $(dir_guard)
    $(CXX) $(CXXFLAGS) -o $(OBJ)/lex.o -c $(LEX_CPP)
util.o: 
    $(dir_guard)
    $(CXX) $(CXXFLAGS) -o $(OBJ)/util.o -c $(UTIL_CPP)
token.o: 
    $(dir_guard)
    $(CXX) $(CXXFLAGS) -o $(OBJ)/token.o -c $(TOKEN_CPP)
test.o:
    $(dir_guard)
    $(CXX) $(CXXFLAGS) -o $(OBJ)/test.o -c $(TEST_CPP)
compiler.o:
    $(dir_guard)
    $(CXX) $(CXXFLAGS) -o $(OBJ)/compiler.o -c $(COMPILER_CPP)
parser.o:
    $(dir_guard)
    $(CXX) $(CXXFLAGS) -o $(OBJ)/parser.o -c $(PARSER_CPP)

clean:
    rm $(OBJ)/*.o

我想将所有.o文件放到obj文件夹中,并将exe文件放到bin文件夹中。

我的操作系统是Ubuntu 15.04并使用GNU Make 4.0。

感谢任何帮助,提前谢谢!

3 个答案:

答案 0 :(得分:4)

只是非常清楚:你的makefile以各种方式被打破,djgandy和Norman Gray已经正确诊断出来了。但是对于为什么文件不断重建的具体问题的具体答案是:

token.o: 
    $(dir_guard)
    $(CXX) $(CXXFLAGS) -o $(OBJ)/token.o -c $(TOKEN_CPP)

不考虑所有其他问题,在这里你告诉make:"如果你想建立一个文件token.o,那么你可以用这个食谱来做#34;。但是这个配方并没有构建token.o,它构建了$(OBJ)/token.o。这是错误的,因为在配方运行后,目标(token.o仍然不存在。因此,下次你运行make时,它会说"哦,我需要构建token.o ... gee,它不存在所以我想我必须构建它...哦,这里有一个可以建立它的规则......“它仍然没有真正构建它。”

您的makefile违反了second rule of Makefiles每个非.PHONY规则都必须使用其目标的确切名称更新文件。

您的规则应始终构建$@,因为它始终具有相信您应该构建的目标路径。

答案 1 :(得分:3)

你在Makefile中重复了很多 - 这就是make应该避免的。

考虑以下

MODULES=parser compiler test token lex util main
COMPILER_OBJS=$(MODULES:%=$(OBJ)/%.o)

$(OBJ)/%.o: %.c
    @test -d $(OBJ) || mkdir $(OBJ)
    $(CXX) $(CXXFLAGS) -c -o $@ $<

compiler: $(COMPILER_OBJS)
    $(CXX) $(CXXFLAGS) -o $(COMPILER) $(COMPILER_OBJS)

也就是说,你正在使用'模式规则'告诉Make如何从.c文件创建目标文件,并且稍微不合标准但合理地将该文件放在另一个目录中。 / p>

使用基于模式的变量引用来设置COMPILER_OBJS意味着您只需要在一个地方指定模块列表。

最终结果应该是(我还没有测试过)make compiler(或者只是make,只要compiler仍然是文件中的第一个目标)将构建compiler二进制文件,重建那些.o个文件已过期。

一直存在使Makefile过于复杂的倾向,但使用模式规则和基于模式的变量引用是有用的核心功能,因此您应该确保理解这些行是如何工作的(尽管请注意这两个特性都是特定的)到GNU Make)。

答案 2 :(得分:1)

您没有为每个对象指定任何依赖项。 Make无法知道源文件所依赖的标头是否已更改或源文件的任何其他依赖项。例如,如果某个“定义”文件发生更改,则可能需要重新生成标头,这反过来又需要重新编译相关的源文件。

为了让Make基于dependents进行有条件的编译,你必须告诉Make make依赖是什么。但要注意,如果你弄错了,你可能会陷入一种令人讨厌的境地,在这种情况下,由于你的依赖列表不正确,或者只是被某些文件部分拾取,你所做的更改没有被提取。

我建议在自动生成依赖项时查看这些主题。对于非常小的项目,手动管理它们是可以的,但是对于许多文件而言,这些项此外,维护复杂的Makefile是一项很少享受的任务。

Makefile (Auto-Dependency Generation)

Makefile, header dependencies

有关MAKE参考,请参阅:http://kirste.userpage.fu-berlin.de/chemnet/use/info/make/make_4.html