Review of Compiling Multiple File Projects
Introduction to "make" and Makefile(s)

Note: This content is adapted from CS330 Lab: Compiling and Debugging

Review of Compiling Multiple File Projects

Before we get into a discussion of make, it would be good to review how to compile projects on Linux. Given three files: main.cpp, greet.cpp, and greet.h, we must undergo two steps to create the executable file "demo".

  1. First create the object files using the -c option (for each .cpp file)
  2. Then link the object files into an executable file
Our commands will look something like this on Linux:
A044872[102]% g++ -c greet.cpp
A044872[103]% g++ -c main.cpp
A044872[104]% g++ -o demo greet.o main.o
A044872[105]% demo
Hello, World

Note that after issuing the g++ -c greet.cpp command, the object file greet.o will be produced.

Introduction to "make" and Makefile(s)

Note: some content on make is copied from the 170 lab

What is make?

The idea behind make is that it simplifies the compilation of projects with multiple files. Consider the above example (with two .cpp files). Compared to a single file project, you must issue two additional commands. Now, think about what happens when your project consists of several more files. You will waste a lot of time typing.

This is where make comes in handy. It automates compilation. It checks which files have been modified and based on dependencies (or rules) determines which object files will need to be recompiled. In addition, it saves time by compiling only files that have changed since the last build.

make is a UNIX command that looks for a file called Makefile or makefile. (Makefile is typically preferred because it appears at the beginning of the directory listing). Within the Makefile, there are variables and things called dependencies. A simple make file for the project that we discussed above might look like this:

The red text highlights the explanations.

# leads comments in a line 
# Build all: default target
all : demo

# Separate compilation to build object files
main.o : main.cpp greet.h 
	g++ -c -ggdb main.cpp

greet.o : greet.cpp greet.h 
	g++ -c -ggdb greet.cpp

# Linking
#demo is a target which depends upon main.o and greet.o 
#"g++ main.o greet.o -o demo" is the command to produce the executable file
#You need to use a TAB before g++ 
demo : main.o greet.o
	g++ main.o greet.o -o demo

# Testing
check : all

# Clean up all build targets so that one may get a clean build
clean :
	rm -f *.o demo

Information on how to use the make command:

	main.cpp is the main program
	greet.cpp and greet.h contain the helper function
	Makefile contains the build script
		"make all" or simply "make" to build everything
		"make clean" to erase all the files built by make
		"make clean all" to get a clean build
		"make check" to run the "demo"

Adding Variables to Makefile(s)

Makefile(s) may also contain variables. For instance, if you will be adding additional object files or changing the compiler, it will be easier to use variables and make modifications only in one place in the file. Variables are set using the equal sign as in

Note that by long standing convention, the variable CC is used to define the C compiler to be used. The variable CXX was added to this convention to define the C++ compiler.

To use the variable, it is prefixed by a $ and surrounded by parenthesis as in: $(CXX). A Makefile with variables might look something like the following:

# this is a comment
# specify the object files ...
OBJ= global.o access.o mem.o rungoal.o sup.o unify.o wexhdr.o

# specify the compiler
CC   = cc   # this is the cross platform standard C compiler
CXX  = g++  # this is the GNU C++ compiler
#CXX = CC   # Solaris C++ compiler

# specify the compiler options

# specify compiler preprocessor options
CPPFLAGS = -I/usr/local/include

# specify linker options
LDFLAGS  = -L/usr/local/lib

# specify the name of the ultimate executable file
EXEC = runwex

# create the executable
$(EXEC): $(OBJ)
	$(CXX) $(LDFLAGS) -o $(EXEC) $(OBJ)
	@echo 'runwex has been created'

access.o: access.cpp global.cpp globdefs.h allwexhdr.h
	$(CXX) $(CPPFLAGS) $(CFLAGS) -c access.cpp
global.o: global.cpp global.h globdefs.h
	$(CXX) $(CPPFLAGS) $(CFLAGS) -c global.cpp
mem.o: mem.cpp
	$(CXX) $(CPPFLAGS) $(CFLAGS) -c mem.cpp
rungoal.o:rungoal.cpp rungoal.h global.h sup.h
	$(CXX) $(CPPFLAGS) $(CFLAGS) -c rungoal.cpp
sup.o: sup.cpp sup.h
	$(CXX) $(CPPFLAGS) $(CFLAGS) -c sup.cpp
unify.o: unify.cpp globdefs.h
	$(CXX) $(CPPFLAGS) $(CFLAGS) -c unify.cpp
wexhdr.o: wexhdr.cpp
	$(CXX) $(CPPFLAGS) $(CFLAGS) -c wexhdr.cpp

	 -/bin/rm -f $(EXEC) $(OBJ)

Some comments on this Makefile:

You may have noticed the "@echo" command in the makefile. You can use this to direct, for example, informational or status messages to the console.

Some comments about make:

Some of the comments on this page are specific to the GNU make. The Linux systems only use the GNU version, which can be accessed using either the make command or the gmake command.

Command Line Options & Usage for make

Like most UNIX commands, make has a number of command line options. A few of them are as follows:

You can try them out if you like!