###
# GNU ARM Embedded Toolchain
CC=arm-none-eabi-gcc
LD=arm-none-eabi-ld
AR=arm-none-eabi-ar
AS=arm-none-eabi-as
CP=arm-none-eabi-objcopy
OD=arm-none-eabi-objdump
SIZE=arm-none-eabi-size

###
# Directory Structure
BINDIR=bin
SRCDIR=.

ASOURCES=$(shell find -L $(SRCDIR) -name '*.s')
CSOURCES+=$(shell find -L $(SRCDIR) -name '*.c')
HEADERS=$(shell find -L $(SRCDIR) -name '*.h')

COMMON_DIR=../common
COMMON_SOURCE_LIST=$(shell cat ../common/sourcelist.txt)
CSOURCES+=$(COMMON_SOURCE_LIST:%.c=../common/%.c)

INC=$(shell find -L $(SRCDIR) -name '*.h' -exec dirname {} \; | uniq)
INC+=$(COMMON_DIR)/includes
INCLUDES=$(INC:%=-I%)

# Create object list
OBJECTS=$(CSOURCES:%.c=obj/%.o)
OBJECTS+=$(ASOURCES:%.s=obj/%.o)

# Define output files ELF & IHEX
BINELF=outp.elf
BINHEX=outp.hex

###
# MCU FLAGS
MCFLAGS=-mcpu=cortex-m4 -mthumb -mlittle-endian \
	-mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb-interwork \
	-include stm32f4xx.h
# COMPILE FLAGS
DEFS=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DARM_MATH_CM4

CWARNS += -Wextra
CWARNS += -Wformat
CWARNS += -Wmissing-braces
CWARNS += -Wno-cast-align
CWARNS += -Wparentheses
CWARNS += -Wshadow
CWARNS += -Wno-sign-compare
CWARNS += -Wswitch
CWARNS += -Wuninitialized
CWARNS += -Wunknown-pragmas
CWARNS += -Wunused-function
CWARNS += -Wunused-label
CWARNS += -Wunused-parameter
CWARNS += -Wunused-value
CWARNS += -Wunused-variable
CWARNS += -Wmissing-prototypes

CFLAGS  =-Wall $(CWARNS) -ggdb -std=c99 -c $(MCFLAGS) $(DEFS) $(INCLUDES)
# LINKER FLAGS
LDSCRIPT= $(SRCDIR)/bsp/stm32_flash.ld
LDFLAGS =-T $(LDSCRIPT) --specs=nosys.specs $(MCFLAGS) -Wl,-Map=$(BINDIR)/outp.map

###
# Optimizations
OPT?='O2 O3 O6'
# O1 and O4 are irrelevant
# O5 breaks FreeRTOS somehow
# I'm not trusting O7

ifneq ($(filter O1,$(OPT)),)
CXXFLAGS+=-fno-exceptions # Uncomment to disable exception handling
DEFS+=-DNO_EXCEPTIONS # The source code has to comply with this rule
endif

ifneq ($(filter O2,$(OPT)),)
CFLAGS+=-Os # Optimize for size https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
CXXFLAGS+=-Os
LDFLAGS+=-Os # Optimize for size https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
endif

ifneq ($(filter O3,$(OPT)),)
CFLAGS+=-ffunction-sections -fdata-sections # Place each function or data item into its own section in the output file
CXXFLAGS+=-ffunction-sections -fdata-sections # -||-
LDFLAGS+=-Wl,-gc-sections # Remove isolated unused sections
endif

ifneq ($(filter O4,$(OPT)),)
CFLAGS+=-fno-builtin # Disable C++ exception handling
CXXFLAGS+=-fno-builtin # Disable C++ exception handling
endif

ifneq ($(filter O5,$(OPT)),)
CFLAGS+=-flto # Enable link time optimization
CXXFLAGS+=-flto # Enable link time optimization
LDFLAGS+=-flto # Enable link time optimization
endif

ifneq ($(filter O6,$(OPT)),)
CXXFLAGS+=-fno-rtti # Disable type introspection
endif

ifneq ($(findstring O7,$(OPT)),)
LDFLAGS+=--specs=nano.specs # Use size optimized newlib
endif

###
# Build Rules
.PHONY: all release debug clean

all: release

release: $(BINDIR)/$(BINHEX)

debug: CFLAGS+=-g
debug: LDFLAGS+=-g
debug: release


$(BINDIR)/$(BINHEX): $(BINDIR)/$(BINELF)
	@$(CP) -O ihex $< $@
	@echo "[CP] $@"
	@echo "[:)] Happiness :)"

$(BINDIR)/$(BINELF): vc.h $(OBJECTS)
	@$(CC) $(LDFLAGS) $(OBJECTS) -o $@ -lm
	@echo "[CC] $@"
	@$(SIZE) $(BINDIR)/$(BINELF)

dir_guard=@mkdir -p $(@D)

obj/%.o: %.c $(HEADERS)
	$(dir_guard)
	@echo "[CC] $<"
	@$(CC) $(CFLAGS) $< -o $@

obj/%.o: %.s $(HEADERS)
	$(dir_guard)
	@echo [AS] $<
	@$(CC) $(CFLAGS) $< -o $@

vc.h: ../../.git/logs/HEAD
	@echo "// This file is generated by Makefile." > vc.h
	@echo "// Do not edit this file!" >> vc.h
	@git log -1 --format="format:#define GIT_VERSION \"%h\"" >> vc.h
	@echo >> vc.h
	@echo >> vc.h
	@echo [EC] vc.h

clean:
	@rm -f $(OBJECTS) $(BINDIR)/$(BINELF) $(BINDIR)/$(BINHEX)
	@echo "[RM] Cleanuped °o°"

# Connect to openocd's gdb server on port 3333
deploy: $(BINDIR)/$(BINELF)
ifeq ($(wildcard /opt/openocd/bin/openocd),)
	/usr/bin/openocd -f /usr/share/openocd/scripts/board/stm32f4discovery.cfg -c "program bin/"$(BINELF)" verify reset" -c "init" -c "reset" -c "exit"
else
	/opt/openocd/bin/openocd -f /opt/openocd/share/openocd/scripts/board/stm32f4discovery.cfg -c "program bin/"$(BINELF)" verify reset" -c "init" -c "reset" -c "exit"
endif