Go away human. You should not be here.

Back

07 Dec 2012

Kill all humans. Kill all humans. Kill all humans.

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

.