Why Use a Makefile?
Usual compilation with g++ will involve a command as follows.
g++ -o program main.cc
The command will compile each C++ source file and create object files. If you have additional include files,
-I flag can be used to specify the include directory containing .h / .hpp header files.
-L flag can be used to specify additional library locations and you will have to use
-l flag to specify which library needs to be linked (eg:-
However, when you have a complicated compilation process, retyping the compile command becomes cumbersome and it also recompiles all your object binaries (even the source files you did not modify since the last compilation). In contrast, a Makefile only compiles the source files that have been modified since the last compile time saving compute time.
Make sure to align your Makefile using tabs.
In this tutorial, I will provide a step by step guide to build a fully equipped Makefile.
Here’s example 1 as a copiable block 😉. Example 1 demonstrates how the compile and link command can be in one step using g++.
CC=g++ CFLAGS=-std=c++11 main: $(CC) -o program main.cc $(CFLAGS)
But more often than not, you will find that you need to have multiple rules to compile a program binary and a test binary. Example 2 explains how you can use multiple rules.
CC=g++ CFLAGS=-std=c++11 main: $(CC) -o program main.cc $(CFLAGS) test: $(CC) -o program test.cc $(CFLAGS)
Makefile with Step Compilation
In this example, we will see how we can separate out compilation steps for each object file. In figure 2, when we run the command
make, the main rule is executed (Step 1 in Figure 2). Make meets with the need to build
functions.o. Thus, scans through the rules to figure out whether there is a matching rule. When met with
functions.o rule, the second step begins (Step 2 in Figure 2).
In Step 2, Make replaces the symbols appropriately as shown.
$< is replaced with the right hand side (this is to your right) variable name, and
$@ is replaced by the left hand side.
After the variable replacement, Make creates the object file by compiling using g++. Make, moves to the first rule (“main”) to perform the final compile and link step. Again
$^ is replaced with the first element on the right hand side of the colon. After replacing, Make performs the link step and generates the binary.
CC=g++ CFLAGS=-std=c++11 all: main functions.o: functions.c $(CC) -c -o $@ $< -I functions.h $(CFLAGS) main: functions.o $(CC) -o program $^ main.cc $(CFLAGS)
In Figure 3, we can see a Makefile which includes compiling binary files in a separate object directory because when you have many source files, compiling all object files to the main build directory is not the best idea.
Make executes the $(TARGET) rule in step 1 and as explained earlier, it moves to figure out what $(OBJ) means, in example 4. In step 2, a pattern substitution is made using
patsubst, replacing to create the concatenated file name
bin/functions.o. This specifies the output object file name as well as the directory association.
In step 3, Make performs actual compilation having resolved all directory names and moves to $(TARGET) rule to final linking.
CC=g++ CFLAGS=-std=c++11 TARGET=program INCLUDE_DIR=include ODIR=bin all: $(TARGET) OBJ_DEPS = functions.o OBJ = $(patsubst %,$(ODIR)/%, $(OBJ_DEPS)) $(ODIR)/%.o: %c $(CC) -c -o $@ $< -I $(INCLUDE_DIR) $(CFLAGS) $(TARGET): $(OBJ) $(CC) -c -o $(TARGET) $^ main.cc $(CFLAGS) .PHONY clean clean: rm -rf $(ODIR)/*.o *~ $(TARGET)
Let me know if you have further questions. This article is part of a Computer Architecture collection. If you would like to read more about Computer Architecture, please use the tag filter and
Here’s a summary created for SEO optimizations.
Time needed: 15 minutes.
How to create a Makefile
- Add the Main Build Step as Follows
- Separate out Build Steps
Separate out the compile steps to have more control as shown in Figure 2.
- Run the Make Command
All compile steps are now integrated.