我怎样才能让Makefile自动重build包含修改过的头文件的源文件? (在C / C ++中)
我有下面的makefile,我用它来构build一个正在处理的程序(实际上是一个内核)。 它从零开始,我正在学习这个过程,所以它不是完美的,但是我认为在这个阶段我已经足够强大,可以编写makefile了。
AS = nasm CC = gcc LD = ld TARGET = core BUILD = build SOURCES = source INCLUDE = include ASM = assembly VPATH = $(SOURCES) CFLAGS = -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \ -nostdinc -fno-builtin -I $(INCLUDE) ASFLAGS = -f elf #CFILES = core.c consoleio.c system.c CFILES = $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) SFILES = assembly/start.asm SOBJS = $(SFILES:.asm=.o) COBJS = $(CFILES:.c=.o) OBJS = $(SOBJS) $(COBJS) build : $(TARGET).img $(TARGET).img : $(TARGET).elf c:/python26/python.exe concat.py stage1 stage2 pad.bin core.elf floppy.img $(TARGET).elf : $(OBJS) $(LD) -T link.ld -o $@ $^ $(SOBJS) : $(SFILES) $(AS) $(ASFLAGS) $< -o $@ %.o: %.c @echo Compiling $<... $(CC) $(CFLAGS) -c -o $@ $< #Clean Script - Should clear out all .o files everywhere and all that. clean: -del *.img -del *.o -del assembly\*.o -del core.elf
我的这个makefile的主要问题是,当我修改一个或多个C文件包含的头文件时,C文件不会被重build。 我可以很容易地解决这个问题,因为我所有的头文件都是我C文件的依赖关系,但是如果我改变/添加一个头文件,那么这将会导致完整的项目重build,这不会很优雅。
我想要的只是包含我改变的头文件的C文件被重build,并且整个项目再次链接。 我可以通过使所有头文件成为目标的依赖关系,但我不知道如何使C文件无效,当他们包含的头文件更新。
我听说GCC有一些命令可以做到这一点(所以makefile可以找出哪些文件需要重build),但我不能为我的生活find一个实际的实例来看看。 有人可以发布一个解决scheme,将在makefile中启用此行为?
编辑:我应该澄清,我熟悉把个人的目标,并有每个target.o需要头文件的概念。 这要求我每次在某个地方包含头文件时都要编辑makefile,这有点痛苦。 我正在寻找一个解决scheme,可以派生自己的头文件依赖项,我相当肯定,我已经看到在其他项目。
正如本网站上其他地方已经指出的,请参阅此页面: http : //make.paulandlesley.org/autodep.html
简而言之,gcc可以自动为你创build.d依赖文件,这是你的make文件的小片段,它包含你编译的.c文件的依赖关系。 每次更改.c文件并编译它,.d文件将被更新。
除了向gcc添加-M标志之外,还需要将.d文件包含在makefile中(就像Chris上面所写的那样)。 在使用sed解决的页面中有一些更复杂的问题,但是你可以忽略它们并且做一个“make clean”来清除.d文件,每当抱怨不能build立一个不再存在的头文件。
您可以像其他人所说的那样添加“make depend”命令,但是为什么不能让gcc同时创build依赖项并进行编译:
DEPS := $(COBJS:.o=.d) -include $(DEPS) %.o: %.c $(CC) -c $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<
'-MF'参数指定一个文件来存储依赖关系。
在'-include'开始处的短划线告诉Make在.d文件不存在时继续(例如在第一次编译时)。
注意,关于-o选项似乎有一个gcc中的错误。 如果将对象文件名设置为obj/_file__c.o
那么生成的_file_.d
将仍然包含_file_.o
,而不是obj/_file_c.o
。
这相当于克里斯·多德的答案 ,但是使用了不同的命名约定(并且巧合的是不需要sed
魔法。
如果您使用的是GNU编译器,则编译器可以为您组合一系列依赖项。 Makefile片段:
depend: .depend .depend: $(SOURCES) rm -f ./.depend $(CC) $(CFLAGS) -MM $^>>./.depend; include .depend
也有工具makedepend
,但我从来没有像gcc -MM
那样喜欢它
您必须为每个C文件创build单独的目标,然后将头文件列为依赖项。 你仍然可以使用你的通用目标,然后放置.h
依赖项,就像这样:
%.o: %.c @echo Compiling $<... $(CC) $(CFLAGS) -c -o $@ $< foo.c: bar.h # And so on...
除了@mipadi所说的之外,还可以探索使用' -M
'选项来生成依赖关系的logging。 你甚至可以把它们生成一个单独的文件(可能是'depend.mk'),然后把它包含在makefile中。 或者你可以find一个“ make depend
”规则,用正确的依赖关系编辑makefile(Google条款:“不要删除这行”并依赖)。
基本上,当头文件改变时,你需要dynamic创buildmakefile规则来重build目标文件。 如果你使用gcc和gnumake,这相当简单; 只是把这样的东西:
$(OBJDIR)/%.d: %.c $(CC) -MM -MG $(CPPFLAGS) $< | sed -e 's,^\([^:]*\)\.o[ ]*:,$(@D)/\1.o $(@D)/\1.d:,' >$@ ifneq ($(MAKECMDGOALS),clean) include $(SRCS:%.c=$(OBJDIR)/%.d) endif
在你的makefile中。
没有答案为我工作。 例如马丁Fido的答案build议gcc可以创build依赖文件,但是当我试图为它生成空(零字节)的对象文件没有任何警告或错误。 这可能是一个海湾合作委员会的bug。 我在
$ gcc –version gcc(GCC)4.4.7 20120313(Red Hat 4.4.7-16)
所以这里是我完整的Makefile,适合我。 它是解决scheme+其他人没有提到的东西的组合(例如“.cc.o”指定的“后缀replace规则”):
CC = g++ CFLAGS = -Wall -g -std=c++0x INCLUDES = -I./includes/ # LFLAGS = -L../lib # LIBS = -lmylib -lm # List of all source files SRCS = main.cc cache.cc # Object files defined from source files OBJS = $(SRCS:.cc=.o) # # define the executable file MAIN = cache_test #List of non-file based targets: .PHONY: depend clean all ## .DEFAULT_GOAL := all # List of dependencies defined from list of object files DEPS := $(OBJS:.o=.d) all: $(MAIN) -include $(DEPS) $(MAIN): $(OBJS) $(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LFLAGS) $(LIBS) #suffix replacement rule for building .o's from .cc's #build dependency files first, second line actually compiles into .o .cc.o: $(CC) $(CFLAGS) $(INCLUDES) -c -MM -MF $(patsubst %.o,%.d,$@) $< $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< clean: $(RM) *.o *~ $(MAIN) *.d
注意我使用了.cc ..上面的Makefile很容易调整.c文件。
同样重要的是要注意这两行的重要性:
$(CC) $(CFLAGS) $(INCLUDES) -c -MM -MF $(patsubst %.o,%.d,$@) $< $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
所以gcc被调用一次构build一个依赖文件,然后实际编译一个.cc文件。 等等每个源文件。
更简单的解决scheme:只需使用Makefile将.c编译为.o编译规则就会依赖于头文件和其他任何与项目相关的依赖项。
例如,在某个地方的Makefile中:
DEPENDENCIES=mydefs.h yourdefs.h Makefile GameOfThrones.S07E01.mkv ::: (your other Makefile statements like rules ::: for constructing executables or libraries) # Compile any .c to the corresponding .o file: %.o: %.c $(DEPENDENCIES) $(CC) $(CFLAGS) -c -o $@ $<
我相信mkdep
命令是你想要的。 它实际上扫描#include
行的.c文件,并为它们创build一个依赖关系树。 我相信Automake / Autoconf项目默认使用这个。