A while back I made a makefile which could compile virtually any simple project in c++. Recently, this need appeared again so I created the makefile again.
This source code is released into the public domain. Basically, you may use it in any way you like, but read the unlicense notice at the beginning of the file for more precise information.
Requirements
The project should have this makefile above src
folder, and it creates a build
folder for intermediate compilation files and exe
folder for the final executables.
It uses grep
, g++
, mkdir
, and dirname
which should be accessible on every unix system.
How it works
Rough idea is to identify executables by searching for int main
.
Other files are compiled into object files in ./build/
folder.
Next, we compile the executables into the ./exe/
folder (quite ineffectively linking all the object files).
As a sideproduct when building we create dependency definition files in the ./build/
folder which have information on mutual dependencies of the files – all of these are included as rules at the end of the makefile, so only files which are affected by a change are recompiled.
That’s it.
Use make
to build all, or make exe/a
to compile only the executable created from a.cpp
.
MAKEFLAGS += --no-builtin-rules
MAKEFLAGS += --no-builtin-variables
CC = g++
LOADLIBES = -lm
CXXFLAGS = --std=c++17 -Wall -pedantic -g -O5 -ffast-math
DEPFLAGS = -MT $@ -MMD -MP -MF ./build/$*.d
MAIN_FILES = $(shell grep -lr 'int main' './src/' | grep 'cpp$$')
OTHER_FILES = $(shell grep -Lr 'int main' './src/' | grep 'cpp$$')
SOURCE_FILES = $(OTHER_FILES) $(MAIN_FILES)
EXE_FILES = $(MAIN_FILES:./src/%.cpp=./exe/%)
OBJECT_FILES = $(OTHER_FILES:./src/%.cpp=./build/%.o)
HEADER_FILES = $(OTHER_FILES:.cpp=.hpp)
DEPENDENCIES = $(SOURCE_FILES:./src/%.cpp=./build/%.d)
compile: $(EXE_FILES)
./build/%.o: ./src/%.cpp ./build/%.d
@mkdir -p `dirname $@`
$(CC) $(CXXFLAGS) $(DEPFLAGS) -c -o $@ $<
$(EXE_FILES): ./exe/%: ./src/%.cpp $(OBJECT_FILES)
@mkdir -p `dirname $@`
$(CC) $(CXXFLAGS) -o $@ $< $(OBJECT_FILES)
.PHONY: clean
clean:
@echo "don't change this rule, as it may be dangerous if small bugs are introduced"
rm -rf build/ exe/
$(DEPENDENCIES):
include $(wildcard $(DEPENDENCIES))
Code explanation
As the make
tool has by default many rules it behaves quite unpredicably, if we don’t know the rules by heart.
First two lines disable all builtin rules.
MAKEFLAGS += --no-builtin-rules
MAKEFLAGS += --no-builtin-variables
Next, we define the compiler in CC
and some compilation flags in CXXFLAGS
; also DEPFLAGS
is setup to guide creation of dependency.
CC = g++
LOADLIBES = -lm
CXXFLAGS = --std=c++17 -Wall -pedantic -g -O5 -ffast-math
DEPFLAGS = -MT $@ -MMD -MP -MF ./build/$*.d
Find source files which are executalbe and ones which are not.
MAIN_FILES = $(shell grep -lr 'int main' './src/' | grep 'cpp$$')
OTHER_FILES = $(shell grep -Lr 'int main' './src/' | grep 'cpp$$')
Rename the source files to get filenames for necessary object, executable, and dependency files.
SOURCE_FILES = $(OTHER_FILES) $(MAIN_FILES)
EXE_FILES = $(MAIN_FILES:./src/%.cpp=./exe/%)
OBJECT_FILES = $(OTHER_FILES:./src/%.cpp=./build/%.o)
HEADER_FILES = $(OTHER_FILES:.cpp=.hpp)
DEPENDENCIES = $(SOURCE_FILES:./src/%.cpp=./build/%.d)
Define the main goal when make
is run – compile everything.
compile: $(EXE_FILES)
To get the object files, we need the .cpp
source and its dependencies .d
to be up-to-date; if those changed, then recompile it.
./build/%.o: ./src/%.cpp ./build/%.d
@mkdir -p `dirname $@`
$(CC) $(CXXFLAGS) $(DEPFLAGS) -c -o $@ $<
To get the executable we need its .cpp
source and also all object files ready.
Then, again, build it.
$(EXE_FILES): ./exe/%: ./src/%.cpp $(OBJECT_FILES)
@mkdir -p `dirname $@`
$(CC) $(CXXFLAGS) -o $@ $< $(OBJECT_FILES)
To clean, we simply remove the only two folders which got poluted.
clean:
rm -rf build/ exe/
Finally, as sources files are dependent on each other we include those dependencies into this makefile as rules.
These are created thanks to the -MMD
flag of g++
as a sideproduct of .o
compilation.
$(DEPENDENCIES):
include $(wildcard $(DEPENDENCIES))