Makefile Pattern规则:循环makefile.o< - makefile依赖项被删除

时间:2016-07-14 22:18:11

标签: c++ makefile circular-dependency

我正在研究一个C ++项目的makefile,它需要支持一些配置,即调试,发布,以及将来可能会有一些更自定义的配置。

目前,我生成的.o文件的命名约定是$(SOURCE_FULLPATH).$(CONFIGURATION).o。例如,ABC.cpp在调试模式下生成ABC.cpp.debug.o

现在我想编写模式规则,以独立于配置的方式生成这些目标文件。我做的是:从每个XX.o文件名中,我从.debug中删除.releaseXX后缀,并使用XX的剩余部分作为来源文件名。

%.o: $$(basename %)
    $(CC) $(CC_FLAGS) $(INCLUDE_FOLDERS) -c -o $@ $<

通过这个技巧,我可以正确构建可执行文件,除了我从make获得一条警告消息:

make: Circular makefile.o <- makefile dependency dropped.

我很困惑,因为我没有在我的makefile中的任何地方列出makefilemakefile.o作为目标或依赖项。我在SO上进行了搜索,但是关于循环依赖的大多数问题都在特定的用户源文件上,而不是makefile本身。 任何人都可以帮助我了解导致循环依赖的原因,以及如何摆脱此警告信息

下面列出了可以重现此问题的示例makefile。

.SECONDEXPANSION:

PROJECT := helloworld
CC := clang++
BUILD_FOLDER := Build
OBJ_FILE_SUFFIX := .o

# Source
CPP_FILES :=\
    Source/hello.cpp \
    Source/mysqrt.cpp \

INCLUDE_FOLDERS := \
    -IInclude

# MMD outputs the dependency files (".d" files). These files will be used by
# this makefile to allow for dependency checking on .h files.
CC_FLAGS += -MMD

EXISTING_OBJ_FILES = $(wildcard $(addsuffix *.o, $(basename $(CPP_FILES))))

##--------------------
## Targets definition
##--------------------
.PHONY:default
default: all

.PHONY:all
all: debug release

.PHONY:debug release
# Add a 'debug'/'release' suffix to the name of the object file
# e.g. hello.cpp -> hello.cpp.debug.o
debug release: OBJ_FILES=$(addsuffix .$@$(OBJ_FILE_SUFFIX), $(CPP_FILES))
debug release: $${OBJ_FILES}    # Use Secondary Expansion to get the obj names
    $(CC) $^ -o $(BUILD_FOLDER)/$(PROJECT)_$@ 

# Strip configuration name from the end of the object file name
%.o: $$(basename %)
    $(CC) $(CC_FLAGS) $(INCLUDE_FOLDERS) -c -o $@ $<

## clean: remove executable, all object files, and all dependency files
.PHONY:clean
clean:
    -rm -f $(BUILD_FOLDER)/$(PROJECT) $(EXISTING_OBJ_FILES) $(EXISTING_OBJ_FILES:.o=.d)

# Include the dependent files so that in later builds, modified .h files 
# will cause all .cpp dependent on them to rebuild
-include $(OBJ_FILES:.o=.d)

文件夹结构

makefile
Source
- hello.cpp
- mysqrt.cpp
Include
- mysqrt.h

make debug的完整输出是

make: Circular makefile.o <- makefile dependency dropped.
clang++ -MMD -IInclude -c -o Source/hello.cpp.debug.o Source/hello.cpp
clang++ -MMD -IInclude -c -o Source/mysqrt.cpp.debug.o Source/mysqrt.cpp
clang++ Source/hello.cpp.debug.o Source/mysqrt.cpp.debug.o -o Build/helloworld_debug 

除了第一行外,一切都很好。

如果我的makefile中有任何不良做法(我仍然是makefile中的新手),如果有人能指出我,我也会非常感激。提前谢谢!

1 个答案:

答案 0 :(得分:2)

GNU Make总是尝试更新它之前读过的makefile 做其他事情。如果它找到了告诉它的规则和先决条件 更新makefile,然后它会这样做,然后从头开始 - 包括尝试更新makefile。请参阅3.5 How Makefiles Are Remade

在你的食谱中:

%.o: $$(basename %)
    $(CC) $(CC_FLAGS) $(INCLUDE_FOLDERS) -c -o $@ $<

您已向make提供了从makefile.o提出makefile的规则。

它也是内置配方中规则的反转

%: %.o
    $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@

从单个目标文件生成可执行文件。所以你的食谱引入了循环性:

makefile.o <- makefile <- makefile.o

makemakefile本身视为目标时。 您可以通过明确删除内置逆规则来抑制循环, 通过编写空规则:

%: %.o
makefile中的

。然后你可以观察到以下混乱 编译器:

$ make makefile.o
clang++   -c -o makefile.o makefile
clang: warning: makefile: 'linker' input unused

如果您尝试制作任何依赖的目标,也会发生同样的情况 在makefile.o

假设您没有依赖的目标可能是安全的 makefile.o。然而,一个规则,将尝试 从任何现有文件foo.o编译foo显然比你更加彻底 想要还是需要。对于您希望捕获的特定依赖模式:

foo.cpp.{debug|release}.o: foo.cpp

你最好用:

%.o: $$(basename $$(basename %)).cpp
    $(CC) $(CC_FLAGS) $(INCLUDE_FOLDERS) -c -o $@ $<

请注意,顺便说一句,在GNU Make约定中 - 那些约定 由GNU Make的内置规则假设 - CC表示您的C编译器 CXX表示您的C ++编译器。同样,C编译器的标志是 表示为CFLAGS,C ++编译器的标志表示为CXXFLAGS

预处理程序的标志用CPPFLAGS-I 路径选项表示 - 这些是预处理程序选项 - 通常通过CPPFLAGS传递。