Skip to main content
Added link to online doc.
Source Link
Luke Van In
  • 2.7k
  • 14
  • 26

For more information, here's a nice introduction to how make works.

For more information, here's a nice introduction to how make works.

Source Link
Luke Van In
  • 2.7k
  • 14
  • 26

The makefile can be confusing at times because a lot of the steps are left out for simplicity sakes, and implied by make using default behaviour.

It might make a bit more sense if you manually replace the variables (OBJS and HDRS) with their "expanded" values, e.g.:

# automatically generated list of object files
# OBJS = $(SRCS:.c=.o)
OBJS = speller.o dictionary.o

# default target    // here comes the confusing part!
# $(EXE): $(OBJS) $(HDRS) Makefile
#    $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS)
speller: speller.o dictionary.o dictionary.h Makefile
    $(CC) $(CFLAGS) -o $@ speller.o dictionary.o

# dependencies 
# $(OBJS): $(HDRS) Makefile
speller.o dictionary.o: dictionary.h Makefile
    $(CC) $(CFLAGS)

There are actually two different kinds of rules here:

  1. When to compile something (e.g. speller: speller.o dictionary.o dictionary.h Makefile).
  2. How to compile the thing (e.g. $(CC) $(CFLAGS) -o $@ speller.o dictionary.o).

Your intuition is correct, in that make recompiles the .c files when the .o files are out of date. It's basically checking the timestamps, and if the .c file is newer than the corresponding .o file then make recompiles.

The reason there is no direct correlation here is because make has a default behaviour to try to look for the .c file with the same name as the output file.

Quick detour, recall earlier in the course where make was executed without the existence of a makefile, e.g:

make mario

Here you're asking make for an executable. make goes and figures out that it needs to find a file called mario.c file. Actually what's happening behind the scenes, is that make sees that it needs to create an executable, and to do that it needs a binary .o file. When make sees that a .o file is needed, it knows to look for a .c source file. The .c file is compiled to a .o, which is then linked to create the final executable.

One way is to think of it as a chain of dependencies, that make fills in by itself, unless you tell it otherwise:

mario -> mario.o -> mario.c

Back to the makefile. When you run make from the command line, it will first look in the makefile for something to do. In this case it finds speller and tries to compile it. With the default behaviour, make would just look at speller.c before deciding if anything needs to be done. If we're only modifying dictionary.c then make would not see anything has changed, and would not do anything.

The extra file names (speller.o, dictionary.o, etc) are telling make to look at those files as well, to see if anything has changed:

speller: speller.o dictionary.o dictionary.h Makefile

So if any of these files are newer than the current executable, then make knows to recompile using the line below that condition:

$(CC) $(CFLAGS) -o $@ speller.o dictionary.o

Without this line, make will just follow the default behaviour and compile speller.o only with the default options.

The "dependencies" line is similar:

speller.o dictionary.o: dictionary.h Makefile

This is telling make that speller.o and dictionary.o should be recompiled if dictionary.h or Makefile has changes.

The reason the Makefile itself is included in the list, is to ensure that make checks the Makefile for changes. This is to cater for the scenario where the Makefile changes, but not any of the source or binary files.

The next line below that doesn't specify any compiler commands, so make will use the default behaviour to compile those files. The command it creates would look something like this:

$(CC) $(CFLAGS) 

Since that is the command we want anyway it can be left out. CC and CFLAGS are both "default" variables in make. In other words, just by defining the CFLAGS variable set in the file, make will pass those arguments to the compiler.