#---------
#
# ComponentMakefile
#
# Include this file in your makefile
# It makes
#    A static library
#    A test executable
#
# The necessary parameters are shown in 
#    ComponentMakefileExampleParameters
#
# Inputs 
#   SRC_FILES - Specific source files to build into library
#	SRC_DIRS - Directories od source file to built into the library
#	TEST_SRC - unit test code build into the unit test runner
#	MOCKS_SRC - mock objects build into the test runner
#	INCLUDES - List of -I files
#	CPPUTEST_CXXFLAGS - flags for the C++ compiler
#	CPPUTEST_CPPFLAGS - flags for the C++ AND C compiler
#	CPPUTEST_CFLAGS - C complier
#	CPPUTEST_LDFLAGS - Linker flags
#	TARGET_LIB -  the created library
#	TEST_TARGET - the created test executable
#	OTHER_MAKEFILE_TO_INCLUDE - a hook to use this makefile to make 
#		other targets. Like CSlim, which is part of fitnesse
#----------

#Helper Functions
get_src_from_dir  = $(wildcard $1/*.cpp) $(wildcard $1/*.c)
get_dirs_from_dirspec  = $(wildcard $1)
get_src_from_dir_list = $(foreach dir, $1, $(call get_src_from_dir,$(dir)))				        
src_to_o = $(subst .c,.o, $(subst .cpp,.o,$1))
src_to_d = $(subst .c,.d, $(subst .cpp,.d,$1))
#move_file_location = $(patsubst %.d,build/%.d, $1)
src_to = $(subst .c,$1, $(subst .cpp,$1,$2))

#Derived
STUFF_TO_CLEAN += $(TEST_TARGET) $(TEST_TARGET).exe $(TARGET_LIB)

SRC += $(call get_src_from_dir_list, $(SRC_DIRS)) $(SRC_FILES)			        
OBJ = $(call src_to_o,$(SRC))
STUFF_TO_CLEAN += $(OBJ)

TEST_SRC = $(call get_src_from_dir_list, $(TEST_SRC_DIRS))
TEST_OBJS = $(call src_to_o,$(TEST_SRC))
STUFF_TO_CLEAN += $(TEST_OBJS)

MOCKS_SRC = $(call get_src_from_dir_list, $(MOCKS_SRC_DIRS))
MOCKS_OBJS = $(call src_to_o,$(MOCKS_SRC))
STUFF_TO_CLEAN += $(MOCKS_OBJS)

ALL_SRC = $(SRC) $(TEST_SRC) $(MOCKS_SRC)

#Test coverage with gcov
GCOV_OUTPUT = gcov_output.txt
GCOV_REPORT = gcov_report.txt
GCOV_ERROR = gcov_error.txt
GCOV_GCDA_FILES = $(call src_to,.gcda, $(ALL_SRC))
GCOV_GCNO_FILES = $(call src_to,.gcno, $(ALL_SRC))
TEST_OUTPUT = $(TEST_TARGET).txt
STUFF_TO_CLEAN += \
	$(GCOV_OUTPUT)\
	$(GCOV_REPORT)\
	$(GCOV_ERROR)\
	$(GCOV_GCDA_FILES)\
	$(GCOV_GCNO_FILES)\
	$(TEST_OUTPUT)


#Other stuff needed
CPPUTEST_LIB = $(CPPUTEST_HOME)/lib/libCppUTest.a 

CPPUTEST_CPPFLAGS +=  $(INCLUDES) $(GCOVFLAGS)

#The gcda files for gcov need to be deleted before each run
#To avoid annoying messages.
GCOV_CLEAN = $(SILENCE)rm -f $(GCOV_GCDA_FILES) $(GCOV_OUTPUT) $(GCOV_REPORT) $(GCOV_ERROR)
RUN_TEST_TARGET = $(SILENCE)  $(GCOV_CLEAN) ; echo "Running $(TEST_TARGET)"; ./$(TEST_TARGET)

ifneq "$(OTHER_MAKEFILE_TO_INCLUDE)" ""
-include $(OTHER_MAKEFILE_TO_INCLUDE)
endif

ifneq "$(MAP_FILE)" ""
	CPPUTEST_LDFLAGS += -Wl,-map,$(MAP_FILE)
endif

INCLUDES_DIRS_EXPANDED = $(call get_dirs_from_dirspec, $(INCLUDE_DIRS))
INCLUDES += $(foreach dir, $(INCLUDES_DIRS_EXPANDED), -I$(dir))
MOCK_DIRS_EXPANDED = $(call get_dirs_from_dirspec, $(MOCKS_SRC_DIRS))
INCLUDES += $(foreach dir, $(MOCK_DIRS_EXPANDED), -I$(dir))


DEP_FILES = $(call src_to_d, $(ALL_SRC))
STUFF_TO_CLEAN += $(DEP_FILES) + $(PRODUCTION_CODE_START) + $(PRODUCTION_CODE_END)
STUFF_TO_CLEAN += $(STDLIB_CODE_START) + $(MAP_FILE) + cpputest_*.xml junit_run_output

# We'll use the CPPUTEST_CFLAGS etc so that you can override AND add to the CppUTest flags
CFLAGS = $(CPPUTEST_CFLAGS) $(CPPUTEST_ADDITIONAL_CFLAGS)
CPPFLAGS = $(CPPUTEST_CPPFLAGS) $(CPPUTEST_ADDITIONAL_CPPFLAGS)
CXXFLAGS = $(CPPUTEST_CXXFLAGS) $(CPPUTEST_ADDITIONAL_CXXFLAGS)
LDFLAGS = $(CPPUTEST_LDFLAGS) $(CPPUTEST_ADDITIONAL_LDFLAGS)

# Targets
.PHONY: all
all: $(TEST_TARGET) 
	$(RUN_TEST_TARGET)

.PHONY: all_no_tests
all_no_tests: $(TEST_TARGET)

.PHONY: flags
flags: 
	$(SILENCE)echo Compile with these flags:
	$(SILENCE)for f in $(CPPFLAGS) ; do \
		echo "    C++ $$f" ; \
	done
	$(SILENCE)for f in $(CFLAGS) ; do \
		echo "    C   $$f" ; \
	done
	$(SILENCE)for f in $(LDFLAGS) ; do \
		echo "    LD   $$f" ; \
	done
	$(SILENCE)for f in $(ARFLAGS) ; do \
		echo "    AR   $$f" ; \
	done
	
	
$(TEST_TARGET): $(TEST_OBJS) $(MOCKS_OBJS)  $(PRODUCTION_CODE_START) $(TARGET_LIB) $(USER_LIBS) $(PRODUCTION_CODE_END) $(CPPUTEST_LIB) $(STDLIB_CODE_START) 
	$(SILENCE)echo Linking $@
	$(SILENCE)echo command $(LINK.o) -o $@ $^ $(LD_LIBRARIES)
	$(SILENCE)$(LINK.o) -o $@ $^ $(LD_LIBRARIES)

$(TARGET_LIB): $(OBJ)
	$(SILENCE)echo Building archive $@
	$(SILENCE)mkdir -p lib
	$(SILENCE)echo command $(AR) $(ARFLAGS) $@ $^
	$(SILENCE)$(AR) $(ARFLAGS) $@ $^
	$(SILENCE)ranlib $@

test: $(TEST_TARGET)
	$(RUN_TEST_TARGET) | tee $(TEST_OUTPUT)
	
vtest: $(TEST_TARGET)
	$(RUN_TEST_TARGET) -v  | tee $(TEST_OUTPUT)

%.o: %.cpp
	@echo compiling $(notdir $<)
	$(SILENCE)echo command $(COMPILE.cpp) -M -MF $(subst .o,.d,$@) -MT "$@ $(subst .o,.d,$@)" $<
	$(SILENCE)$(COMPILE.cpp) -M -MF $(subst .o,.d,$@) -MT "$@ $(subst .o,.d,$@)" $<
	$(SILENCE)echo command $(COMPILE.cpp) $(OUTPUT_OPTION) $<
	$(SILENCE)$(COMPILE.cpp) $(OUTPUT_OPTION) $<

%.o: %.c
	@echo compiling $(notdir $<)
	$(SILENCE)echo command $(COMPILE.c) -M -MF $(subst .o,.d,$@) -MT "$@ $(subst .o,.d,$@)" $<
	$(SILENCE)$(COMPILE.c) -M -MF $(subst .o,.d,$@) -MT "$@ $(subst .o,.d,$@)" $<
	$(SILENCE)echo command $(COMPILE.c) $(OUTPUT_OPTION) $<
	$(SILENCE)$(COMPILE.c) $(OUTPUT_OPTION) $<

ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEP_FILES)
endif

.PHONY: clean
clean:
	$(SILENCE)echo Making clean
	$(SILENCE)$(RM) -rf $(STUFF_TO_CLEAN)
	$(SILENCE)find . -name \*.gcov | xargs rm -f

gcov: test
	$(SILENCE)for d in $(SRC_DIRS) ; do \
		gcov -o $$d $$d/*.c $$d/*.cpp >> $(GCOV_OUTPUT) 2>>$(GCOV_ERROR) ; \
	done
	$(CPPUTEST_HOME)/scripts/filterGcov.sh $(GCOV_OUTPUT) $(GCOV_ERROR) $(GCOV_REPORT) $(TEST_OUTPUT)
	cat $(GCOV_REPORT)
 
.PHONEY: format
format: 
	$(CPPUTEST_HOME)/scripts/reformat.sh $(PROJECT_HOME_DIR)
	
debug:
	echo Stuff to clean
	$(SILENCE)for f in $(STUFF_TO_CLEAN) ; do \
		echo "$$f" ; \
	done
	echo Includes
	$(SILENCE)for i in $(INCLUDES) ; do \
		echo "$$i" ; \
	done
	
