theDevBrent

Creating a simple make file

If you need to get a small C++ project up and running quickly there’s nothing easier than writing a quick Make file to get started with building your project. We will get a small project up and running with a makefile that can grow with your project.

I will be using a small project that includes SDL to show how external libraries can be linked as well.

Obviously, at some point, projects can become monsters and need to be tamed with more sophisticated systems. However, this will keep us going without too much hassle.

TLDR; If you just want to get started writing code. A GitHub template can be found here.

Getting setup

To get started let us create a simple folder structure to house our project.

mkdir make-project-template
cd make-project-template
mkdir src
mkdir libs

Now we can create our Makefile

build:
    g++ -Wall -Wfatal-errors -I"./libs/" ./src/*.cpp -o main

This is enough to let us compile a simple program that lives in the “/src” directory.

Run:

make

This will build the project.

Let’s extend this a bit to make it easier for us to run the project. Add the below code to your Makefile.

....

run:
    ./main

clean:
    rm main

Adding that to our Makefile allows us to run make run and make clean to run our project and delete the binary.

Now we have a very basic Makefile that does everything we need for now.

Adding Variables

To make the Makefile a little easier to maintain let’s add a few variables to allow us to customize it easier

# Compiler
COMPILER = g++

# Language Standard
LANG_STD = -std=c++17

# Compiler Flags
COMPILER_FLAGS = -Wall -Wfatal-errors

# Include Paths
INCLUDE_PATH = -I"./libs"

# Source Folders
SRC_FOLDERS = ./src/*.cpp

# Project Name
PROJECT_NAME = main

After adding these variables to the top of the Makefile we can change the build command:

build:
    $(COMPILER) $(COMPILER_FLAGS) $(LANG_STD) $(INCLUDE_PATH) $(SRC_FOLDERS) -o $(PROJECT_NAME)

After making those changes running make should result in the same behavior.

By adding the variables it leaves the run and clean commands sort of broken. Let’s go ahead and fix this now.

run:
    ./$(PROJECT_NAME)

clean:
    rm $(PROJECT_NAME)

This is enough to have our simple program work and easily build.

Adding Libraries

Now to add some external libraries. This is very straightforward now that we have made it easier to maintain our Makefile. Let’s add a new variable to our Makefile.

...
LINKER_FLAGS = -lSDL2
...

Now add the variable to our build command.

build:
    $(COMPILER) $(COMPILER_FLAGS) $(LANG_STD) $(INCLUDE_PATH) $(SRC_FOLDERS) $(LINKER_FLAGS) -o $(PROJECT_NAME)

This will link the SDL library when we build our project.

Extending the makefile

If you are unlucky enough to be working on a project on a Mac you are in luck. Let’s set this Makefile up to make it work with your Mac and Homebrew.

First, let’s add a couple of new variables to our Makefile:

BREW_INCLUDE_PATH = -I"/opt/homebrew/include/"
BREW_LIB_PATH = -L"/opt/homebrew/lib/"

## Gets the OS name
UNAME := $(shell uname)

With the above variables, we can now make our Makefile a little more dynamic. Now make the below updates to the build command.

build:
    ifeq ($(UNAME), Darwin)
        $(CC) $(COMPILER_FLAGS) $(LANG_STD) $(INCLUDE_PATH) $(BREW_INCLUDE_PATH) $(SRC_FOLDERS) $(BREW_LIB_PATH) $(LINKER_FLAGS) -o $(PROJECT_NAME)
    else
        $(CC) $(COMPILER_FLAGS) $(LANG_STD) $(INCLUDE_PATH) $(SRC_FOLDERS) $(LINKER_FLAGS) -o $(PROJECT_NAME)
    endif

This will include all of the Homebrew paths that we need on Mac.

Adding more source files

Another problem we will have to solve is adding more source files to the project. This is very straightforward since we have set up our Makefile in an extendable way. To add more source folders just change your SRC_FOLDERS variable like the below:

# Source Folders
SRC_FOLDERS =   ./src/*.cpp \
                ./src/more/*.cpp \
                ./src/anotherDir/*.cpp        

The final Makefile

# Compiler
CC = g++

# Language Standard
LANG_STD = -std=c++17

# Compiler flags
COMPILER_FLAGS = -Wall -Wfatal-errors 

# Include Paths
INCLUDE_PATH = -I"./libs/" -I"/opt/homebrew/include/"

# Homebrew include and lib paths
BREW_INCLUDE_PATH = -I"/opt/homebrew/include/"
BREW_LIB_PATH = -L"/opt/homebrew/lib/"

# Source folders
SRC_FOLDERS =     ./src/*.cpp

# Linker Flags
LINKER_FLAGS = -lSDL2 -lSDL2_image -lSDL2_ttf -lSDL2_mixer -llua5.4

# Project Name
PROJECT_NAME = main

UNAME := $(shell uname)

build:
ifeq ($(UNAME), Darwin)
    $(CC) $(COMPILER_FLAGS) $(LANG_STD) $(INCLUDE_PATH) $(BREW_INCLUDE_PATH) $(SRC_FOLDERS) $(BREW_LIB_PATH) $(LINKER_FLAGS) -o $(PROJECT_NAME)
else
    $(CC) $(COMPILER_FLAGS) $(LANG_STD) $(INCLUDE_PATH) $(SRC_FOLDERS) $(LINKER_FLAGS) -o $(PROJECT_NAME)
endif

run:
    ./$(PROJECT_NAME)

clean:
    rm $(PROJECT_NAME)

Proudly powered by WordPress