How to Use Makefile in C++

Jinku Hu Mar 12, 2025 C++ C++ Makefile
  1. What is a Makefile?
  2. Creating a Simple Makefile
  3. Adding Multiple Source Files
  4. Managing Dependencies
  5. Conclusion
  6. FAQ
How to Use Makefile in C++

Makefiles are an essential tool for C++ developers, streamlining the build process and managing dependencies efficiently. If you’re working on a C++ project, understanding how to leverage Makefiles can significantly enhance your productivity.

This article will guide you through the fundamentals of using Makefiles in C++, covering everything from basic structure to advanced features. Whether you’re compiling a simple program or a complex application, you’ll discover how Makefiles can simplify your workflow and minimize manual errors. Let’s dive in!

What is a Makefile?

A Makefile is a special file that contains a set of directives used with the make build automation tool. It defines how to compile and link a program. The beauty of a Makefile lies in its ability to automate the build process, allowing developers to focus on writing code rather than managing compilation commands.

The basic structure of a Makefile consists of rules, which include targets, prerequisites, and commands. Targets are typically the files you want to create, prerequisites are the files that the target depends on, and commands are the actions needed to create the target.

Creating a Simple Makefile

To create a simple Makefile for a C++ project, follow these steps. Let’s assume you have a C++ source file named main.cpp that you want to compile into an executable named app.

First, create a file named Makefile in your project directory and add the following content:

CC = g++
CFLAGS = -Wall -g
TARGET = app
SOURCES = main.cpp

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

In this Makefile:

  • CC specifies the compiler to use, which is g++ for C++.
  • CFLAGS includes compiler flags; -Wall enables all warnings, and -g adds debugging information.
  • TARGET is the name of the output executable.
  • SOURCES lists the source files that need to be compiled.

To build your application, simply run the following command in your terminal:

make

Output:

g++ -Wall -g -o app main.cpp

This command triggers the Makefile, compiling main.cpp into an executable named app. If you modify main.cpp, running make again will only recompile the changed files, saving time and resources.

Adding Multiple Source Files

As your project grows, you may have multiple source files. Let’s modify the Makefile to handle additional files. Suppose you have two more source files, utils.cpp and utils.h. Update your Makefile as follows:

CC = g++
CFLAGS = -Wall -g
TARGET = app
SOURCES = main.cpp utils.cpp
OBJECTS = $(SOURCES:.cpp=.o)

$(TARGET): $(OBJECTS)
	$(CC) $(CFLAGS) -o $(TARGET) $(OBJECTS)

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

Here’s what’s new:

  • OBJECTS defines an object file for each source file.
  • The rule %.o: %.cpp tells make how to compile .cpp files into .o object files.

Now, when you run make, it will compile each source file into an object file before linking them together into the final executable.

Output:

g++ -Wall -g -c main.cpp
g++ -Wall -g -c utils.cpp
g++ -Wall -g -o app main.o utils.o

This approach not only makes the build process more efficient but also allows for better organization of your codebase. You can easily add new source files by just updating the SOURCES variable.

Managing Dependencies

One of the key advantages of using a Makefile is its ability to manage dependencies automatically. If your code relies on header files, you want to ensure that changes to those headers trigger recompilation of the affected source files. You can enhance your Makefile to include dependency tracking as follows:

CC = g++
CFLAGS = -Wall -g
TARGET = app
SOURCES = main.cpp utils.cpp
OBJECTS = $(SOURCES:.cpp=.o)

$(TARGET): $(OBJECTS)
	$(CC) $(CFLAGS) -o $(TARGET) $(OBJECTS)

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

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

In this enhanced Makefile, we’ve added a clean target. This target allows you to remove all object files and the final executable with one command:

make clean

Output:

rm -f main.o utils.o app

This is particularly useful for ensuring a fresh build environment. By managing dependencies effectively, you minimize the risk of compiling outdated code, leading to fewer bugs and a more stable application.

Conclusion

Using a Makefile in your C++ projects can greatly streamline the build process, manage dependencies, and improve overall efficiency. By understanding the basic structure of a Makefile and how to adapt it for various project needs, you can focus more on coding and less on compilation. Whether you’re working on a small project or a large application, Makefiles are an invaluable asset in the development toolkit. Start implementing Makefiles in your projects today and enjoy a smoother coding experience!

FAQ

  1. What is a Makefile?
    A Makefile is a file containing a set of directives used by the make build automation tool to compile and link programs.

  2. How do I create a Makefile for my C++ project?
    You can create a Makefile by defining targets, prerequisites, and commands for compiling your source files into an executable.

  3. Can I use Makefiles for large projects?
    Yes, Makefiles are particularly useful for large projects as they can manage multiple source files and dependencies efficiently.

  4. What command do I use to build my project with a Makefile?
    You simply run the command make in your terminal to build the project defined in your Makefile.

  5. How can I clean up my project using a Makefile?
    You can add a clean target in your Makefile that removes object files and executables, allowing you to start fresh.

Enjoying our tutorials? Subscribe to DelftStack on YouTube to support us in creating more high-quality video guides. Subscribe
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook