Writing Makefile for C++ Projects
Setting variables
Let’s start by setting some basic variables:
# The program name:
NAME = program
# The compiler
CXX = c++Considerations
Note: The variable
CXXis used to specify the C++ compiler. The commandc++is analogous to usingccfor compiling C programs. This is similar to howg++/gccare used for GNU Compilers, andclang/clang++for Clang compilers.
Flags
Here, we are setting the compilation flags used in École 42’s C++ modules.
More specifically:
-Wall: Enable all compiler’s warning messages-Wextra: Enable extra warning messages that are not turned on by-Wall-Werror: Turn all warning messages into errors-std=c++98: Specify the C++ version to conform
CXXFLAGS = -Wall -Wextra -Werror -std=c++98Specifying the source files
In the SRCS variable we can specify all the .cpp files used in the project. These are the files that are going to be compiled into object files (.o) and then combined into an executable file.
SRCS = main.cpp
OBJS = $(SRCS:.cpp=.o)
INCLUDES = main.hppNotes on OBJS
The
OBJSvariable is a special computed variable that takes all the values from theSRCSvariable that has the.cppextension and turns into a.ofile.
Defining some basic rules
When writing a Makefile we usually define 4 basic rules. Those are:
all: Compile the programclean: Clean all object filesfclean: Clean all object files + the executable filere: Clean everything that was generated and generate everything again (fclean+all)
all: $(NAME)
$(NAME): $(OBJS)
$(CXX) $(CXXFLAGS) $(OBJS) -o $(NAME)
clean:
$(RM) $(OBJS)
fclean: clean
$(RM) $(NAME)
re: fclean allLet’s analyze each line here.
all
To compile the program we just have to run make all but as all is the first rule that is getting specified, simply running make will execute make all.
all: $(NAME)As we can see here, all depends on the creation of $(NAME) which is the program name, in other words the executable file. At this point, we don’t have the executable file yet, so make searches for a rule that creates $(NAME):
$(NAME): $(OBJS)
$(CXX) $(CXXFLAGS) $(OBJS) -o $(NAME)The creation of $(NAME) depends on the creation of all object files on this project so they are created. After the creation of $(OBJS) we finally can run $(CXX) (the compiler) specifying $(CXXFLAGS) (our compilation flags) passing all the object files to compile and naming the final product to $(NAME).
To better illustrate this imagine you have 2 C++ files:
main.cpp
other.cppWe want to name our executable file as program:
c++ -Wall -Wextra -Werror -std=c++98 main.o other.o -o programThis is the actual command that gets executed.
Creating object files
This is the rule used to create object files. It basically says: For any C++ file, generate an object file. It also depends on the INCLUDES so if any header file changes, all object files are going to get regenerated. Then it compiles each .cpp file (represented by the automatic variable $< here) to an object file (with the -c flag) and outputs a file with the same name but with the .o extension. Here $@ represents each .o file:
%.o: %.cpp $(INCLUDES)
$(CXX) $(CXXFLAGS) -c $< -o $@Implementing clean
To implement clean, fclean and re is really simple.
clean just removes all object files so:
clean:
$(RM) $(OBJS)fclean calls clean to remove all object files and in addition, also removes the executable file:
fclean: clean
$(RM) $(NAME)re runs fclean and all:
re: fclean all.PHONY?
.PHONY is used to indicate that a target is not a real file but a command or routine to be executed. For example, running make clean executes the clean routine, even though there is no file named clean.
.PHONY: all clean fclean reThis sums up the creation of a basic Makefile for some basic C++ projects.