如何让 Makefile 自动重建包含修改过的头文件的源文件? (在 C/C + + 中)

下面的 makefile 用于构建我正在使用的程序(实际上是内核)。它从头开始,我正在学习这个过程,所以它不是完美的,但我认为它足够强大,在这一点上,我的经验水平写 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 文件。我可以通过让所有的头文件都成为所有 C 文件的依赖项来轻松地解决这个问题,但是这会在任何时候改变/添加一个头文件时有效地导致项目的完全重建,这不会很优雅。

我想要的只是 C 文件,包括的头文件,我改变重建,并为整个项目再次链接。我可以通过使所有头文件成为目标的依赖项来进行链接,但是我不知道如何使 C 文件在其包含的头文件更新时失效。

我听说 GCC 有一些命令可以实现这一点(这样 makefile 就可以以某种方式确定需要重新构建哪些文件) ,但是我一辈子都找不到一个实际的实现示例来查看。有人可以在 makefile 中发布一个解决方案来启用这种行为吗?

编辑: 我应该澄清一下,我熟悉这样一个概念: 将单个目标放入,并且让每个 target.o 需要头文件。这要求我每次在某处包含头文件时都要编辑 makefile,这有点麻烦。我正在寻找一个解决方案,可以获得头文件依赖关系本身,这是我相当肯定我在其他项目中看到的。

89878 次浏览

You'll have to make individual targets for each C file, and then list the header file as a dependency. You can still use your generic targets, and just place the .h dependencies afterwards, like so:

%.o: %.c
@echo Compiling $<...
$(CC) $(CFLAGS) -c -o $@ $<


foo.c: bar.h
# And so on...

Over and above what @mipadi said, you can also explore the use of the '-M' option to generate a record of the dependencies. You might even generate those into a separate file (perhaps 'depend.mk') which you then include in the makefile. Or you can find a 'make depend' rule which edits the makefile with the correct dependencies (Google terms: "do not remove this line" and depend).

Basically, you need to dynamically create the makefile rules to rebuild the object files when the header files change. If you use gcc and gnumake, this is fairly easy; just put something like:

$(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

in your makefile.

As already pointed out elsewhere on this site, see this page: Auto-Dependency Generation

In short, gcc can automatically create .d dependency files for you, which are mini makefile fragments containing the dependencies of the .c file you compiled. Every time you change the .c file and compile it, the .d file will be updated.

Besides adding the -M flag to gcc, you'll need to include the .d files in the makefile (like Chris wrote above). There are some more complicated issues in the page which are solved using sed, but you can ignore them and do a "make clean" to clear away the .d files whenever make complains about not being able to build a header file that no longer exists.

This is equivalent to Chris Dodd's answer, but uses a different naming convention (and coincidentally doesn't require the sed magic. Copied from a later duplicate.


If you are using a GNU compiler, the compiler can assemble a list of dependencies for you. Makefile fragment:

depend: .depend


.depend: $(SOURCES)
rm -f ./.depend
$(CC) $(CFLAGS) -MM $^>>./.depend;


include .depend

There is also the tool makedepend, but I never liked it as much as gcc -MM

I believe the mkdep command is what you want. It actually scans .c files for #include lines and creates a dependency tree for them. I believe Automake/Autoconf projects use this by default.

You could add a 'make depend' command as others have stated but why not get gcc to create dependencies and compile at the same time:

DEPS := $(COBJS:.o=.d)


-include $(DEPS)


%.o: %.c
$(CC) -c $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<

The '-MF' parameter specifies a file to store the dependencies in.

The dash at the start of '-include' tells Make to continue when the .d file doesn't exist (e.g. on first compilation).

Note there seems to be a bug in gcc regarding the -o option. If you set the object filename to say obj/_file__c.o then the generated _file_.d will still contain _file_.o, not obj/_file_c.o.

None of the answers worked for me. E.g. Martin Fido's answer suggests gcc can create dependency file, but when I tried that it was generating empty (zero bytes) object files for me without any warnings or errors. It might be a gcc bug. I am on

$ gcc --version gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)

So here's my complete Makefile that works for me; it's a combination of solutions + something that wasn't mentioned by anyone else (e.g. "suffix replacement rule" specified as .cc.o:):

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

Notice I used .cc .. The above Makefile is easy to adjust for .c files.

Also notice importance of these two lines :

$(CC) $(CFLAGS) $(INCLUDES) -c -MM -MF $(patsubst %.o,%.d,$@) $<
$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<

so gcc is called once to build a dependency file first, and then actually compiles a .cc file. And so on for each source file.

Simpler solution: Just use the Makefile to have the .c to .o compilation rule be dependent on the header file(s) and whatever else is relevant in your project as a dependency.

E.g., in the Makefile somewhere:

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 $@ $<