Download - make: the good, the bad, and the ugly
Outline
What is make, and why do you care? Key concepts
makefiles, targets, directives & variables.
A simple makefile Implicit variables A better makefile Words of caution
What is Make?
“The ‘make’ utility automatically determines which pieces of a large program need to be recompiled, and issues commands to recompile them.”
-- The GNU Make Manual
Why Do I Care?
Let’s say I have a program with only one source file – simple.cc.
Creating the program is easy: g++ <flags> simple.cc –o simple
What if I have 10 source files? g++ <flags> s1.cc … s10.cc –o simple
If I change two of them?
Incremental Compilation
If I know which two I’ve changed, I can recompile them (into objects) and re-link the executable.
Figuring out the minimum number of steps that must be performed is what make is good at.
How Does Make Work?
You tell make what files your program depends on – some that you provide (the source) and some that the compiler provides (objects).
You tell make how to run the compiler, and finally how to assemble all those files into the program.
When things change, make decides what commands need to be run to bring your program up to date.
For the Technically Minded
Make constructs a dependency graph: what targets depend on what other
targets how do I construct a given target
When you invoke Make for a specific target, it visits the graph, performing the needed commands to produce the thing you asked for.
It’s not Just for Programs Anymore
Make isn’t just good for compiling programs – it’s good for figuring out what commands to run to perform some task.
Time permitting, we’ll give non-programming examples of make later.
Key Concepts: Makefile
Make needs instructions on how to decide what to do – by convention, these instructions are stored in a file called “Makefile”. It’s just a convention – the –f flag to
make will tell it to look at a specific file for it’s instructions.
Makefiles (regardless of their filename) have a specific format…
Basic Makefile Format
# comment linevariable-name = valuetarget: dependencies<TAB>directive
The <TAB> is critical – it can’t be spaces, it must be a tab. Emacs has a makefile mode and will warn you if it finds an obvious error.
Make Targets
The first part of a rule – the thing make is trying to make.
Generally (but not always) a file – a .o, for example.
Targets have a (possibly empty) list of dependencies – other targets that this target depends on.
Directives
The final part of a rule – tells make what command(s) to run to create this target.
Generally the compiler for whatever language you’re working in.
Variables
If you don’t know what a variable is, time to review CS5/10.
Make variables look a lot like variables in SH. Assignment: VARNAME=Value Reference: ${VARNAME}, or $
(VARNAME)
Makefile.ex1
# three rules to build simpleall: simplesimple: simple.o<TAB>g++ -W –w –Wall –Werror –pedantic \ -o simple simple.o
simple.o: simple.cc<TAB>g++ -W –w –Wall –Werror –pedantic \ -c -o simple.o simple.cc
Makefile.ex2
CPPFLAGS=-W –w –Wall –Werror –pedanticCXX=g++all: simplesimple: simple.o<TAB>${CXX} –o simple simple.osimple.o: simple.cc<TAB>${CXX} ${CPPFLAGS} –o simple.o
–c \ simple.cc
Implicit Rules & Their Variables
Make has a slew of implicit rules – for example, it “knows” how to turn a .cc file into a .o file without you telling it. To make these implicit rules flexible,
make uses a range of built-in variable names in them.
If you assign to these variables, make will insert those values into the rules.
Some Built In Variables
CC – the C compiler CFLAGS – flags for the C compiler CXX – the C++ compiler CPPFLAGS – any guesses? LD – the linker LDFLAGS – let’s not see the same
hands…
Makefile.ex3
CPPFLAGS=-W –w –Wall –Werror –pedantic
all: simple
Having Make Figure Out Dependencies
Makefile.ex3 only works for single file projects.
Real projects have more than one file, and those files have to be built in a specific order.
Make (with some help from the compiler) can figure out that order for you.
Makefile.ex4
CC=${CXX}SOURCE = simple.cc.depend:${SOURCE}<TAB>@set –e; $(CC) –MM ${CPPFLAGS} \ ${SOURCE} | \ sed ‘s/\($*\)\.o[ :]*/\1.o : /g’ \ > .depend; [ -s $@ ] || rm –f $@-include .depend
Say What?
If you really want to know how/why it works – I encourage you to go figure it out. Read the make manual Read the sed man page See if you can figure out what’s going
on.
Having Make Find The Source
Thanks to some nifty tricks built into (gnu) make – you often don’t even need to tell make what the source files are.
You might think you could do: SOURCE = *.cc
But that’s not what you want. What you want: SOURCE := $(wildcard *.cc)
Why Not SOURCE=*.cc?
The trouble with saying SOURCE = *.cc
Is that make doesn’t evaluate that wildcard during variable assignment, it will expand it when the variable is used in a target or command.
SOURCE := $(wildcard *.cc)
Two Forms of = ??
There are two different assignment operators in Make: = is a “deferred” assignment – it’s
evaluated when make is actually running commands.
:= is an “immediate” assignment – it’s evaluated when make is reading makefiles and deciding what to do.
the distinction is subtle, and can be confusing.
Next Step: Figuring out the Objects
Wouldn’t it be nice if we could tell make – “the list of objects is the list of source files, but replace .cc with .o” ?
We can, like this:
OBJS := $(patsubst %.cc,%.o,$(wildcard *.cc)) Or this:
OBJS = $(SOURCE:.cc=.o)
So What Do We End Up With?
CC=${CXX}CPPFLAGS=-w –W –Wall –Werror –pedanticCPPFLAGS += -gLDFLAGS =SOURCE := $(wildcard *.cc)OBJS := $(patsubst %.cc,%.o,$(wildcard *.cc))TARGET = simpleall : ${TARGET}${TARGET}: ${OBJS}.depend…
So What’s Not To Love?
Makefile syntax is ugly – especially the fact that TABs have to be TABs.
While make has been ported to many operating systems, it’s easy to write Makefiles that aren’t portable. That sed trick, for example, won’t work on
Windows. Many have tried to “fix” make – few of
those efforts have survived.
Learning More
% info make google for “make tutorial”