#
# GNUMakefile
#
# Copyright (C) 2017-2019, Takazumi Shirayanagi
# This software is released under the new BSD License,
# see LICENSE
#

# Test GNUmakefile (for GNU make)
#
# SYNOPSIS:
#
#   make [all]         - makes everything.
#   make test          - makes everything and run.
#   make TARGET        - makes the given target.
#   make run_$(TARGET) - makes the given target and run.
#   make clean         - removes all files generated by make.

#
#
# Config
#

ifndef OUTDIR
OUTDIR = .
endif

#
#
# include
#
include ../utils/utils.mk
include CommonMakefile.in

#
#
# Flags passed to the C++ compiler.
#

# Optimize
#OPTIMIZE=-O2

STRICT_CXXFLAGS:= -Wformat=2 -Wcast-qual -Wcast-align -Wwrite-strings -Wpointer-arith
# STRICT_CXXFLAGS+= -Wfloat-equal
# Necessary for peep class operator
# STRICT_CXXFLAGS+= -Wconversion

CXXFLAGS += -g -Wall -Wextra $(STRICT_CXXFLAGS) $(OPTIMIZE)

ifdef _DEBUG
CXXFLAGS += -ggdb
endif

ifdef _VERBOSE
CXXFLAGS += -v
endif

ifdef USE_COVERAGE
CXXFLAGS += -coverage
ifndef CCOV
CCOV=$(USE_COVERAGE)
endif
endif

ifdef GGC_MIN_EXPAND
CXXFLAGS += --param ggc-min-expand=${GGC_MIN_EXPAND}
endif
ifdef GGC_MIN_HEAPSIZE
CXXFLAGS += --param ggc-min-heapsize=${GGC_MIN_HEAPSIZE}
endif

ifdef CI_NODEBUG
CXXFLAGS += -g0 -O0
endif

# debug options
# CXXFLAGS += -ftemplate-backtrace-limit=0

#
#
# compiler
#

#
# Wine g++
#
ifeq ($(CXX),wineg++)
LIBS+=ws2_32
CXXFLAGS += -static-libgcc -static-libstdc++ -m32
endif

#
# scan-build
#
ifeq ($(findstring c++-analyzer, $(CXX)),c++-analyzer)
USE_SCANBUILD=yes
endif

ifdef OUTPUTXML
CXXFLAGS +=-DOUTPUTXML
endif

ifeq ($(HOST),WINE)
#WINE_ =wine
WINE_ =wineconsole
else
WINE_ =
endif

ifeq ($(IUTEST_MKFLAG_CYGWIN),1)
STDFLAG:=$(subst c++,gnu++,$(STDFLAG))
endif

ifdef USE_GNU_EXTENSION
ifeq ($(USE_GNU_EXTENSION),0)
STDFLAG:=$(subst gnu++,c++,$(STDFLAG))
else
STDFLAG:=$(subst c++,gnu++,$(STDFLAG))
endif
endif

#
# gtest-version tests
#
ifdef GTEST_EXPECT_VER
CXXFLAGS+=-DGTEST_EXPECT_VER=$(GTEST_EXPECT_VER)
ifndef GTEST_EXPECT_LATEST
CXXFLAGS+=-DGTEST_EXPECT_LATEST=0
endif
endif
ifdef GTEST_EXPECT_LATEST
CXXFLAGS+=-DGTEST_EXPECT_LATEST=$(GTEST_EXPECT_LATEST)
endif

CXXFLAGS += $(DEFS) $(STDFLAG) $(STDLIB)
LIBS_ = $(LIBS:%=-l%)
LDFLAGS += $(LIBS_)

MAKEFILE=GNUmakefile CommonMakefile.in

ALLTESTS_TEST_OBJS = $(ALLTESTS_TEST_SRCS:%.cpp=$(OUTDIR)/%.o)
ALLTESTS_SRC_OBJS = $(ALLTESTS_TEST_OBJS) main.cpp
NAMESPACETESTS_TEST_SRCS = $(ALLTESTS_TEST_SRCS:%.cpp=%.ns.cpp)
NAMESPACETESTS_TEST_OBJS = $(NAMESPACETESTS_TEST_SRCS:%.cpp=$(OUTDIR)/%.o)
NAMESPACETESTS_SRC_AND_OBJS = main.cpp $(NAMESPACETESTS_TEST_OBJS)

ALL_OBJS=$(ALLTESTS_TEST_OBJS) $(NAMESPACETESTS_TEST_OBJS)

COMMON_RUN_TARGETS=$(ALLTESTS_TARGET) $(EXTENSIONTESTS_TARGET) $(TARGETS1) $(TARGETS2)
ifndef CI_SMALL_RESOURCE
COMMON_RUN_TARGETS+=$(NAMESPACETESTS_TARGET)
endif
ifneq ($(USE_GTEST)$(USE_GMOCK),)
RUN_TARGETS = $(COMMON_RUN_TARGETS)
else
RUN_TARGETS = $(COMMON_RUN_TARGETS) $(TARGETS_IUTEST_ONLY) $(UNITTESTS_TARGET)
endif

ifeq ($(IUTEST_USE_PYTHON),1)
ifeq ($(USE_GTEST)$(USE_GMOCK)$(USE_COVERAGE)$(USE_SCANBUILD),)
ERR_TARGETS = $(COMPILEERROR_TARGETS)
endif
endif

BUILD_TARGETS = $(RUN_TARGETS) $(BUILD_ONLY)
TARGETS = $(BUILD_TARGETS) $(ERR_TARGETS)
NO_DEFAULT_TARGETS=$(SYNTAXTESTS_TARGET)

SRCS   = $(BUILD_TARGETS:%=%.cpp)
RUNNER = $(RUN_TARGETS:$(OUTDIR)/%=run_%)

ifdef USE_FUSEDD
IUTEST_INCLUDE=-I$(IUTEST_DIR)/fused-src
IUTEST_HEADERS+=$(IUTEST_DIR)/fused-src/iutest.hpp
endif

COVERITY_SCAN_BUILD_TARGETS = $(ALLTESTS_TARGET) $(EXTENSIONTESTS_TARGET) $(TARGETS1) $(TARGETS2) $(TARGETS_IUTEST_ONLY) $(UNITTESTS_TARGET)

#
#
# build targets.
#

.PHONY: clean default all run test bench benchmark lint cppcheck gtest-version

default : $(TARGETS)

all : clean default test

# dir :
# 	@if [ ! -e `dirname $(OUTDIR)` ]; then mkdir -p `dirname $(OUTDIR)`; fi

clean :
	$(RM) $(TARGETS) $(NO_DEFAULT_TARGETS) $(ALL_OBJS) *.o *.stackdump *.core *.exe *.log *.ns.cpp check_stdlib
	$(RM) -R *.dSYM
ifdef OUTPUTXML
	$(RM) *.xml
endif
ifdef USE_COVERAGE
	$(RM) *.xml *.gcov *.gcno *.gcda coverage.info
endif

clean-xml :
	$(RM) *.xml

clean-cov :
	$(RM) *.xml *.gcov *.gcno *.gcda coverage.info

clean-all : clean clean-xml clean-cov
	make -C benchmark clean
	make -C configcheck clean
	make -C cpplint clean

check : test
run : test
test : $(BUILD_ONLY) $(RUNNER)

lint : cpplint/Makefile $(IUTEST_HEADERS) $(MAKEFILE)
	make -C cpplint

showcxxversion:
	$(CXX) --version
ifeq ($(CXX_NAME),g++)
	$(CXX) -dumpversion
endif
ifdef GCCMAJOR
	@echo g++-$(GCCMAJOR).$(GCCMINOR)
endif
ifdef CLANGMAJOR
	@echo clang++-$(CLANGMAJOR).$(CLANGMINOR)
endif

showcxxmacros:
	$(CXX) $(STDFLAG) -dM -E -x c++ - < /dev/null

showcxxflags:
	@echo $(CXXFLAGS)

%_build: $(OUTDIR)/%
	@echo $(OUTDIR)/$(<F)

%_run : $(OUTDIR)/%
	@echo $(OUTDIR)/$(<F)
ifdef USE_COVERAGE
	${WINE_} $(OUTDIR)/$(<F) $(RUN_OPTION) --iutest_output=xml:$(OUTDIR)/$(<F).xml > /dev/null
else
ifdef OUTPUTXML
ifeq ($(OUTPUTXML),junit)
	${WINE_} $(OUTDIR)/$(<F) $(RUN_OPTION) --iutest_output=junit:$(OUTDIR)/$(<F).xml
else
	${WINE_} $(OUTDIR)/$(<F) $(RUN_OPTION) --iutest_output=xml:$(OUTDIR)/$(<F).xml
endif
else
	${WINE_} $(OUTDIR)/$(<F) $(RUN_OPTION)
endif
endif

run_% : %_run
	@echo $< > /dev/null

prepare: $(NAMESPACETESTS_TEST_SRCS)
	@if [[ ! -d "${OUTDIR}" ]]; then mkdir $(OUTDIR); fi

# gtest version test
gtest-version: clean gtest_version_tests_run

coverity_scan: $(COVERITY_SCAN_BUILD_TARGETS)

#
#
# fused
#

FUSED_PY= ../tools/fused/*.py

fused: $(IUTEST_HEADERS) $(MAKEFILE) ../tools/fused/Makefile $(FUSED_PY)
	make -C ../tools/fused

fused_min: $(IUTEST_HEADERS) $(MAKEFILE) ../tools/fused/Makefile $(FUSED_PY)
	make -C ../tools/fused min

#
#
# coverage
#

COVARALLS_EXCLUDE=-e doc -e projects -e samples -e tools
COVARALLS_EXCLUDE_PATTERN  =-E ".*/test/[^\.].*" -E ".*/include/tr1/.*" -E ".*/include/gtest/.*"
# define and constant only header
#COVARALLS_EXCLUDE_PATTERN +=-E ".*iutest_ver\.hpp" -E ".*iutest_config\.hpp" -E ".*iutest_ignore\.hpp"
#COVARALLS_EXCLUDE_PATTERN +=-E ".*iutest_compiler\.hpp" -E ".*iutest_constant\.hpp" -E ".*iutest_util\.hpp"
#COVARALLS_EXCLUDE_PATTERN +=-E ".*iutest_internal\.hpp" -E ".*iutest_pp\.hpp" -E ".*iutest_pragma\.hpp"
#COVARALLS_EXCLUDE_PATTERN +=-E ".*iutest_typelist\.hpp" -E ".*iutest_type_traits\.hpp" -E ".*iutest_typed_util\.hpp"
#COVARALLS_EXCLUDE_PATTERN +=-E ".*iutest_all\.cpp" -E ".*iutest_util_output\.hpp" -E ".*iutest_tuple\.hpp"
COVARALLS_SRCEXT=-x .h -x .hpp -x .cpp -x .ipp
ifdef LCOV_GCOV_TOOL
CCOV_OPT += --gcov-tool $(LCOV_GCOV_TOOL)
endif

coverage:
	$(CCOV) -v
ifeq ($(USE_COVERAGE),lcov)
	$(CCOV) -c -d . -o coverage.info $(CCOV_OPT)
	$(CCOV) -r coverage.info *gcc* -o coverage.info
	$(CCOV) -r coverage.info */test/* -o coverage.info
endif
ifeq ($(USE_COVERAGE),gcov)
ifeq ($(findstring relative-only, $(shell $(CCOV) --help)), relative-only)
	$(CCOV) -r *.gcda $(CCOV_OPT)
else
	$(CCOV) *.gcda $(CCOV_OPT)
endif
endif

COVERALLS_OPTION= --service travis-pro
send-coveralls: coverage
ifeq ($(USE_COVERAGE),lcov)
	cd ../; lcoveralls ${COVERALLS_OPTION} --retry-count 3 ./test/coverage.info
endif
ifeq ($(USE_COVERAGE),gcov)
	cd ../; coveralls ${COVERALLS_OPTION} -n -r ./ -b ./test $(COVARALLS_EXCLUDE_PATTERN) $(COVARALLS_EXCLUDE)
endif

send-codecov: coverage
	curl -s https://codecov.io/bash | bash /dev/stdin

#
#
# benchmark
#
bench: benchmark
benchmark: build_benchmark
	@awk 'BEGIN{ sum=0; max=0; min=-1; num=0; } \
		{ if($$1=="user") { num+=1; if(min==-1){ min=$$2; } sum+=$$2; if($$2>max){max=$$2}; if(min>$$2){min=$$2}; } }\
		END{ num-=2; sum-=min; sum-=max; print("basis", sum/num); }' benchmark_basis_time.log >> benchmark_build_time.log
	@awk 'BEGIN{ sum=0; max=0; min=-1; num=0; basis=0; } \
		{ if($$1=="user") { num+=1; if(min==-1){ min=$$2; } sum+=$$2; if($$2>max){max=$$2}; if(min>$$2){min=$$2}; } }\
		{ if($$1=="basis") { basis=$$2; } } \
		END{ print("Total:", sum, "(",num,")" ); num-=2; sum-=min; sum-=max; print("Min:", min); print("Max:", max); \
		avg=sum/num; print("Avg:", avg); print("Score:", basis/avg*100); print("Basis:", basis); }' benchmark_build_time.log

BENCHMARK_MAKE_OPTION=--no-print-directory -C benchmark

build_benchmark: SHELL=/bin/bash
build_benchmark:
	@if [ -f benchmark_basis_time.log ]; then rm benchmark_basis_time.log; fi
	@if [ -f benchmark_build_time.log ]; then rm benchmark_build_time.log; fi
	@num=1; while [[ $$num -le 10 ]]; do \
		make $(BENCHMARK_MAKE_OPTION) clean; \
		{ time -p make $(BENCHMARK_MAKE_OPTION) -j 1 basis 2>&1; } 2>> benchmark_basis_time.log; \
		make $(BENCHMARK_MAKE_OPTION) clean prebuild; \
		{ time -p make $(BENCHMARK_MAKE_OPTION) -j 1 build 2>&1; } 2>> benchmark_build_time.log; \
		((num = num + 1)); \
	done

#
#
# NAMESPACETESTS_TEST_SRCS
#

%.ns.cpp: %.cpp
	@echo $@
	@echo '#include <cmath>' > $@
	@echo '#include "../include/gtest/iutest_spi_switch.hpp"' >> $@
	@echo '#include "../include/internal/iutest_filepath.hpp"' >> $@
	@echo 'namespace test { namespace iutest { class dummy; }' >> $@
	@echo '#include "$<"' >> $@
	@echo '}' >> $@


#
#
# Build tests.
#

ifeq ($(USE_GTEST)$(USE_GMOCK),)

TARGETS_EXCLUDE_ALLTESTS= $(TARGETS1) $(TARGETS2) $(TARGETS_IUTEST_ONLY) $(BUILD_ONLY)
CXXFLAGS += -Werror $(IUTEST_CXX_WARN_FLAGS)

ifdef USE_LIB
CXXFLAGS += -DIUTEST_USE_LIB=1 -L../lib
LDFLAGS:= -liutest $(LDFLAGS)
endif

$(TARGETS_EXCLUDE_ALLTESTS) : $(OUTDIR)/% : %.cpp $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -o $@ $< $(LDFLAGS)


# compiler error test

PY_COMPILEERROR_TEST_TOOL=../tools/python/iutest_compile_error_test.py
ifeq ($(CXX_NAME),)
IUTEST_COMPILE_ERROR_TEST_OPTION=-c $(CXX)
else
IUTEST_COMPILE_ERROR_TEST_OPTION=-c $(CXX_NAME)
endif
ifeq ($(CI),true)
IUTEST_COMPILE_ERROR_TEST_OPTION+= --verbose
endif

$(ERR_TARGETS) : $(OUTDIR)/% : %.cpp $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -o $@ $< $(LDFLAGS) 2>&1 | python $(PY_COMPILEERROR_TEST_TOOL) $(IUTEST_COMPILE_ERROR_TEST_OPTION)

else

# use Google Test or Google Mock
include ../utils/usegtest.mk

$(BUILD_ONLY) : $(OUTDIR)/% : %.cpp $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -o $@ $< $(LDFLAGS)

$(TARGETS1) : $(OUTDIR)/% : %.cpp $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -o $@ $< $(LDFLAGS)

$(TARGETS2) : $(OUTDIR)/% : %.cpp $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -o $@ $<

endif

$(ALLTESTS_TARGET) : $(ALLTESTS_SRC_OBJS) $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -o $@ $(ALLTESTS_SRC_OBJS) $(LDFLAGS)

$(NAMESPACETESTS_TARGET) : $(NAMESPACETESTS_SRC_AND_OBJS) $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -o $@ $(NAMESPACETESTS_SRC_AND_OBJS) $(LDFLAGS)

$(EXTENSIONTESTS_TARGET) : $(EXTENSIONTESTS_SRCS) $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -o $@ $(EXTENSIONTESTS_SRCS) $(LDFLAGS)

$(UNITTESTS_TARGET) : $(UNITTESTS_SRCS) $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -o $@ $(UNITTESTS_SRCS) $(LDFLAGS)

$(SYNTAXTESTS_TARGET) : $(SYNTAXTESTS_SRCS) $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -o $@ $(SYNTAXTESTS_SRCS) $(LDFLAGS)

$(OUTDIR)/%.o: %.cpp $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -c -o $@ $<

.cpp: $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -o $@ $@.cpp main.cpp $(LDFLAGS)

check_stdlib: % : %.cpp $(IUTEST_HEADERS) $(MAKEFILE)
	$(CXX) $(IUTEST_INCLUDE) $(CXXFLAGS) -v -o $@ $<
	./$@

#
#
# Include Guard Test
#

IUTEST_INCLUDE_FILES := $(wildcard $(IUTEST_HEADERS))
IUTEST_INCLUDE_GUARD_CHECK := $(IUTEST_INCLUDE_FILES:%=%.incg)

check_incg: $(IUTEST_INCLUDE_GUARD_CHECK)

%.incg: %
#	@echo $(notdir $<)
	python ../tools/python/iutest_incg_test.py $<


#
# cppcheck
#

cppcheck:
	make -C cppcheck

#
# report
#

report:
	CXX_NAME=${CXX_NAME} CXX_VERSION=${CXX_VERSION} STDFLAG=${STDFLAG} ./../.ci/report-basic-test.sh || true
