# A sample Makefile for building Google Test and using it in user
# A sample Makefile for building Google Test and using it in user
# tests.  Please tweak it to suit your environment and project.  You
# may want to move it to your project's root directory.
#
# SYNOPSIS:
#
#   make [all]  - makes everything.
#   make TARGET - makes the given target.
#   make clean  - removes all files generated by make.


# Where to find user code.
USER_DIR = ../main
TEST_DIR = unit
ROOT = ../..
OBJECT_DIR = ../../obj/test
TARGET_DIR = $(USER_DIR)/target

include $(ROOT)/make/system-id.mk
include $(ROOT)/make/targets_list.mk

VPATH := $(VPATH):$(USER_DIR):$(TEST_DIR)

# specify which files that are included in the test in addition to the unittest file.
# variables available:
#   <test_name>_SRC
#   <test_name>_DEFINES
#   <test_name>_INCLUDE_DIRS
#   <test_name>_EXPAND (run for each target, call the above with target as $1)
#   <test_name>_BLACKLIST (targets to exclude from an expanded test's run)

alignsensor_unittest_SRC := \
		$(USER_DIR)/sensors/boardalignment.c \
		$(USER_DIR)/common/sensor_alignment.c \
		$(USER_DIR)/common/maths.c

arming_prevention_unittest_SRC := \
		$(USER_DIR)/fc/core.c \
		$(USER_DIR)/fc/dispatch.c \
		$(USER_DIR)/fc/rc_controls.c \
		$(USER_DIR)/fc/rc_modes.c \
		$(USER_DIR)/fc/runtime_config.c \
		$(USER_DIR)/flight/gps_rescue.c \
		$(USER_DIR)/common/bitarray.c

arming_prevention_unittest_DEFINES := \
            USE_GPS_RESCUE=

atomic_unittest_SRC := \
		$(USER_DIR)/build/atomic.c \
		$(TEST_DIR)/atomic_unittest_c.c

baro_bmp085_unittest_SRC := \
		$(USER_DIR)/drivers/barometer/barometer_bmp085.c

baro_bmp085_unittest_DEFINES := \
                USE_BARO_BMP085=

baro_bmp280_unittest_SRC := \
		$(USER_DIR)/drivers/barometer/barometer_bmp280.c

baro_bmp280_unittest_DEFINES := \
                USE_BARO_BMP280= \
                USE_BARO_SPI_BMP280=

baro_bmp388_unittest_SRC := \
		$(USER_DIR)/common/maths.c \
		$(USER_DIR)/drivers/barometer/barometer_bmp388.c

baro_bmp388_unittest_DEFINES := \
                USE_EXTI= \
                USE_BARO_BMP388= \
                USE_BARO_SPI_BMP388=

baro_ms5611_unittest_SRC := \
		$(USER_DIR)/drivers/barometer/barometer_ms5611.c

baro_ms5611_unittest_DEFINES := \
                USE_BARO_MS5611= \
                USE_BARO_SPI_MS5611=

# This test is disabled due to build errors.
# Its source code is archived in unit/battery_unittest.cc.txt
#
#battery_unittest_SRC := \
#		$(USER_DIR)/sensors/battery.c \
#		$(USER_DIR)/common/maths.c


blackbox_unittest_SRC :=  \
		$(USER_DIR)/blackbox/blackbox.c \
		$(USER_DIR)/blackbox/blackbox_encoding.c \
		$(USER_DIR)/blackbox/blackbox_io.c \
		$(USER_DIR)/common/encoding.c \
		$(USER_DIR)/common/printf.c \
		$(USER_DIR)/common/maths.c \
		$(USER_DIR)/common/typeconversion.c \
		$(USER_DIR)/drivers/accgyro/gyro_sync.c

blackbox_encoding_unittest_SRC :=  \
		$(USER_DIR)/blackbox/blackbox_encoding.c \
		$(USER_DIR)/common/encoding.c \
		$(USER_DIR)/common/printf.c \
		$(USER_DIR)/common/typeconversion.c

cli_unittest_SRC := \
		$(USER_DIR)/cli/cli.c \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/printf.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/config/feature.c \
		$(USER_DIR)/pg/pg.c \
		$(USER_DIR)/common/typeconversion.c

cli_unittest_DEFINES := \
		USE_OSD= \
		USE_CLI= \
		SystemCoreClock=1000000

cms_unittest_SRC := \
		$(USER_DIR)/cms/cms.c \
		$(USER_DIR)/cms/cms_menu_saveexit.c \
		$(USER_DIR)/common/typeconversion.c \
		$(USER_DIR)/drivers/display.c


common_filter_unittest_SRC := \
		$(USER_DIR)/common/filter.c \
		$(USER_DIR)/common/maths.c


encoding_unittest_SRC := \
		$(USER_DIR)/common/encoding.c


flight_failsafe_unittest_SRC := \
		$(USER_DIR)/common/bitarray.c \
		$(USER_DIR)/fc/rc_modes.c \
		$(USER_DIR)/fc/runtime_config.c \
		$(USER_DIR)/flight/failsafe.c

flight_failsafe_unittest_DEFINES := \
		USE_GPS_RESCUE=


flight_imu_unittest_SRC := \
		$(USER_DIR)/common/bitarray.c \
		$(USER_DIR)/common/maths.c \
		$(USER_DIR)/config/feature.c \
		$(USER_DIR)/fc/rc_modes.c \
		$(USER_DIR)/flight/position.c \
		$(USER_DIR)/flight/imu.c


flight_mixer_unittest :=  \
		$(USER_DIR)/flight/mixer.c \
		$(USER_DIR)/flight/servos.c \
		$(USER_DIR)/common/maths.c


gps_conversion_unittest_SRC := \
		$(USER_DIR)/common/gps_conversion.c


io_serial_unittest_SRC := \
		$(USER_DIR)/io/serial.c \
		$(USER_DIR)/drivers/serial_pinconfig.c


ledstrip_unittest_SRC := \
		$(USER_DIR)/common/bitarray.c \
		$(USER_DIR)/fc/rc_modes.c \
		$(USER_DIR)/io/ledstrip.c

ledstrip_unittest_DEFINES := \
		USE_LED_STRIP=


maths_unittest_SRC := \
		$(USER_DIR)/common/maths.c


motor_output_unittest_SRC := \
		$(USER_DIR)/drivers/dshot.c


osd_unittest_SRC := \
		$(USER_DIR)/osd/osd.c \
		$(USER_DIR)/osd/osd_elements.c \
		$(USER_DIR)/osd/osd_warnings.c \
		$(USER_DIR)/common/typeconversion.c \
		$(USER_DIR)/drivers/display.c \
		$(USER_DIR)/common/maths.c \
		$(USER_DIR)/common/printf.c \
		$(USER_DIR)/common/time.c \
		$(USER_DIR)/common/filter.c \
		$(USER_DIR)/fc/runtime_config.c

osd_unittest_DEFINES := \
		USE_OSD= \
		USE_GPS= \
		USE_RTC_TIME= \
		USE_ADC_INTERNAL=

link_quality_unittest_SRC := \
		$(USER_DIR)/osd/osd.c \
		$(USER_DIR)/osd/osd_elements.c \
		$(USER_DIR)/osd/osd_warnings.c \
		$(USER_DIR)/common/typeconversion.c \
		$(USER_DIR)/drivers/display.c \
		$(USER_DIR)/drivers/serial.c \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/maths.c \
		$(USER_DIR)/common/printf.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/common/time.c \
		$(USER_DIR)/fc/runtime_config.c \
		$(USER_DIR)/common/bitarray.c \
		$(USER_DIR)/fc/rc_modes.c \
		$(USER_DIR)/rx/rx.c \
		$(USER_DIR)/pg/pg.c \
		$(USER_DIR)/rx/crsf.c \
		$(USER_DIR)/pg/rx.c

link_quality_unittest_DEFINES := \
		USE_OSD= \
		USE_CRSF_LINK_STATISTICS= \
		USE_RX_LINK_QUALITY_INFO=

pg_unittest_SRC := \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/pg/pg.c


rc_controls_unittest_SRC := \
		$(USER_DIR)/fc/rc_controls.c \
		$(USER_DIR)/pg/pg.c \
		$(USER_DIR)/common/bitarray.c \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/maths.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/fc/rc_adjustments.c \
		$(USER_DIR)/fc/rc_modes.c


rx_crsf_unittest_SRC := \
		$(USER_DIR)/rx/crsf.c \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/printf.c \
		$(USER_DIR)/common/typeconversion.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/drivers/serial.c


rx_ibus_unittest_SRC := \
		$(USER_DIR)/rx/ibus.c


rx_ranges_unittest_SRC := \
		$(USER_DIR)/common/bitarray.c \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/maths.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/fc/rc_modes.c \
		$(USER_DIR)/rx/rx.c \
		$(USER_DIR)/pg/pg.c \
		$(USER_DIR)/pg/rx.c


rx_rx_unittest_SRC := \
		$(USER_DIR)/rx/rx.c \
		$(USER_DIR)/fc/rc_modes.c \
		$(USER_DIR)/common/bitarray.c \
		$(USER_DIR)/common/maths.c \
		$(USER_DIR)/config/feature.c \
		$(USER_DIR)/pg/rx.c

rx_sumd_unittest_SRC := \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/rx/sumd.c

scheduler_unittest_SRC := \
		$(USER_DIR)/scheduler/scheduler.c \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/streambuf.c

scheduler_unittest_DEFINES := \
		USE_OSD= 

sensor_gyro_unittest_SRC := \
		$(USER_DIR)/sensors/gyro.c \
		$(USER_DIR)/sensors/gyro_init.c \
		$(USER_DIR)/sensors/boardalignment.c \
		$(USER_DIR)/common/filter.c \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/maths.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/common/sensor_alignment.c \
		$(USER_DIR)/drivers/accgyro/accgyro_fake.c \
		$(USER_DIR)/drivers/accgyro/gyro_sync.c \
		$(USER_DIR)/pg/pg.c \
		$(USER_DIR)/pg/gyrodev.c

telemetry_crsf_unittest_SRC := \
		$(USER_DIR)/rx/crsf.c \
		$(USER_DIR)/telemetry/crsf.c \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/maths.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/common/gps_conversion.c \
		$(USER_DIR)/common/printf.c \
		$(USER_DIR)/common/typeconversion.c \
		$(USER_DIR)/fc/runtime_config.c

telemetry_crsf_unittest_DEFINES := \
		FLASH_SIZE=128 \
		STM32F10X_MD= \
		__TARGET__="TEST" \
		__REVISION__="revision"


telemetry_crsf_msp_unittest_SRC := \
		$(USER_DIR)/rx/crsf.c \
		$(USER_DIR)/build/atomic.c \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/common/printf.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/drivers/serial.c \
		$(USER_DIR)/common/typeconversion.c \
		$(USER_DIR)/telemetry/crsf.c \
		$(USER_DIR)/common/gps_conversion.c \
		$(USER_DIR)/telemetry/msp_shared.c \
		$(USER_DIR)/fc/runtime_config.c

telemetry_crsf_msp_unittest_DEFINES := \
		USE_MSP_OVER_TELEMETRY=


telemetry_hott_unittest_SRC := \
		$(USER_DIR)/telemetry/hott.c \
		$(USER_DIR)/common/gps_conversion.c


telemetry_ibus_unittest_SRC := \
		$(USER_DIR)/telemetry/ibus_shared.c \
		$(USER_DIR)/telemetry/ibus.c

timer_definition_unittest_EXPAND := yes

# SITL is a simulator with empty timerHardware and many hearders in target.c.
timer_definition_unittest_BLACKLIST := SITL

timer_definition_unittest_SRC = \
		$(TARGET_DIR)/$(call get_base_target,$1)/target.c

timer_definition_unittest_DEFINES = \
		TARGET=$1 \
		BASE_TARGET=$(call get_base_target,$1)

timer_definition_unittest_INCLUDE_DIRS = \
		$(TEST_DIR)/timer_definition_unittest.include \
		$(TARGET_DIR)/$(call get_base_target,$1)

transponder_ir_unittest_SRC := \
		$(USER_DIR)/drivers/transponder_ir_ilap.c \
		$(USER_DIR)/drivers/transponder_ir_arcitimer.c

ws2811_unittest_SRC := \
		$(USER_DIR)/drivers/light_ws2811strip.c

huffman_unittest_SRC := \
		$(USER_DIR)/common/huffman.c \
		$(USER_DIR)/common/huffman_table.c

huffman_unittest_DEFINES := \
		USE_HUFFMAN=

rcdevice_unittest_SRC := \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/bitarray.c \
		$(USER_DIR)/fc/rc_modes.c \
		$(USER_DIR)/io/rcdevice.c \
		$(USER_DIR)/io/rcdevice_cam.c \
		$(USER_DIR)/pg/pg.c \

pid_unittest_SRC :=  \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/filter.c \
		$(USER_DIR)/common/maths.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/drivers/accgyro/gyro_sync.c \
		$(USER_DIR)/fc/controlrate_profile.c \
		$(USER_DIR)/fc/runtime_config.c \
		$(USER_DIR)/flight/pid.c \
		$(USER_DIR)/flight/pid_init.c \
		$(USER_DIR)/pg/pg.c

pid_unittest_DEFINES := \
		USE_ITERM_RELAX= \
		USE_RC_SMOOTHING_FILTER= \
		USE_ABSOLUTE_CONTROL= \
		USE_LAUNCH_CONTROL= \
		USE_FEEDFORWARD=

rcdevice_unittest_DEFINES := \
		USE_RCDEVICE=

vtx_unittest_SRC := \
		$(USER_DIR)/fc/core.c \
		$(USER_DIR)/fc/dispatch.c \
		$(USER_DIR)/fc/rc_controls.c \
		$(USER_DIR)/fc/rc_modes.c \
		$(USER_DIR)/fc/runtime_config.c \
		$(USER_DIR)/drivers/vtx_common.c \
		$(USER_DIR)/drivers/vtx_table.c \
		$(USER_DIR)/io/vtx_control.c \
		$(USER_DIR)/io/vtx.c \
		$(USER_DIR)/common/bitarray.c

vtx_unittest_DEFINES := \
		USE_VTX_COMMON= \
		USE_VTX_CONTROL= \
		USE_VTX_SMARTAUDIO=

rx_spi_spektrum_unittest_SRC := \
		$(USER_DIR)/rx/cyrf6936_spektrum.c

rx_spi_spektrum_unittest_DEFINES := \
		USE_RX_SPI= \
		USE_RX_SPEKTRUM=

rx_spi_expresslrs_unittest_SRC := \
        $(USER_DIR)/pg/rx_spi_expresslrs.c \
		$(USER_DIR)/rx/expresslrs_common.c \
		$(USER_DIR)/rx/expresslrs.c \
		$(USER_DIR)/build/atomic.c \

rx_spi_expresslrs_unittest_DEFINES := \
		USE_RX_SPI= \
		USE_RX_EXPRESSLRS= \
		USE_RX_SX1280= \
		USE_RX_SX127X= \

rx_spi_expresslrs_telemetry_unittest_SRC := \
		$(USER_DIR)/rx/crsf.c \
		$(USER_DIR)/telemetry/crsf.c \
		$(USER_DIR)/common/crc.c \
		$(USER_DIR)/common/maths.c \
		$(USER_DIR)/common/streambuf.c \
		$(USER_DIR)/common/gps_conversion.c \
		$(USER_DIR)/common/printf.c \
		$(USER_DIR)/common/typeconversion.c \
		$(USER_DIR)/rx/expresslrs_telemetry.c \
		$(USER_DIR)/build/atomic.c \
		$(USER_DIR)/telemetry/msp_shared.c \

rx_spi_expresslrs_telemetry_unittest_DEFINES := \
		USE_RX_EXPRESSLRS= \
		USE_GPS= \
		USE_MSP_OVER_TELEMETRY= \

# Please tweak the following variable definitions as needed by your
# project, except GTEST_HEADERS, which you can use in your own targets
# but shouldn't modify.

# Points to the root of Google Test, relative to where this file is.
# Remember to tweak this if you move this file.
GTEST_DIR = ../../lib/test/gtest

# Use clang/clang++ by default

CC  := clang-10
CXX := clang++-10
ifeq ($(shell which $(CC) 2>/dev/null),)
$(info Falling back to 'clang'.)
CC  := clang
CXX := clang++
endif

#CC  := gcc
#CXX := g++

# These flags are needed for clang 10 (linux / MacOS) to make test work:
# -Wno-c99-extensions
# -Wno-reorder

COMMON_FLAGS = \
	-g \
	-Wall \
	-Wextra \
	-Werror \
	-Wno-error=unused-command-line-argument \
	-ggdb3 \
	-O0 \
	-DUNIT_TEST \
	-isystem $(GTEST_DIR)/inc \
	-MMD -MP \
	-Wno-c99-extensions \
	-Wno-reorder

CC_VERSION = $(shell $(CC) -dumpversion)
CXX_VERSION = $(shell $(CXX) -dumpversion)

ifeq ($(shell $(CC) -v 2>&1 | grep -q "clang version" && echo "clang"),clang)

# Please revisit versions when new clang version arrive. Supported versions: { Linux / OSX: 7 - 13 }
# Travis reports CC_VERSION of 4.2.1
CC_VERSION_MAJOR := $(firstword $(subst ., ,$(CC_VERSION)))
CC_VERSION_CHECK_MIN := 7
CC_VERSION_CHECK_MAX := 13

# Added flags for clang 11 - 13 are not backwards compatible
ifeq ($(shell expr $(CC_VERSION_MAJOR) \> 10 \& $(CC_VERSION_MAJOR) \< 14), 1)
COMMON_FLAGS += \
	-fcommon \
	-Wno-void-pointer-to-int-cast
endif

ifeq ($(shell expr $(CC_VERSION_MAJOR) \< $(CC_VERSION_CHECK_MIN) \| $(CC_VERSION_MAJOR) \> $(CC_VERSION_CHECK_MAX)),1)
$(error $(CC) $(CC_VERSION) is not supported. The officially supported version of clang is 'clang-10'. If this is not found, 'clang' is used as a fallback. The version of the compiler must be between $(CC_VERSION_CHECK_MIN) and $(CC_VERSION_CHECK_MAX).)
endif

COMMON_FLAGS += -fblocks
ifndef CYGWIN
ifneq ($(OSFAMILY), macosx)
LDFLAGS	     += -lBlocksRuntime
endif
endif
endif

$(info CC version: $(shell $(CC) --version))
$(info CXX version: $(shell $(CXX) --version))

USE_PTHREAD = YES
USE_COVERAGE = YES
ifeq ($(OSFAMILY), macosx)
	USE_PTHREAD =
endif
ifdef CYGWIN
	USE_PTHREAD =
	USE_COVERAGE =
endif

ifdef USE_PTHREAD
COMMON_FLAGS += -pthread
endif

# Flags passed to the C compiler.
C_FLAGS = $(COMMON_FLAGS) \
	-std=gnu99

# Flags passed to the C++ compiler.
CXX_FLAGS = $(COMMON_FLAGS) \
	-std=gnu++11

# Compiler flags for coverage instrumentation
ifdef USE_COVERAGE
COVERAGE_FLAGS := --coverage
endif

C_FLAGS   += $(COVERAGE_FLAGS)
CXX_FLAGS += $(COVERAGE_FLAGS)

C_FLAGS   += -D_GNU_SOURCE

# Set up the parameter group linker flags according to OS
ifeq ($(OSFAMILY), macosx)
LDFLAGS  += -Wl,-map,$(OBJECT_DIR)/$@.map
else
LDFLAGS  += -Wl,-T,$(TEST_DIR)/pg.ld -Wl,-Map,$(OBJECT_DIR)/$@.map
endif

# Gather up all of the tests.
TEST_SRCS = $(sort $(wildcard $(TEST_DIR)/*.cc))
TEST_BASENAMES = $(TEST_SRCS:$(TEST_DIR)/%.cc=%)
TESTS_TARGET_SPECIFIC = $(foreach test,$(TEST_BASENAMES),$(if $($(test)_EXPAND),$(test)))
TESTS_TARGET_SPECIFIC_EXPANDED = $(foreach test,$(TESTS_TARGET_SPECIFIC),$(foreach \
		target,$(filter-out $($(test)_BLACKLIST),$(VALID_TARGETS)),$(test).$(target)))

TESTS = $(foreach test,$(TEST_BASENAMES),$(if $($(test)_EXPAND),,$(test)))
TESTS_ALL = $(TESTS) $(TESTS_TARGET_SPECIFIC_EXPANDED)
TESTS_REPRESENTATIVE = $(TESTS) $(foreach test,$(TESTS_TARGET_SPECIFIC), \
		$(test).$(word 1,$(filter-out $($(test)_BLACKLIST),$(VALID_TARGETS))))

# All Google Test headers.  Usually you shouldn't change this
# definition.
GTEST_HEADERS = $(GTEST_DIR)/inc/gtest/*.h

## V                 : Set verbosity level based on the V= parameter
##                     V=0 Low
##                     V=1 High
include ../../make/build_verbosity.mk

# House-keeping build targets.

## test        : Build and run the non target specific Unit Tests (default goal)
test: $(TESTS:%=test_%)

## test-all : Build and run all Unit Tests
test-all: $(TESTS_ALL:%=test_%)

## test-representative : Build and run a representative subset of the Unit Tests (i.e. run every expanded test only for the first target)
test-representative: $(TESTS_REPRESENTATIVE:%=test_%)

## junittest   : Build and run the Unit Tests, producing Junit XML result files."
junittest: EXEC_OPTS = "--gtest_output=xml:$<_results.xml"
junittest: $(TESTS:%=test_%)



## help        : print this help message and exit
## what        : print this help message and exit
## usage       : print this help message and exit
help what usage: Makefile
	@echo ""
	@echo "Makefile for Unit Tests"
	@echo ""
	@echo "Usage:"
	@echo "        make [goal] "
	@echo ""
	@echo "Valid goals are:"
	@echo ""
	@sed -n 's/^## //p' $<
	@echo ""
	@echo "Any of the Unit Test programs can be used as goals to build:"
	@$(foreach test, $(TESTS), echo "    $(OBJECT_DIR)/$(test)/$(test)";)
	@echo ""
	@echo "Any of the Unit Test programs (except for target specific unit tests) can be used as goals to build and run:"
	@$(foreach test, $(TESTS), echo "    test_$(test)";)

versions:
	@echo "C compiler: $(CC): $(CC_VERSION)"
	@echo "C++ compiler: $(CXX): $(CXX_VERSION)"

## clean       : Cleanup the UnitTest binaries.
clean :
	rm -rf $(OBJECT_DIR)


# Builds gtest.a and gtest_main.a.

# Usually you shouldn't tweak such internal variables, indicated by a
# trailing _.
GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/inc/gtest/*.h $(GTEST_HEADERS)

# For simplicity and to avoid depending on Google Test's
# implementation details, the dependencies specified below are
# conservative and not optimized.  This is fine as Google Test
# compiles fast and for ordinary users its source rarely changes.
$(OBJECT_DIR)/gtest-all.o : $(GTEST_SRCS_)
	@echo "compiling $@" "$(STDOUT)"
	@mkdir -p $(dir $@)
	$(V1) $(CXX) $(CXX_FLAGS) -I$(GTEST_DIR) -Wno-missing-field-initializers -Wno-unused-const-variable -c \
            $(GTEST_DIR)/src/gtest-all.cc -o $@

$(OBJECT_DIR)/gtest_main.o : $(GTEST_SRCS_)
	@echo "compiling $@" "$(STDOUT)"
	@mkdir -p $(dir $@)
	$(V1) $(CXX) $(CXX_FLAGS) -I$(GTEST_DIR) -c \
            $(GTEST_DIR)/src/gtest_main.cc -o $@

$(OBJECT_DIR)/gtest.a : $(OBJECT_DIR)/gtest-all.o
	@echo "linking $@" "$(STDOUT)"
	$(V1) $(AR) $(ARFLAGS) $@ $^

$(OBJECT_DIR)/gtest_main.a : $(OBJECT_DIR)/gtest-all.o $(OBJECT_DIR)/gtest_main.o
	@echo "linking $@" "$(STDOUT)"
	$(V1) $(AR) $(ARFLAGS) $@ $^

-include $(OBJECT_DIR)/gtest-all.d \
         $(OBJECT_DIR)/gtest_main.d


# includes in test dir must override includes in user dir, unless the user
# specifies a list of endorsed directories in ${target}_INCLUDE_DIRS.
test_include_dirs  = $1 $(TEST_DIR) $(USER_DIR)
test_cflags	   = $(addprefix -I,$(call test_include_dirs,$1))


# target name extractor
# param $1 = expanded test name in the form of test.target
target = $(1:$(basename $1).%=%)

# canned recipe for all test builds
#
# variable expansion rules of thumb (number of $'s):
# * parameters: one $, e.g. $1
# * statically accessed variables set elsewhere: one $, e.g. $(C_FLAGS)
# * dynamically accessed variables set elsewhere: one $, e.g. $($1_SRC)
# * make functions accessing only the above: one $, e.g. $(basename $1)
# * dynamically set and accessed variables: two $, e.g. $$($1_OBJS)
# * make functions accessing dynamically set variables: two $,
#   e.g. $$(call test_cflags,$$($1_INCLUDE_DIRS))
#
# param $1 = plain test name for global tests, test.target for per-target tests
define test-specific-stuff

ifeq ($1,$(basename $1))
# standard global test
$1_OBJS = $(patsubst \
	$(TEST_DIR)/%,$(OBJECT_DIR)/$1/%,$(patsubst \
	$(USER_DIR)/%,$(OBJECT_DIR)/$1/%,$($1_SRC:=.o)))
else
# test executed for each target, $1 has the form of test.target
$1_SRC = $(addsuffix .o,$(call $(basename $1)_SRC,$(call target,$1)))
$1_OBJS = $$(patsubst \
	$(TEST_DIR)/%,$(OBJECT_DIR)/$1/%,$$(patsubst \
	$(USER_DIR)/%,$(OBJECT_DIR)/$1/%,$$(patsubst \
	$(TARGET_DIR)/$(call get_base_target,$(call target,$1))/%,$(OBJECT_DIR)/$1/%,$$($1_SRC))))
$1_DEFINES = $(call $(basename $1)_DEFINES,$(call target,$1))
$1_INCLUDE_DIRS = $(call $(basename $1)_INCLUDE_DIRS,$(call target,$1))
endif

# $$(info $1 -v-v-------)
# $$(info $1_SRC:  $$($1_SRC))
# $$(info $1_OBJS: $$($1_OBJS))
# $$(info $1 -^-^-------)

# include generated dependencies
-include $$($1_OBJS:.o=.d)
-include $(OBJECT_DIR)/$1/$(basename $1).d

$(OBJECT_DIR)/$1/%.c.o: $(USER_DIR)/%.c
	@echo "compiling $$<" "$(STDOUT)"
	$(V1) mkdir -p $$(dir $$@)
	$(V1) $(CC) $(C_FLAGS) $$(call test_cflags,$$($1_INCLUDE_DIRS)) \
                $$(foreach def,$$($1_DEFINES),-D $$(def)) \
                -c $$< -o $$@

$(OBJECT_DIR)/$1/%.c.o: $(TEST_DIR)/%.c
	@echo "compiling test c file: $$<" "$(STDOUT)"
	$(V1) mkdir -p $$(dir $$@)
	$(V1) $(CC) $(C_FLAGS) $$(call test_cflags,$$($1_INCLUDE_DIRS)) \
                $$(foreach def,$$($1_DEFINES),-D $$(def)) \
                -c $$< -o $$@

ifneq ($1,$(basename $1))
# per-target tests may compile files from the target directory
$(OBJECT_DIR)/$1/%.c.o: $(TARGET_DIR)/$(call get_base_target,$(call target,$1))/%.c
	@echo "compiling target c file: $$<" "$(STDOUT)"
	$(V1) mkdir -p $$(dir $$@)
	$(V1) $(CC) $(C_FLAGS) $$(call test_cflags,$$($1_INCLUDE_DIRS)) \
                $$(foreach def,$$($1_DEFINES),-D $$(def)) \
                -c $$< -o $$@
endif

$(OBJECT_DIR)/$1/$(basename $1).o: $(TEST_DIR)/$(basename $1).cc
	@echo "compiling $$<" "$(STDOUT)"
	$(V1) mkdir -p $$(dir $$@)
	$(V1) $(CXX) $(CXX_FLAGS) $$(call test_cflags,$$($1_INCLUDE_DIRS)) \
                $$(foreach def,$$($1_DEFINES),-D $$(def)) \
                -c $$< -o $$@

$(OBJECT_DIR)/$1/$(basename $1): $$($1_OBJS) \
	$(OBJECT_DIR)/$1/$(basename $1).o \
	$(OBJECT_DIR)/gtest_main.a

	@echo "linking $$@" "$(STDOUT)"
	$(V1) mkdir -p $(dir $$@)
	$(V1) $(CXX) $(CXX_FLAGS) $(LDFLAGS) $$^ -o $$@

test_$1: $(OBJECT_DIR)/$1/$(basename $1)
	$(V1) $$< $$(EXEC_OPTS) "$(STDOUT)" && echo "running $$@: PASS"

endef


ifeq ($(MAKECMDGOALS),test-all)
    $(eval $(foreach test,$(TESTS_ALL),$(call test-specific-stuff,$(test))))
else
    ifeq ($(MAKECMDGOALS),test-representative)
        $(eval $(foreach test,$(TESTS_REPRESENTATIVE),$(call test-specific-stuff,$(test))))
    else
        $(eval $(foreach test,$(TESTS),$(call test-specific-stuff,$(test))))
    endif
endif

$(foreach test,$(TESTS_ALL),$(if $($(basename $(test))_SRC),,$(error \
	Test 'unit/$(basename $(test)).cc' has no '$(basename $(test))_SRC' variable defined)))
$(foreach var,$(filter-out TARGET_SRC,$(filter %_SRC,$(.VARIABLES))),$(if $(filter $(var:_SRC=)%,$(TESTS_ALL)),,$(error \
	Variable '$(var)' has no 'unit/$(var:_SRC=).cc' test)))


target_list:
	@echo ========== BASE TARGETS ==========
	@echo $(BASE_TARGETS)
	@echo ========== ALT TARGETS ==========
	@echo $(ALT_TARGETS)
	@echo ========== VALID_TARGETS ==========
	@echo $(VALID_TARGETS)
	@echo ========== BASE/ALT PAIRS ==========
	@echo $(BASE_ALT_PAIRS)
	@echo ========== ALT/BASE MAPPING ==========
	@echo $(foreach target,$(ALT_TARGETS),$(target)\>$(call get_base_target,$(target)))
	@echo ========== ALT/BASE FULL MAPPING ==========
	@echo $(foreach target,$(VALID_TARGETS),$(target)\>$(call get_base_target,$(target)))
