How to Make a Simple C++ Makefile

How to make a SIMPLE C++ Makefile

Since this is for Unix, the executables don't have any extensions.

One thing to note is that root-config is a utility which provides the right compilation and linking flags; and the right libraries for building applications against root. That's just a detail related to the original audience for this document.

Make Me Baby

or You Never Forget The First Time You Got Made

An introductory discussion of make, and how to write a simple makefile

What is Make? And Why Should I Care?

The tool called Make is a build dependency manager. That is, it takes care of knowing what commands need to be executed in what order to take your software project from a collection of source files, object files, libraries, headers, etc., etc.---some of which may have changed recently---and turning them into a correct up-to-date version of the program.

Actually, you can use Make for other things too, but I'm not going to talk about that.

A Trivial Makefile

Suppose that you have a directory containing: tool tool.cc tool.o support.cc support.hh, and support.o which depend on root and are supposed to be compiled into a program called tool, and suppose that you've been hacking on the source files (which means the existing tool is now out of date) and want to compile the program.

To do this yourself you could

  1. Check if either support.cc or support.hh is newer than support.o, and if so run a command like

    g++ -g -c -pthread -I/sw/include/root support.cc
  2. Check if either support.hh or tool.cc are newer than tool.o, and if so run a command like

    g++ -g  -c -pthread -I/sw/include/root tool.cc
  3. Check if tool.o is newer than tool, and if so run a command like

    g++ -g tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
    -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
    -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl

Phew! What a hassle! There is a lot to remember and several chances to make mistakes. (BTW-- the particulars of the command lines exhibited here depend on our software environment. These ones work on my computer.)

Of course, you could just run all three commands every time. That would work, but it doesn't scale well to a substantial piece of software (like DOGS which takes more than 15 minutes to compile from the ground up on my MacBook).

Instead you could write a file called makefile like this:

tool: tool.o support.o
g++ -g -o tool tool.o support.o -L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz -Wl,-framework,CoreServices \
-Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root -lm -ldl

tool.o: tool.cc support.hh
g++ -g -c -pthread -I/sw/include/root tool.cc

support.o: support.hh support.cc
g++ -g -c -pthread -I/sw/include/root support.cc

and just type make at the command line. Which will perform the three steps shown above automatically.

The unindented lines here have the form "target: dependencies" and tell Make that the associated commands (indented lines) should be run if any of the dependencies are newer than the target. That is, the dependency lines describe the logic of what needs to be rebuilt to accommodate changes in various files. If support.cc changes that means that support.o must be rebuilt, but tool.o can be left alone. When support.o changes tool must be rebuilt.

The commands associated with each dependency line are set off with a tab (see below) should modify the target (or at least touch it to update the modification time).

Variables, Built In Rules, and Other Goodies

At this point, our makefile is simply remembering the work that needs doing, but we still had to figure out and type each and every needed command in its entirety. It does not have to be that way: Make is a powerful language with variables, text manipulation functions, and a whole slew of built-in rules which can make this much easier for us.

Make Variables

The syntax for accessing a make variable is $(VAR).

The syntax for assigning to a Make variable is: VAR = A text value of some kind
(or VAR := A different text value but ignore this for the moment).

You can use variables in rules like this improved version of our makefile:

CPPFLAGS=-g -pthread -I/sw/include/root
LDFLAGS=-g
LDLIBS=-L/sw/lib/root -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint \
-lPostscript -lMatrix -lPhysics -lMathCore -lThread -lz -L/sw/lib -lfreetype -lz \
-Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -pthread -Wl,-rpath,/sw/lib/root \
-lm -ldl

tool: tool.o support.o
g++ $(LDFLAGS) -o tool tool.o support.o $(LDLIBS)

tool.o: tool.cc support.hh
g++ $(CPPFLAGS) -c tool.cc

support.o: support.hh support.cc
g++ $(CPPFLAGS) -c support.cc

which is a little more readable, but still requires a lot of typing

Make Functions

GNU make supports a variety of functions for accessing information from the filesystem or other commands on the system. In this case we are interested in $(shell ...) which expands to the output of the argument(s), and $(subst opat,npat,text) which replaces all instances of opat with npat in text.

Taking advantage of this gives us:

CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))

tool: $(OBJS)
g++ $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)

tool.o: tool.cc support.hh
g++ $(CPPFLAGS) -c tool.cc

support.o: support.hh support.cc
g++ $(CPPFLAGS) -c support.cc

which is easier to type and much more readable.

Notice that

  1. We are still stating explicitly the dependencies for each object file and the final executable
  2. We've had to explicitly type the compilation rule for both source files

Implicit and Pattern Rules

We would generally expect that all C++ source files should be treated the same way, and Make provides three ways to state this:

  1. suffix rules (considered obsolete in GNU make, but kept for backwards compatibility)
  2. implicit rules
  3. pattern rules

Implicit rules are built in, and a few will be discussed below. Pattern rules are specified in a form like

%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<

which means that object files are generated from C source files by running the command shown, where the "automatic" variable $< expands to the name of the first dependency.

Built-in Rules

Make has a whole host of built-in rules that mean that very often, a project can be compile by a very simple makefile, indeed.

The GNU make built in rule for C source files is the one exhibited above. Similarly we create object files from C++ source files with a rule like $(CXX) -c $(CPPFLAGS) $(CFLAGS).

Single object files are linked using $(LD) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS), but this won't work in our case, because we want to link multiple object files.

Variables Used By Built-in Rules

The built-in rules use a set of standard variables that allow you to specify local environment information (like where to find the ROOT include files) without re-writing all the rules. The ones most likely to be interesting to us are:

  • CC -- the C compiler to use
  • CXX -- the C++ compiler to use
  • LD -- the linker to use
  • CFLAGS -- compilation flag for C source files
  • CXXFLAGS -- compilation flags for C++ source files
  • CPPFLAGS -- flags for the c-preprocessor (typically include file paths and symbols defined on the command line), used by C and C++
  • LDFLAGS -- linker flags
  • LDLIBS -- libraries to link

A Basic Makefile

By taking advantage of the built-in rules we can simplify our makefile to:

CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))

all: tool

tool: $(OBJS)
$(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)

tool.o: tool.cc support.hh

support.o: support.hh support.cc

clean:
$(RM) $(OBJS)

distclean: clean
$(RM) tool

We have also added several standard targets that perform special actions (like cleaning up the source directory).

Note that when make is invoked without an argument, it uses the first target found in the file (in this case all), but you can also name the target to get which is what makes make clean remove the object files in this case.

We still have all the dependencies hard-coded.

Some Mysterious Improvements

CC=gcc
CXX=g++
RM=rm -f
CPPFLAGS=-g $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)

SRCS=tool.cc support.cc
OBJS=$(subst .cc,.o,$(SRCS))

all: tool

tool: $(OBJS)
$(CXX) $(LDFLAGS) -o tool $(OBJS) $(LDLIBS)

depend: .depend

.depend: $(SRCS)
$(RM) ./.depend
$(CXX) $(CPPFLAGS) -MM $^>>./.depend;

clean:
$(RM) $(OBJS)

distclean: clean
$(RM) *~ .depend

include .depend

Notice that

  1. There are no longer any dependency lines for the source files!?!
  2. There is some strange magic related to .depend and depend
  3. If you do make then ls -A you see a file named .depend which contains things that look like make dependency lines

Other Reading

  • GNU make manual
  • Recursive Make Considered Harmful on a common way of writing makefiles that is less than optimal, and how to avoid it.

Know Bugs and Historical Notes

The input language for Make is whitespace sensitive. In particular, the action lines following dependencies must start with a tab. But a series of spaces can look the same (and indeed there are editors that will silently convert tabs to spaces or vice versa), which results in a Make file that looks right and still doesn't work. This was identified as a bug early on, but (the story goes) it was not fixed, because there were already 10 users.

(This was copied from a wiki post I wrote for physics graduate students.)

How do I make a simple makefile for gcc on Linux?

Interesting, I didn't know make would default to using the C compiler given rules regarding source files.

Anyway, a simple solution that demonstrates simple Makefile concepts would be:

HEADERS = program.h headers.h

default: program

program.o: program.c $(HEADERS)
gcc -c program.c -o program.o

program: program.o
gcc program.o -o program

clean:
-rm -f program.o
-rm -f program

(bear in mind that make requires tab instead of space indentation, so be sure to fix that when copying)

However, to support more C files, you'd have to make new rules for each of them. Thus, to improve:

HEADERS = program.h headers.h
OBJECTS = program.o

default: program

%.o: %.c $(HEADERS)
gcc -c $< -o $@

program: $(OBJECTS)
gcc $(OBJECTS) -o $@

clean:
-rm -f $(OBJECTS)
-rm -f program

I tried to make this as simple as possible by omitting variables like $(CC) and $(CFLAGS) that are usually seen in makefiles. If you're interested in figuring that out, I hope I've given you a good start on that.

Here's the Makefile I like to use for C source. Feel free to use it:

TARGET = prog
LIBS = -lm
CC = gcc
CFLAGS = -g -Wall

.PHONY: default all clean

default: $(TARGET)
all: default

OBJECTS = $(patsubst %.c, %.o, $(wildcard *.c))
HEADERS = $(wildcard *.h)

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

.PRECIOUS: $(TARGET) $(OBJECTS)

$(TARGET): $(OBJECTS)
$(CC) $(OBJECTS) -Wall $(LIBS) -o $@

clean:
-rm -f *.o
-rm -f $(TARGET)

It uses the wildcard and patsubst features of the make utility to automatically include .c and .h files in the current directory, meaning when you add new code files to your directory, you won't have to update the Makefile. However, if you want to change the name of the generated executable, libraries, or compiler flags, you can just modify the variables.

In either case, don't use autoconf, please. I'm begging you! :)

how to write a simple makefile for c

Well, makefiles are just kind of special scripts. Every is unique, for such simple task this would be sufficient:

Makefile:

CC=gcc
CFLAGS=-lm -lcrypto
SOURCES=my.c cJ/cJ.c

all: my

my: $(SOURCES)
$(CC) -o my $(SOURCES) $(CFLAGS)

Later you may want to use some other options such as wildcards %.c to compile in multiple files without having to write them in.

Alternatively:

CC=gcc
CFLAGS=-lm -lcrypto

MY_SOURCES = my.c cJ/cJ.c
MY_OBJS = $(patsubst %.c,%.o, $(MY_SOURCES))

all: my

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

my: $(MY_OBJS)
$(CC) $(CFLAGS) $^ -o $@

Note that lines following each target ("my:", ...) must start with tab (\t), not spaces.

Makefile for simple C project

This Makefile should work:

CC = gcc
CPPFLAGS = -Ilib
CFLAGS = -Wall -O2
RM = rm -f
MAINEXE = main
MAINDEP = lib/foo.h
MAINOBJS = main.o lib/foo.o

all : $(MAINEXE)

# Generate output and link object files
$(MAINEXE): $(MAINOBJS)
$(CC) $(MAINOBJS) -o $@

$(MAINOBJS): $(MAINDEP)

clean:
$(RM) $(MAINEXE) $(MAINOBJS) *.out

.PHONY: all clean

How do I write a simple makefile for a C project?

hellomake: hellomake.c hellofunc.c
gcc -o hellomake hellomake.c hellofunc.c -I.

this is the optimal way to make an makeFile.
some cases you need to download even a plug in.
which software are you using?

Simple makefile for C++

I think you have two problems here.

First, the ordering SRCS = summation.cpp average.cpp main.cpp. This causes the .o to be provided to g++ in the order summation.o average.o main.o which causes the undefined error. You want to provide them in the order main.o summation.o average.o, hence SRCS should be SRCS = main.cpp summation.cpp average.cpp.

Second, in the line:

${CXX} ${CXXFLAGS} ${OBJS} -o ${MAIN} ${OBJS}

The second ${OBJS} is not needed.

Try the following:

CXX = g++
CXXFLAGS = -std=c++0x -Wall -pedantic-errors -g

SRCS = main.cpp summation.cpp average.cpp
OBJS = ${SRCS:.cpp=.o}
HEADERS = summation.h average.h

MAIN = myprog

all: ${MAIN}
@echo Simple compilter named myprog has been compiled

${MAIN}: ${OBJS}
${CXX} ${CXXFLAGS} ${OBJS} -o ${MAIN}

.cpp.o:
${CXX} ${CXXFLAGS} -c $< -o $@

clean:
${RM} ${PROGS} ${OBJS} *.o *~.

Creating a makefile - (C)

Rules in a make file are of the form:

target: dependency1 dependency2 etc
command to build target

target is the name of the file you want to build. So the line

program: client.o
gcc client.o -o Client

Is trying to build a file called program. However, the command does not create a file called program, it creates a file called Client. This is less of a problem than you might think, it just means that the rule is always executed whether Client is up to date or not. However, you should change it so the target is the file you are building.

Client: client.o
gcc client.o -o Client

By the way, in most *nixes, file names are case sensitive Client and client are different files on Linux, for example.

That rule has a single dependency: client.o. Unfortunately, your make file does not know how to build client.o - there is no target called client.o.

I am speculating the cause of your error is that you have an old client.o hanging about that doesn't have a main() function in it. This is why the link (the gcc command in the program target) is failing.

The target program.o has the same problem as the target program. You are not building program.o, you are building client.o. This target needs to be changed to

client.o: client.c $(HEADERS)
gcc -c client.c -o client.o

which is happily the dependency for your Client target.

Note The indentation for the command part of a make rule has to be done with a tab. If copy-pasting my answer or any of the other answers, or the answers in the linked question, please make sure your indents are tabs, not spaces.

Update (the issue with test() being an undefined reference)

If you have a function in guiBuilder.c that has a prototype in guiBuilder.h you'll need to compile guiBuilder.c and add it to the link phase.

Your rule for guiBuilder.o will look very similar to the rule for client.o

guiBuilder.o: guiBuilder.c $(HEADERS)
gcc -c guiBuilder.c -o guiBuilder.o

Then you need to add guiBuilder.o as a dependency of Client

Client: client.o guiBuilder.o
gcc client.o guiBuilder.o -o Client

You may have noticed that you now have two rules for creating .o files that are identical other than the names of the source and object files. The accepted answer to the question that you linked shows how you modify the make file so you only need to define the rule once.

Set up my makefile to compile C with just make

The default goal is the first goal, so switch the order of these so that all is the default.

There's more detail in the documentation on goals:

By default, the goal is the first target in the makefile (not counting targets that start with a period). Therefore, makefiles are usually written so that the first target is for compiling the entire program or programs they describe. If the first rule in the makefile has several targets, only the first target in the rule becomes the default goal, not the whole list. You can manage the selection of the default goal from within your makefile using the .DEFAULT_GOAL variable


Related Topics



Leave a reply



Submit