假设我有一个Makefile规则:
%.o: %.c
gcc -Wall -Iinclude ...
每当一个头文件更改时,我希望 *.o 能够被重新构建。与其计算出所有依赖关系,不如只要 /include
目录下的任何头文件更改,那么该目录下的所有对象都必须重新构建。
我想不出一种好的方法来修改规则以适应这一点,欢迎提供建议。如果头文件列表不需要硬编码,则可获得额外加分。
假设我有一个Makefile规则:
%.o: %.c
gcc -Wall -Iinclude ...
每当一个头文件更改时,我希望 *.o 能够被重新构建。与其计算出所有依赖关系,不如只要 /include
目录下的任何头文件更改,那么该目录下的所有对象都必须重新构建。
我想不出一种好的方法来修改规则以适应这一点,欢迎提供建议。如果头文件列表不需要硬编码,则可获得额外加分。
如果您正在使用GNU编译器,编译器可以为您组装一个依赖项列表。Makefile片段:
depend: .depend
.depend: $(SRCS)
rm -f "$@"
$(CC) $(CFLAGS) -MM $^ -MF "$@"
include .depend
或者depend: .depend
.depend: $(SRCS)
rm -f "$@"
$(CC) $(CFLAGS) -MM $^ > "$@"
include .depend
其中SRCS
是一个指向你所有源文件列表的变量。
还有一个工具叫做makedepend
,但我从来没有像gcc -MM
一样喜欢它。
depend
只在源文件更改时运行?它似乎每次都在运行... - chasebuild/file.o
? - RiaD大多数答案出人意料的复杂或错误。然而,已经在其他地方发布了简单而强大的示例[codereview]。诚然,gnu预处理器提供的选项有点令人困惑。然而,使用-MM
从构建目标中删除所有目录是有文档记录并且不是bug[gpp]:
默认情况下,CPP采用主输入文件的名称,删除任何目录组件和任何文件后缀,例如'.c',并附上平台通常的对象后缀。
(较新的) -MMD
选项可能是您想要的。为了完整起见,这里提供一个支持多个src目录和build目录的makefile示例,并带有一些注释。如果您需要一个没有build目录的简单版本,请参见[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 multiple dependency lines for a single target?
CPP
应该改为CPPS
。 - ctruczaa.cpp
,b.cpp
)在 ./src/
目录下,那么这个替换操作会使得 $(OBJ)=./build/src/a.o ./build/src/b.o
吗? - galoisGNU make
标准变量名:https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html。因此,使用CXXFLAGS而不是CXX_FLAGS,并且链接时您是不是指的是LDFLAGS而不是CXX_FLAGS? - Matt Wallismake
时,没有重新编译任何东西。难道最好为 *.d makefiles 设定一个规则以便重新生成吗?或者我们只使用 clean
来删除某些内容? - LRDPRDX正如我在这里所发的帖子中提到的,gcc可以同时创建依赖关系并进行编译:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.c
$(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<
'-MF'参数指定了一个文件来存储所依赖的文件。
'-include' 开头的破折号告诉Make在.d文件不存在时继续编译(例如第一次编译)。
请注意,gcc中关于-o选项似乎存在一个错误。 如果你将对象文件名设置为比如obj/_file__c.o,则生成的 file .d仍将包含 file .o而不是obj/_file__c.o。
man gcc
中说 -MM
暗示了 -E
,该选项会在预处理后停止。你需要使用 -MMD
代替 -MM
:https://dev59.com/NnE95IYBdhLWcg3wPrgI#30142139 - Ciro Santilli OurBigBook.com这样怎么样:
includes = $(wildcard include/*.h)
%.o: %.c ${includes}
gcc -Wall -Iinclude ...
您也可以直接使用通配符,但我发现我需要在多个地方使用它们。
请注意,这仅适用于小型项目,因为它假定每个目标文件都依赖于每个头文件。
make clean all
的等价物。 - Julien Guertaultgcc
行根本没有执行,而是执行了内置规则(%o:%.c
规则)。 - Penghe GengMartin的解决方案很好,但是不能处理位于子目录中的.o文件。Godric指出,-MT标志可以解决这个问题,但同时会阻止正确写入.o文件。下面的方法将解决这两个问题:
DEPS := $(OBJS:.o=.d)
-include $(DEPS)
%.o: %.c
$(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
$(CC) $(CFLAGS) -o $@ $<
这是两行代码:
CPPFLAGS = -MMD
-include $(OBJS:.c=.d)
只要你在OBJS
中列出了所有的目标文件,这将与默认的构建配方一起使用。
$(CC) $(CFLAGS) -MD -o $@ $<
我用gcc 4.8.3进行了测试。
$(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 $@
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 $@ $<
-MMD
:将创建一个.d文件,其内容类似于“A.o: B.cpp C.hpp D.hpp”。这将存储目标文件以及它所依赖的.cpp和.hpp文件。这是生成二进制文件的技巧,当.hpp文件发生更改时,可以再次生成二进制文件。
-include
:https://www.gnu.org/software/make/manual/html_node/Include.html,它将包含“A.o: B.cpp C.hpp D.hpp”字符串,创建一个多行规则。 - Caio V.