views:

2877

answers:

8

I have the following makefile that I use to build a program (a kernel, actually) that I'm working on. Its from scratch and I'm learning about the process, so its not perfect, but I think its powerful enough at this point for my level of experience writing makefiles.

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

My main issue with this makefile is that when I modify a header file that one or more C files include, the C files aren't rebuilt. I can fix this quite easily by having all of my header files be dependencies for all of my C files, but that would effectively cause a complete rebuild of the project any time I changed/added a header file, which would not be very graceful.

What I want is for only the C files that include the header file I change to be rebuilt, and for the entire project to be linked again. I can do the linking by causing all header files to be dependencies of the target, but I cannot figure out how to make the C files be invalidated when their included header files are newer.

I've heard that GCC has some commands to make this possible (so the makefile can somehow figure out which files need to be rebuilt) but I can't for the life of me find an actual implementation example to look at. Can someone post a solution that will enable this behavior in a makefile?

EDIT: I should clarify, I'm familiar with the concept of putting the individual targets in and having each target.o require the header files. That requires me to be editing the makefile every time I include a header file somewhere, which is a bit of a pain. I'm looking for a solution that can derive the header file dependencies on its own, which I'm fairly certain I've seen in other projects.

+4  A: 

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...
mipadi
+3  A: 

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).

Jonathan Leffler
+1  A: 

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.

Chris Dodd
I sort of understand this, except that (aside fromt he -MM and -MG flags being new) I don't understand what the regex lookin' line of cryptic text is for. That's not going to make me teammates happy... ^_^ I'll try it though and see if I have any results.
Nicholas Flynt
sed is short for "stream editor" which can modify a stream of text without needing to use a file. It is a standard Unix tool and smaller and faster so it is used more often than awk or perl.
Zan Lynx
Ah, there's the issue: I'm doing this under Windows.
Nicholas Flynt
+5  A: 

As already pointed out elsewhere on this site, see this page: http://make.paulandlesley.org/autodep.html

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.

UdiM
+3  A: 

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: $(OBJS)
        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

dmckee
A: 

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.

jdizzle
+1  A: 

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.

Martin Fido
A: 

I like Martin Fido's answer because it seems to have an important advantage: if a singe source file is updated, then only its dependencies have to be re-created.

What do the .d files contain? It looks like they are, in themselves, makefiles. In that case, they must contain rules whose targets are the object files and whose prerequisites are the header files #included by the source. Am I right so far? Since we already have a rule for the .o files, I'm also assuming that the .d files' rules contain no commands: sourcename.o : dependency1.h dependency2.h ;

Now I want to go a little off-topic if you'll forgive me. I am using Eclipse/MinGW with Qt, so my Makefiles are generated by QMAKE. I'd like to generate them so that they use Martin's method above of generating dependencies. There are (at least!) two parts to my problem: 1. I don't know how what to put in my .pro files to reproduce Martin's Makefile usage, in particular how to add the line: -include $(DEPS). 2. Currently, qmake is generating old-fashioned suffix rules like this: .cpp.o: $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< CXXFLAGS is something I can easily alter to include Martin's -MM and -MF flags, BUT since these are "implicit" rules, does this mean that the extra rules included from the .d files would override, rather than supplement, the rule that executes $(CXX)?

I can now answer the Qt-related part of my question ... sort of. It seems Qt is Microsoftesque in that you're not supposed to worry your pretty little head about how anything works internally. I haven't found any reference to how you can affect the type of rules qmake generates. The good news is that qmake already creates an extra (non-implicit) rule in the makefile for each separate .cpp file and each rule includes explicit dependencies. Now the trick is to give qmake a list of directories which it should search for said dependencies. You might think it would use its variable INCLUDEPATH (which is what the compiler uses to resolve #includes), but no. Instead you have to populate variable DEPENDPATH. I suppose the benefit of having two qmake variables is that the latter only really needs to list directories where the header files are likely to change, which might speed things up a bit.

Now why is there an explicit rule for each .cpp file, but not for my .f files? Feel free to tell me I should start a new thread....

major_tom3
You are correct - the .d files are Makefiles that contain rules with no commands. The rule looks something like: file.o: file.c path/first.h path/second.hUnfortunately I can't answer your other questions.
Martin Fido