#   Zerocat Chipflasher --- Flash free firmware, kick the Management Engine.
#
#   Copyright (C) 2015, 2016  kai <kmx@posteo.net>
#   Copyright (C) 2015, 2016  tomás zerolo <tomás@tuxteam.de>
#   Copyright (C) 2016, 2017, 2018, 2020, 2021, 2022, 2023  Kai Mertens <kmx@posteo.net>
#
#   This file is part of Zerocat Chipflasher.
#
#   Zerocat Chipflasher is free software: you can redistribute it and/or
#   modify it under the terms of the GNU General Public License as
#   published by the Free Software Foundation, either version 3 of the
#   License, or (at your option) any later version.
#
#   Zerocat Chipflasher is distributed in the hope that it will be
#   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
#   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
#   General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with Zerocat Chipflasher.
#   If not, see <http://www.gnu.org/licenses/>.


#   Documentation
#   =============
#
#
#   Diagnostic Pragmas
#   ------------------
#
#
#   NOTE Propeller code allows some non ANSI standard format specifiers,
#   i.e. "%b" for binary representation of an integer.
#   That will produce warnings according to -Wformat and -Wformat-extra-args.
#
#   You can temporarily disable these warnings in your source code:
#
#       [...]
#       #pragma GCC diagnostic push
#       #pragma GCC diagnostic ignored "-Wall"
#       #pragma GCC diagnostic ignored "-Wformat-extra-args"
#       //your code goes here, i.e.:
#       //printi("binary number: %08b", x);
#       #pragma GCC diagnostic pop
#       [...]
#
#   Please compare to:
#   https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Pragmas.html#Diagnostic-Pragmas)
#
#
#   Usage
#   -----
#
#
#       $ make help
#
#
#   FIXME
#   -----
#
#
#   A patern rule would be nice. But where to stick the -lm -lfdserial and so on?
#   NOTE lib order matters (-lfoo -lbar). And $^ must go before libs.


#   Workaround for GNU Make < 4.3
#   =============================
#
#   Finalize commands within a $(shell) function with semicolon, e.g.:
#
#           $(shell echo;)


# Shell
SHELL                         := /bin/sh


# White Space
EMPTY                         :=
SPACE                         := $(EMPTY) $(EMPTY)
COMMA                         := ,
M5                            := uuuuu
TAB1                          := \r\t\t\t
TOKEN_DISABLE                 := $(EMPTY)"\/\/\# \t "$(EMPTY)
TOKEN_ENABLE                  := $(EMPTY)"\# \t "$(EMPTY)


# Suffixes
.SUFFIXES :
.SUFFIXES : .c .h .elf


# Root Paths
ROOT_FW1_SRC                  := ../../firmware1/src/
ROOT_DOC                      := ../../doc/


# Standard Utilities
WHEREIS                       := $(shell command -v whereis;)
WHEREIS_FLAGS                 := -b
$(if $(strip $(WHEREIS)),,    $(error Cannot find whereis utility))

CD                            := $(shell command -v cd;)
CD_FLAGS                      :=
$(if $(strip $(CD)),,         $(error Cannot find cd utility))

CP                            := $(shell command -v cp;)
CP_FLAGS                      :=
$(if $(strip $(CP)),,         $(error Cannot find cp utility))

RM                            := $(shell command -v rm;)
RM_FLAGS                      := -f
$(if $(strip $(RM)),,         $(error Cannot find rm utility))

MV                            := $(shell command -v mv;)
MV_FLAGS                      :=
$(if $(strip $(MV)),,         $(error Cannot find mv utility))

CAT                           := $(shell command -v cat;)
CAT_FLAGS                     :=
$(if $(strip $(CAT)),,        $(error Cannot find cat utility))

CUT                           := $(shell command -v cut;)
CUT_FLAGS                     := -d' '
$(if $(strip $(CUT)),,        $(error Cannot find cut utility))

GIT                           := $(shell command -v git;)
GIT_FLAGS                     :=
$(if $(strip $(GIT)),,        $(error Cannot find git utility))

SED                           := $(shell command -v sed;)
SED_FLAGS                     := -r
$(if $(strip $(SED)),,        $(error Cannot find sed utility))

SET                           := $(shell command -v set;)
SET_FLAGS                     := -f
$(if $(strip $(SET)),,        $(error Cannot find set utility))

ECHO                          := $(shell $(WHEREIS) $(WHEREIS_FLAGS) echo | $(CUT) $(CUT_FLAGS) -f2;)
ECHO_FLAGS                    := -e
$(if $(strip $(ECHO)),,       $(error Cannot find echo utility))

READ                          := $(shell command -v read;)
READ_FLAGS                    :=
$(if $(strip $(READ)),,       $(error Cannot find read utility))

#   Note ‘mkdir -p’ might not be supported.
MKDIR                         := $(shell command -v mkdir;)
MKDIR_FLAGS                   :=
$(if $(strip $(MKDIR)),,      $(error Cannot find mkdir utility))

MKTEMP                        := $(shell command -v mktemp;)
MKTEMP_FLAGS                  :=
$(if $(strip $(MKTEMP)),,     $(error Cannot find mktemp utility))


# Extra Utilities
PROPELFGCC                    := $(shell command -v propeller-elf-gcc;)
PROPELFGCC_SYSROOT            = $(strip\
	--sysroot=$(PARALLAX_REPO)/\
)
PROPELFGCC_INCLUDES           := $(strip\
	-I .\
	-I =Learn/Simple\ Libraries/TextDevices/libsimpletext\
	-I =Learn/Simple\ Libraries/Utility/libsimpletools\
	-I =Learn/Simple\ Libraries/Protocol/libsimplei2c\
)
PROPELFGCC_LIBDIRS_lmm        := $(strip\
	-L .\
	-L =Learn/Simple\ Libraries/TextDevices/libsimpletext/lmm\
	-L =Learn/Simple\ Libraries/Utility/libsimpletools/lmm\
	-L =Learn/Simple\ Libraries/Protocol/libsimplei2c/lmm\
)
PROPELFGCC_LIBDIRS_cmm        := $(strip\
	-L .\
	-L =Learn/Simple\ Libraries/TextDevices/libsimpletext/cmm\
	-L =Learn/Simple\ Libraries/Utility/libsimpletools/cmm\
	-L =Learn/Simple\ Libraries/Protocol/libsimplei2c/cmm\
)
PROPELFGCC_LIBS               := $(strip\
	-lsimpletext\
	-lsimpletools\
	-lsimplei2c\
)
PROPELFGCC_FLAGS              = $(strip\
	$(PROPELFGCC_SYSROOT)\
	$(PROPELFGCC_INCLUDES)\
	$(PROPELFGCC_LIBDIRS_$*)\
	-Os -Wall -Wmissing-prototypes -m32bit-doubles -fno-exceptions -std=c99 -m$*\
)
$(if $(strip $(PROPELFGCC)),, $(error Cannot find propeller-elf-gcc utility))


# Settings
PROJECT_TITLE                 := Zerocat Chipflasher
PROJECT_BRIEF                 := Flash free firmware, kick the Management Engine.
PROJECT_NUMBER                := $(strip\
	$(shell\
		$(SET) $(SET_FLAGS)\
		&& $(GIT) $(GIT_FLAGS)\
			describe\
				--long\
				--always\
				--match v*\
		| $(SED) $(SED_FLAGS)\
			-e 's/(.*)(-g)([0-9a-f]*)/\1-\3/;'\
				-;\
	)\
)
MAKEFILE_TITLE                := Build ‘kick’, the first firmware
PARALLAX_REMOTE               := https://github.com/
PARALLAX_REPO                 := parallaxinc/Simple-Libraries
PARALLAX_REPO_VERSION         := c4f9a3e273002ec5e6f8b1d1ab95c14cb1823e82


# Files
LIBPROP                       := $(wildcard libprop/*)
LIBCOMMON                     := $(wildcard libcommon/*)
LIBSPI                        := $(wildcard libSPI/*)
LIBKICK                       := $(wildcard libkick/*)
H_PINS                        := $(ROOT_FW1_SRC)libkick/proppins.h


# Tinned Cans
define HEADER                 =
$(strip\
	$(ECHO) $(ECHO_FLAGS)\
		"\n$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)"\
		"\n$(M5) $(PROJECT_TITLE)"\
		"\n$(M5) – $(PROJECT_BRIEF)"\
		"\n$(M5) $(MAKEFILE_TITLE)"\
		"\n$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)$(M5)"\
)
endef
define HEADLINE               =
$(strip\
	$(ECHO) $(ECHO_FLAGS)\
		"$(M5) Process target “$@” $(M5)$(M5)"\
)
endef
define DONE                   =
$(strip\
	$(ECHO) $(ECHO_FLAGS)\
		"... successfully processed: “$@”"\
		"\n"\
)
endef


# Built-in Special Targets
#~ .SECONDARY :                  # Uncomment for debugging
.PHONY :\
	all\
	\
	hello\
	help\
	print-search-dirs\
	\
	setup-lib-parallaxinc\
	config-BOARD_V2\
	config-BOARD_V1\
	\
	kick\
	\
	remove-lib-parallaxinc\
	clean-config\
	clean


# Targets
#   all: same as kick
all : hello kick
	@$(HEADLINE)\
	&& $(DONE);

#   Say hello.
#     provided with all, clean and help
hello :
	@$(HEADER)\
	&& $(DONE);

#   display help information
help : hello
	@$(HEADLINE)\
	&& $(ECHO) $(ECHO_FLAGS)\
		"\nList of Targets"\
		"\n==============="\
		"\n"\
		"\nDefault"\
		"\n-------"\
		"\n"\
		"\nall$(TAB1)same as kick, with hello greeter"\
		"\n"\
		"\nHelp"\
		"\n----"\
		"\n"\
		"\nhello$(TAB1)greeter, provided with all, clean and help"\
		"\nhelp$(TAB1)display this help information"\
		"\nprint-search-dirs$(TAB1)print Propeller-Elf-GCC search directories"\
		"\n"\
		"\nConfigure"\
		"\n---------"\
		"\n"\
		"\nsetup-lib-parallaxinc$(TAB1)download Parallax Library Repo and Checkout"\
		"\nconfig-BOARD_V2$(TAB1)use board v2, default, e.g.: Chipflasher v2"\
		"\nconfig-BOARD_V1$(TAB1)use board v1, e.g.: Chipflasher board-edition-1"\
		"\n"\
		"\nCompile"\
		"\n-------"\
		"\n"\
		"\nkick$(TAB1)build ‘kick’, the first firmware, with its interface to ‘connect’"\
		"\n"\
		"\nClean"\
		"\n-----"\
		"\n"\
		"\nremove-lib-parallaxinc$(TAB1)remove Parallax Library Repo"\
		"\nclean-config$(TAB1)clean ‘kick’ configuration"\
		"\nclean$(TAB1)make files and folders clean"\
		"\n"\
	&& $(DONE);

#   print search directories
print-search-dirs :
	@$(HEADLINE)\
	&& $(PROPELFGCC)\
		-print-search-dirs\
	&& $(DONE);

#   set up parallaxinc repo clone
setup-lib-parallaxinc : $(PARALLAX_REPO)
	@$(HEADLINE)\
	&& $(DONE);

config-BOARD_V2 : $(H_PINS)
	@$(HEADLINE)\
	&& $(SED) $(SED_FLAGS)\
		--in-place\
		-e "s/^("$(TOKEN_ENABLE)")(.*define.*_CFG_BOARD_V1)/"$(TOKEN_DISABLE)"\2/;"\
		-e "s/^("$(TOKEN_DISABLE)")(.*define.*_CFG_BOARD_V2)/"$(TOKEN_ENABLE)"\2/;"\
			$<\
	&& $(DONE);

config-BOARD_V1 : $(H_PINS)
	@$(HEADLINE)\
	&& $(SED) $(SED_FLAGS)\
		--in-place\
		-e "s/^("$(TOKEN_ENABLE)")(.*define.*_CFG_BOARD_V2)/"$(TOKEN_DISABLE)"\2/;"\
		-e "s/^("$(TOKEN_DISABLE)")(.*define.*_CFG_BOARD_V1)/"$(TOKEN_ENABLE)"\2/;"\
			$<\
	&& $(DONE);

#   target ‘kick’
#     Memory model
#     lmm: stores the program image and variable data in Main RAM, fastest execution
#     cmm: the program image is compiled into a size-optimized form
#     xmmc: stores program images in external flash memory, an SD card, or EEPROM.
kick : kick.lmm.elf kick.cmm.elf
	@$(HEADLINE)\
	&& $(DONE);

$(PARALLAX_REPO) :
	@$(HEADLINE)\
	&& $(READ) $(READ_FLAGS)\
		-p 'Ready to start download from ‘$(PARALLAX_REMOTE)’? [yes|no]: '\
			reply\
	&& if [ "$$reply" = 'yes' ];\
		then\
			$(MKDIR) $(MKDIR_FLAGS)\
				$(dir $@)\
			&& $(CD) $(CD_FLAGS)\
				$(dir $@)\
			&& $(GIT) $(GIT_FLAGS)\
				clone $(PARALLAX_REMOTE)$@.git\
			&& if [ $$? -eq 0 ];\
				then\
					$(CD) $(CD_FLAGS)\
						$(@F)/\
					&& $(GIT) $(GIT_FLAGS)\
						checkout $(PARALLAX_REPO_VERSION)\
					&& $(CD) $(CD_FLAGS)\
						-;\
				else\
					exit 1;\
				fi;\
		else\
			$(ECHO) $(ECHO_FLAGS)\
				"Download cancelled by user."\
			&& exit 1;\
		fi\
	&& $(DONE);

#   build kick.elf file
kick.%.elf : kick.c kick.h $(LIBPROP) $(LIBCOMMON) $(LIBSPI) $(LIBKICK) setup-lib-parallaxinc
	@$(HEADLINE)\
	&& tmpfile=`$(MKTEMP) $(MKTEMP_FLAGS)`\
	&& $(CP) $(CP_FLAGS)\
		libcommon/common.h $$tmpfile\
	&& $(SED) $(SED_FLAGS)\
		--in-place\
		-e 's/<VERSION>/$(PROJECT_NUMBER)/g;'\
			libcommon/common.h\
	&& $(PROPELFGCC) $(PROPELFGCC_FLAGS)\
		$<\
		$(PROPELFGCC_LIBS)\
			-o $@\
	&& $(MV) $(MV_FLAGS)\
		$$tmpfile libcommon/common.h\
	&& $(DONE);

#   clean (remove) parallaxinc folder
remove-lib-parallaxinc :
	@$(HEADLINE)\
	&& $(RM) $(RM_FLAGS) -r\
		$(dir $(PARALLAX_REPO))\
	&& $(DONE);

#   clean kick configuration
clean-config : config-BOARD_V2
	@$(HEADLINE)\
	&& $(DONE);

#   clean kick
clean : hello clean-config
	@$(HEADLINE)\
	&& $(RM) $(RM_FLAGS)\
		kick.lmm.elf\
		kick.cmm.elf\
	&& $(DONE);
