假设我有一个带有规则的makefile
%.o: %.c
gcc -Wall -Iinclude ...
每当头文件更改时,我都希望重建* .o。无论何时/include
更改任何头文件,都不必重建依赖关系列表,而必须重建目录中的所有对象。
我想不出一种改变规则以适应这一需求的好方法,我愿意提出建议。如果不需要对标题列表进行硬编码,则可获得加分
假设我有一个带有规则的makefile
%.o: %.c
gcc -Wall -Iinclude ...
每当头文件更改时,我都希望重建* .o。无论何时/include
更改任何头文件,都不必重建依赖关系列表,而必须重建目录中的所有对象。
我想不出一种改变规则以适应这一需求的好方法,我愿意提出建议。如果不需要对标题列表进行硬编码,则可获得加分
Answers:
如果使用的是GNU编译器,则编译器可以为您汇编依赖项列表。Makefile片段:
depend: .depend
.depend: $(SRCS)
rm -f ./.depend
$(CC) $(CFLAGS) -MM $^ -MF ./.depend;
include .depend
要么
depend: .depend
.depend: $(SRCS)
rm -f ./.depend
$(CC) $(CFLAGS) -MM $^ > ./.depend;
include .depend
其中SRCS
的变量指向您的整个源文件列表。
也有该工具makedepend
,但我从未像现在这样喜欢它gcc -MM
depend
仅在源文件更改后才能运行?它似乎每次都在运行,无论...
build/file.o
?
大多数答案出奇地复杂或错误。但是,简单而强大的示例已发布在其他地方[ codereview ]。诚然,gnu预处理器提供的选项有些混乱。但是,记录了从构建目标中删除所有目录的过程,-MM
而不是错误[ gpp ]:
默认情况下,CPP使用主输入文件的名称,删除任何 目录组件和任何文件后缀(例如“ .c”),并附加平台的通常对象后缀。
(稍微更新)-MMD
选项可能是您想要的。为了完整起见,一个支持多个src目录并带有一些注释的目录的makefile示例。对于没有构建目录的简单版本,请参见[ codereview ]。
CXX = clang++
CXX_FLAGS = -Wfatal-errors -Wall -Wextra -Wpedantic -Wconversion -Wshadow
# Final binary
BIN = mybin
# Put all auto generated stuff to this build dir.
BUILD_DIR = ./build
# List of all .cpp source files.
CPP = main.cpp $(wildcard dir1/*.cpp) $(wildcard dir2/*.cpp)
# All .o files go to build dir.
OBJ = $(CPP:%.cpp=$(BUILD_DIR)/%.o)
# Gcc/Clang will create these .d files containing dependencies.
DEP = $(OBJ:%.o=%.d)
# Default target named after the binary.
$(BIN) : $(BUILD_DIR)/$(BIN)
# Actual target of the binary - depends on all .o files.
$(BUILD_DIR)/$(BIN) : $(OBJ)
# Create build directories - same structure as sources.
mkdir -p $(@D)
# Just link all the object files.
$(CXX) $(CXX_FLAGS) $^ -o $@
# Include all .d files
-include $(DEP)
# Build target for every single object file.
# The potential dependency on header files is covered
# by calling `-include $(DEP)`.
$(BUILD_DIR)/%.o : %.cpp
mkdir -p $(@D)
# The -MMD flags additionaly creates a .d file with
# the same name as the .o file.
$(CXX) $(CXX_FLAGS) -MMD -c $< -o $@
.PHONY : clean
clean :
# This should remove all generated files.
-rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP)
此方法之所以有效,是因为如果单个目标有多个依赖项行,则这些依赖项将简单地合并在一起,例如:
a.o: a.h
a.o: a.c
./cmd
等效于:
a.o: a.c a.h
./cmd
如以下内容所述:Makefile单个目标有多个依赖项行?
CPP
应读取CPPS
a.cpp
,b.cpp
)放在其中./src/
,那么该替换不会生效$(OBJ)=./build/src/a.o ./build/src/b.o
吗?
正如我在这里发布的, gcc可以创建依赖关系并同时进行编译:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.c
$(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<
“ -MF”参数指定用于存储依赖项的文件。
“ -include”开头的破折号告诉Make在.d文件不存在时继续执行(例如,在第一次编译时)。
注意,关于-o选项,gcc中似乎存在一个错误。如果将对象文件名设置为obj / _file__c.o,则生成的文件 .d仍将包含文件 .o,而不是obj / _file__c.o。
怎么样:
includes = $(wildcard include/*.h)
%.o: %.c ${includes}
gcc -Wall -Iinclude ...
您也可以直接使用通配符,但是我倾向于在多个地方使用通配符。
请注意,这仅适用于小型项目,因为它假定每个目标文件都依赖于每个头文件。
make clean all
每次运行。
gcc
行根本不执行,而是执行内置规则(%o: %.c
rule)。
与迈克尔·威廉姆森(Michael Williamson)接受的答案相比,我更喜欢这种解决方案,它可以捕获对源+内联文件,源+头文件以及最终源的更改。这样做的好处是,仅进行了少量更改就不会重新编译整个库。对于具有几个文件的项目来说,这不是一个很大的考虑因素,如果您有10个或100个源,那么您会注意到其中的区别。
COMMAND= gcc -Wall -Iinclude ...
%.o: %.cpp %.inl
$(COMMAND)
%.o: %.cpp %.hpp
$(COMMAND)
%.o: %.cpp
$(COMMAND)
以下对我有用:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.cpp
$(CXX) $(CFLAGS) -MMD -c -o $@ $<
索菲(Sophie)的略微修改版本 答案的它允许将* .d文件输出到另一个文件夹(我只会粘贴生成依赖文件的有趣部分):
$(OBJDIR)/%.o: %.cpp
# Generate dependency file
mkdir -p $(@D:$(OBJDIR)%=$(DEPDIR)%)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM -MT $@ $< -MF $(@:$(OBJDIR)/%.o=$(DEPDIR)/%.d)
# Generate object file
mkdir -p $(@D)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
注意参数
-MT $@
用于确保生成的* .d文件中的目标(即目标文件名)包含* .o文件的完整路径,而不仅仅是文件名。
我不知道为什么将-MMD 与-c 结合使用时不需要此参数(在Sophie的版本中)。通过这种组合,似乎可以将* .o文件的完整路径写入* .d文件。如果没有这种组合,-MMD还将仅将纯文件名而不将任何目录组件写入* .d文件。也许有人知道为什么-MMD与-c结合使用时会写入完整路径。我在g ++手册页中找不到任何提示。