Minimalistic Build System With Make

Back

07 Dec 2012

Every sensible C++ program, which consists of more than one cpp file, need a build system. In this article I present minimalistic solution for this problem with just two plain makefiles.

Features and limitations:

  • target binary must be executable, shared object or library archive
  • handles headers dependencies with dependency files stored in separate directory
  • no change of makefile is needed after new source file is added to project
  • all source files must be placed in one directory
  • supports only one target binary

The first makefile is responsible for environment variables. Here you can adjust target name and compiler settings through commented variables. This makefile is meant to sit at project root directory.

# name the target binary it might be executable, .so or .a
TARGET=test

# list of dynamic linked libraries
LIBS= -lm

# list of include directories
INCLUDE= -I/usr/local/include

# compiler flags
CFLAGS= -Wall -Wcast-align -Wpointer-arith -Wsign-compare -Wshadow -g -O2
CXXFLAGS= -Wall -Wcast-align -Wpointer-arith -Wsign-compare -Wshadow -g -O2

CPPFLAGS= $(INCLUDE)

# list of linker flags
LDFLAGS=-L/usr/local/lib

export TARGET CC CXX AR LD CFLAGS CXXFLAGS CPPFLAGS LDFLAGS

all: depend
    $(MAKE) -C ./src

depend:
    $(MAKE) -C ./src depend

clean:
    $(MAKE) -C ./src clean

.PHONY: clean

Previous makefile expects that all source files are placed in separate directory named 'src'. In that directory is also second makefile, which does all the job.

#create list of all object and depend files
OBJ=$(patsubst %.cpp, %.o, $(wildcard *.cpp))
DEP=$(patsubst %.cpp, .deps\/%.d, $(wildcard *.cpp))

all: $(TARGET)

depend: $(DEP)

$(TARGET): $(OBJ)

#link target binary
ifeq ($(findstring .so, $(TARGET)), .so)
    $(CXX) -shared $(LDFLAGS) -o $(TARGET) $(OBJ)
else
    ifeq ($(findstring .a, $(TARGET)), .a)
    $(AR) cr $(TARGET) $(OBJ)
    else
    $(CXX) $(LDFLAGS) $(SYS_LIBS) $(DEP_LIBS) -o $(TARGET) $(OBJ)
    endif
endif

# compile object file
%.o: %.cpp
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@

# create directory for depend files
.deps:
    mkdir .deps

# create depend files
.deps\/%.d: %.cpp .deps
    set -e; \
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) -MM $< | \
    sed 's/\($*\)\.o[ :]*/\1.o $@: /g' > $@;
    [ -s $@ ] || rm -f $@


clean:
    rm -f $(TARGET)
    rm -f *.o
    rm -fr .deps


.PHONY: clean

# include depend files when building object files
-include $(DEP)

Here is an example of directory structure for convenience.

|-- Makefile
`-- src
    |-- Makefile
    |-- main.cpp
    |-- worker.cpp
    `-- worker.h
.