2

Could someone help provide clarity in the matter of the Makefile used for pset 6? I understand everything up to line 32, that is where it gets confusing. Here the dependencies for the program $(EXE) is a bunch of object files (.o) and the Makefile itself, but shouldn't the dependencies be the .c source files? Does Make look at the object files and then the .c files and determine if the .c files have been modified later than the .o files (inducing a recompilation of those .c files)? I am used to listing .c files when running clang.

#
# Makefile
#
# Computer Science 50
# Problem Set 6
#


# compiler to use
CC = clang

# flags to pass compiler
CFLAGS = -ggdb3 -O0 -Qunused-arguments -std=c99 -Wall -Werror

# name for executable
EXE = speller

# space-separated list of header files
HDRS = dictionary.h

# space-separated list of libraries, if any,
# each of which should be prefixed with -l
LIBS =

# space-separated list of source files
SRCS = speller.c dictionary.c

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


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

# dependencies 
$(OBJS): $(HDRS) Makefile

# housekeeping
clean:
    rm -f core $(EXE) *.o

1 Answer 1

2

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.

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .