
################################################################################
#####
#####   MAKEFILE FOR TYPICAL SMALL C++ PROJECTS
#####
################################################################################
#####
#####   The following targets are provided:
#####
#####     default       : default project = install (create release binary and install)
#####     all           : create debug and release binary and install release
#####     install       : create release binary and install (i.e., copy to install folder)
#####     debug         : create debug binary
#####     release       : create release binary
#####     clean         : delete binary and intermediate folder for current compiler
#####     realclean     : delete complete binary and intermediate folders
#####                     and additional files (such as Visual Studio stuff)
#####
################################################################################





################################################################################
#  main variables
################################################################################
PROJECT_NAME  = ac
SOURCE_DIR    = $(PROJECT_NAME)
BUILD_DIR     = build
BINARY_DIR    = bin
CPP_COMP      = g++
PREFIX        = [$(PROJECT_NAME)]



################################################################################
#  check required binaries
################################################################################
REQUIRED_BINARIES = echo cp mkdir rm sed $(CPP_COMP)
binsOK := $(foreach binary,$(REQUIRED_BINARIES),\
            $(if $(shell which $(binary) 2>/dev/null),,\
              $(error $(PREFIX) ERROR: Binary "$(binary)" is not in PATH)\
            )\
          )



################################################################################
#  set and check derived variables
################################################################################
CPP_VERS      = $(CPP_COMP)-$(shell $(CPP_COMP) -dumpversion)

SOURCES_C     = $(wildcard $(SOURCE_DIR)/*.c)
SOURCES_CC    = $(wildcard $(SOURCE_DIR)/*.cc)
SOURCES_CPP   = $(wildcard $(SOURCE_DIR)/*.cpp)
SOURCES       = $(SOURCES_C) $(SOURCES_CC) $(SOURCES_CPP)

ifeq ($(strip $(SOURCES)),)
  $(error $(PREFIX) ERROR: No C/C++ sources found in folder "$(SOURCE_DIR)")
endif

OBJ_DIR       = $(BUILD_DIR)/$(CPP_VERS)
OBJ_DIR_DBG   = $(OBJ_DIR)/debug
OBJ_DIR_REL   = $(OBJ_DIR)/release
OBJECTS_DBG   = $(SOURCES_C:$(SOURCE_DIR)/%.c=$(OBJ_DIR_DBG)/%.o) \
                $(SOURCES_CC:$(SOURCE_DIR)/%.cc=$(OBJ_DIR_DBG)/%.o) \
                $(SOURCES_CPP:$(SOURCE_DIR)/%.cpp=$(OBJ_DIR_DBG)/%.o)
OBJECTS_REL   = $(SOURCES_C:$(SOURCE_DIR)/%.c=$(OBJ_DIR_REL)/%.o) \
                $(SOURCES_CC:$(SOURCE_DIR)/%.cc=$(OBJ_DIR_REL)/%.o) \
                $(SOURCES_CPP:$(SOURCE_DIR)/%.cpp=$(OBJ_DIR_REL)/%.o)

BIN_EXT       =
BIN_DIR       = $(BINARY_DIR)/$(CPP_VERS)
BIN_DIR_DBG   = $(BIN_DIR)/debug
BIN_DIR_REL   = $(BIN_DIR)/release
BINARY_DBG    = $(BIN_DIR_DBG)/$(PROJECT_NAME)$(BIN_EXT)
BINARY_REL    = $(BIN_DIR_REL)/$(PROJECT_NAME)$(BIN_EXT)

ifeq ($(strip $(INSTALL_DIR)),)
  INSTALL_BIN = 
else
  INSTALL_BIN = $(INSTALL_DIR)/$(PROJECT_NAME)$(BIN_EXT)
endif

ADD_DEL_DIRS  = .vs
ADD_DEL_FILES = $(wildcard *.sdf *~ *.bak $(SOURCE_DIR)/*~ $(SOURCE_DIR)/*.bak)



################################################################################
#  compiler and linker flags
################################################################################
CPP_FLAGS_GEN  = -Wall -Werror -std=c++11 $(CPP_ADD_FLAGS)     
CPP_FLAGS_DBG  = -g  $(CPP_FLAGS_GEN)
CPP_FLAGS_REL  = -O3 $(CPP_FLAGS_GEN)
LINK_FLAGS_GEN = -lm
LINK_FLAGS_DBG = $(LINK_FLAGS_GEN)
LINK_FLAGS_REL = $(LINK_FLAGS_GEN)



################################################################################
#  main targets
################################################################################
.SUFFIXES:

.PHONY:     folders_debug folders_release folders_install \
            delete_bin_build delete_all \
            default all install debug release clean realclean 

default:    install

all:        debug release install

debug:      folders_debug   $(BINARY_DBG)

release:    folders_release $(BINARY_REL)

install:    release folders_install $(INSTALL_BIN)

clean:      delete_bin_build

realclean:  delete_all



################################################################################
#  rules for installing binaries
################################################################################
$(INSTALL_BIN): $(BINARY_REL)
	@echo "$(PREFIX) -> installing release binary as \"$(INSTALL_BIN)\""
	@cp -T $(BINARY_REL) $(INSTALL_BIN)
	@if [ ! -r $(INSTALL_BIN) ]; \
	 then \
	   echo "$(PREFIX) ERROR: Could not install binary \"$(INSTALL_BIN)\""; \
	 fi



################################################################################
#  rules for creating binaries
################################################################################
define link_binary  # (linker flags, object files)
       @echo "$(PREFIX) -> linking binary \"$@\""
       @$(CPP_COMP) -o $@ $(1) $(2)
endef

$(BINARY_REL): $(OBJECTS_REL)
	$(call link_binary,$(LINK_FLAGS_REL),$(OBJECTS_REL))

$(BINARY_DBG): $(OBJECTS_DBG)
	$(call link_binary,$(LINK_FLAGS_DBG),$(OBJECTS_DBG))



################################################################################
#  rules for creating folders
################################################################################
define create_folders # (folders)
       @for f in $(1); \
        do \
          if [ ! -d $$f ]; \
          then \
            echo "$(PREFIX) -> creating folder \"$$f\""; \
            mkdir  -p $$f; \
            if [ ! -d $$f ]; \
            then \
              echo "$(PREFIX) ERROR: Could not create folder \"$$f\""; \
            fi; \
          fi; \
        done
endef

folders_debug:
	$(call create_folders,$(OBJ_DIR_DBG) $(BIN_DIR_DBG))

folders_release:
	$(call create_folders,$(OBJ_DIR_REL) $(BIN_DIR_REL))

folders_install:
	$(call create_folders,$(INSTALL_DIR))



################################################################################
#  rules for creating object files (see http://make.paulandlesley.org/autodep.html)
################################################################################
define compile_depend  # (compiler flags)
       @echo "$(PREFIX) -> compiling file \"$@\""
       @$(CPP_COMP) -c -MMD $(1) -o $@ $<
       @cp $(basename $@).d $(basename $@).P
       @sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' -e '/^$$/ d' \
	    -e 's/$$/ :/' < $(basename $@).d >> $(basename $@).P
       @rm -f $(basename $@).d
endef

$(OBJ_DIR_DBG)/%.o: $(SOURCE_DIR)/%.c
	$(call compile_depend,$(CPP_FLAGS_DBG))

$(OBJ_DIR_DBG)/%.o: $(SOURCE_DIR)/%.cc
	$(call compile_depend,$(CPP_FLAGS_DBG))

$(OBJ_DIR_DBG)/%.o: $(SOURCE_DIR)/%.cpp
	$(call compile_depend,$(CPP_FLAGS_DBG))

$(OBJ_DIR_REL)/%.o: $(SOURCE_DIR)/%.c
	$(call compile_depend,$(CPP_FLAGS_REL))

$(OBJ_DIR_REL)/%.o: $(SOURCE_DIR)/%.cc
	$(call compile_depend,$(CPP_FLAGS_REL))

$(OBJ_DIR_REL)/%.o: $(SOURCE_DIR)/%.cpp
	$(call compile_depend,$(CPP_FLAGS_REL))



################################################################################
#  rules for deleting files and folders
################################################################################
define delete_folders # (folders)
       @for f in $(1); \
        do \
          if [ -d $$f ]; \
          then \
            echo "$(PREFIX) -> delete folder \"$$f\""; \
            rm  -rf $$f; \
            if [ -d $$f ]; \
            then \
              echo "$(PREFIX) ERROR: Could not delete folder \"$$f\""; \
            fi; \
          fi; \
        done
endef
define delete_files # (files)
       @for f in $(1); \
        do \
          if [ -r $$f ]; \
          then \
            echo "$(PREFIX) -> delete file \"$$f\""; \
            rm   -f $$f; \
            if [ -r $$f ]; \
            then \
              echo "$(PREFIX) ERROR: Could not delete file \"$$f\""; \
            fi; \
          fi; \
        done
endef

delete_bin_build:
	$(call delete_folders,$(OBJ_DIR) $(BIN_DIR))
	$(call delete_files,$(INSTALL_BIN))

delete_all:
	$(call delete_folders,$(BUILD_DIR) $(BINARY_DIR) $(ADD_DEL_DIRS))
	$(call delete_files,$(INSTALL_BIN) $(ADD_DEL_FILES))



################################################################################
#  include dependency files
################################################################################
-include $(OBJECTS_DBG:.o=.P)
-include $(OBJECTS_REL:.o=.P)



# end of makefile

