# The C compiler
BFDHOME=/usr

MAKE_STATIC=
ARCH_TYPE=
ADDITIONAL_FLAGS=
SLEIGHVERSION=sleigh-2.1.0

EXTENSION_POINT=../../../../../../../ghidra.ext-u/Ghidra/Features/DecompilerExtensions/src/decompile/cpp
GHIDRA_BIN=../../../../../../../ghidra.bin

OS = $(shell uname -s)
CPU = $(shell uname -m)

# TODO: need to revise to support arm64/aarch64 arch - improve on both OS and arch detection 

ifeq ($(OS),Linux)
# Allow ARCH to be specified externally so we can build for 32-bit from a 64-bit Linux
ifndef ARCH
  ARCH=$(CPU)
endif
ifeq ($(ARCH),x86_64)
  ARCH_TYPE=-m64
  OSDIR=linux_x86_64
else
  ARCH_TYPE=-m32
  OSDIR=linux_x86_32
endif
endif

ifeq ($(OS),Darwin)
  MAKE_STATIC=
  ARCH_TYPE=-arch x86_64
  ADDITIONAL_FLAGS=-mmacosx-version-min=10.6 -w
  OSDIR=mac_x86_64
endif

ifeq ($(OS),OpenBSD)
ifeq ($(ARCH),amd64)
  ARCH_TYPE=-m64
  OSDIR=openbsd_x86_64
endif
endif

CXX=g++ -std=c++11

# Debug flags
DBG_CXXFLAGS=-g -Wall -Wno-sign-compare
#DBG_CXXFLAGS=-g -pg -Wall -Wno-sign-compare
#DBG_CXXFLAGS=-g -fprofile-arcs -ftest-coverage -Wall -Wno-sign-compare

# Optimization flags
OPT_CXXFLAGS=-O2 -Wall -Wno-sign-compare

YACC=bison

# libraries
#INCLUDES=-I$(BFDHOME)/include
INCLUDES=
BFDLIB=-lbfd

LNK=-lz

.PHONY: all
all: ghidra_opt decomp_opt sleigh_opt

# Source files
ALL_SOURCE= $(wildcard *.cc)
ALL_NAMES=$(subst .cc,,$(ALL_SOURCE))
UNITTEST_SOURCE= $(wildcard ../unittests/*.cc)
UNITTEST_NAMES=$(subst .cc,,$(UNITTEST_SOURCE))
UNITTEST_STRIP=$(subst ../unittests/,,$(UNITTEST_NAMES))

COREEXT_SOURCE= $(wildcard coreext_*.cc)
COREEXT_NAMES=$(subst .cc,,$(COREEXT_SOURCE))

GHIDRAEXT_SOURCE= $(wildcard ghidraext_*.cc)
GHIDRAEXT_NAMES=$(subst .cc,,$(GHIDRAEXT_SOURCE))

EXTERNAL_COREEXT_SOURCE= $(wildcard $(EXTENSION_POINT)/coreext_*.cc)
EXTERNAL_GHIDRAEXT_SOURCE= $(wildcard $(EXTENSION_POINT)/ghidraext_*.cc)
EXTERNAL_CONSOLEEXT_SOURCE= $(wildcard $(EXTENSION_POINT)/consoleext_*.cc)
EXTERNAL_COREEXT_NAMES=$(subst .cc,,$(notdir $(EXTERNAL_COREEXT_SOURCE)))
EXTERNAL_GHIDRAEXT_NAMES=$(subst .cc,,$(notdir $(EXTERNAL_GHIDRAEXT_SOURCE)))
EXTERNAL_CONSOLEEXT_NAMES=$(subst .cc,,$(notdir $(EXTERNAL_CONSOLEEXT_SOURCE)))

# The following macros partition all the source files, there should be no overlaps
# Some core source files used in all projects
CORE=	xml marshal space float address pcoderaw translate opcodes metatype globalcontext
# Additional core files for any projects that decompile
DECCORE=capability architecture options graph cover block cast typeop database cpool \
	comment stringmanage modelrules fspec action loadimage grammar varnode op type \
	variable varmap jumptable emulate emulateutil flow userop expression multiprecision \
	funcdata funcdata_block funcdata_op funcdata_varnode unionresolve pcodeinject \
	heritage prefersplit rangeutil ruleaction subflow blockaction merge double \
	transform constseq bitfield coreaction condexe override dynamic crc32 prettyprint \
	printlanguage printc printjava memstate opbehavior paramid signature $(COREEXT_NAMES)
# Files used for any project that use the sleigh decoder
SLEIGH=	sleigh pcodeparse pcodecompile sleighbase slghsymbol \
	slghpatexpress slghpattern semantics context slaformat compression filemanage
# Additional files for the GHIDRA specific build
GHIDRA=	ghidra_arch inject_ghidra ghidra_translate loadimage_ghidra \
	typegrp_ghidra database_ghidra ghidra_context cpool_ghidra \
	ghidra_process comment_ghidra string_ghidra signature_ghidra $(GHIDRAEXT_NAMES)
# Additional files specific to the sleigh compiler
SLACOMP=slgh_compile slghparse slghscan
# Additional special files that should not be considered part of the library
SPECIAL=consolemain sleighexample test
# Any additional modules for the command line decompiler
EXTRA= $(filter-out $(CORE) $(DECCORE) $(SLEIGH) $(GHIDRA) $(SLACOMP) $(SPECIAL),$(ALL_NAMES))

EXECS=decomp_dbg decomp_opt decomp_test_dbg ghidra_dbg ghidra_opt sleigh_dbg sleigh_opt libdecomp_dbg.a libdecomp.a

# Possible conditional compilation flags
#     __TERMINAL__             # Turn on terminal support for console mode
#     CPUI_STATISTICS          # Turn on collection of cover and cast statistics
#     CPUI_RULECOMPILE         # Allow user defined dynamic rules

# Debug compilation flags
#     OPACTION_DEBUG           # Turns on all the action tracing facilities
#     MERGEMULTI_DEBUG         # Check for MULTIEQUAL and INDIRECT intersections
#     BLOCKCONSISTENT_DEBUG    # Check that block graph structure is consistent
#     DFSVERIFY_DEBUG          # make sure that the block ordering algorithm produces
#                                a true depth first traversal of the dominator tree
#     CPUI_DEBUG               # This is the one controlling switch for all the other debug switches

COMMANDLINE_NAMES=$(CORE) $(DECCORE) $(EXTRA) $(SLEIGH) consolemain
COMMANDLINE_DEBUG=-DCPUI_DEBUG -D__TERMINAL__
COMMANDLINE_OPT=-D__TERMINAL__

TEST_NAMES=$(CORE) $(DECCORE) $(SLEIGH) $(EXTRA) test
TEST_DEBUG=-D__TERMINAL__

GHIDRA_NAMES=$(CORE) $(DECCORE) $(GHIDRA)
GHIDRA_NAMES_DBG=$(GHIDRA_NAMES) callgraph ifacedecomp testfunction ifaceterm interface
GHIDRA_DEBUG=-DCPUI_DEBUG
GHIDRA_OPT=

SLEIGH_NAMES=$(CORE) $(SLEIGH) $(SLACOMP)
SLEIGH_DEBUG=-DYYDEBUG
SLEIGH_OPT=

# The SLEIGH library is built with console mode objects and it
# uses the COMMANDLINE_* options
LIBSLA_NAMES=$(CORE) $(SLEIGH) loadimage sleigh memstate emulate opbehavior

# The Decompiler library is built with console mode objects and it uses the COMMANDLINE_* options
LIBDECOMP_NAMES=$(CORE) $(DECCORE) $(EXTRA) $(SLEIGH)

# object file macros
COMMANDLINE_DBG_OBJS=$(COMMANDLINE_NAMES:%=com_dbg/%.o)
COMMANDLINE_OPT_OBJS=$(COMMANDLINE_NAMES:%=com_opt/%.o)
TEST_DEBUG_OBJS=$(TEST_NAMES:%=test_dbg/%.o) $(UNITTEST_STRIP:%=test_dbg/%.o)
GHIDRA_DBG_OBJS=$(GHIDRA_NAMES_DBG:%=ghi_dbg/%.o)
GHIDRA_OPT_OBJS=$(GHIDRA_NAMES:%=ghi_opt/%.o)
SLEIGH_DBG_OBJS=$(SLEIGH_NAMES:%=sla_dbg/%.o)
SLEIGH_OPT_OBJS=$(SLEIGH_NAMES:%=sla_opt/%.o)
LIBSLA_DBG_OBJS=$(LIBSLA_NAMES:%=com_dbg/%.o)
LIBSLA_OPT_OBJS=$(LIBSLA_NAMES:%=com_opt/%.o)
LIBSLA_SOURCE=$(LIBSLA_NAMES:%=%.cc) $(LIBSLA_NAMES:%=%.hh) \
	$(SLACOMP:%=%.cc) slgh_compile.hh slghparse.hh types.h \
	partmap.hh error.hh slghparse.y pcodeparse.y xml.y slghscan.l loadimage_bfd.hh loadimage_bfd.cc
LIBDECOMP_DBG_OBJS=$(LIBDECOMP_NAMES:%=com_dbg/%.o)
LIBDECOMP_OPT_OBJS=$(LIBDECOMP_NAMES:%=com_opt/%.o)

-include $(COMMANDLINE_DBG_OBJS:.o=.d)
-include $(COMMANDLINE_OPT_OBJS:.o=.d)
-include $(TEST_DEBUG_OBJS:.o=.d)
-include $(GHIDRA_DBG_OBJS:.o=.d)
-include $(GHIDRA_OPT_OBJS:.o=.d)
-include $(SLEIGH_DBG_OBJS:.o=.d)
-include $(SLEIGH_OPT_OBJS:.o=.d)
-include $(LIBSLA_DBG_OBJS:.o=.d)
-include $(LIBSLA_OPT_OBJS:.o=.d)
-include $(LIBDECOMP_DBG_OBJS:.o=.d)
-include $(LIBDECOMP_OPT_OBJS:.o=.d)

com_dbg/%.o: %.cc | com_dbg
	$(CXX) $(ARCH_TYPE) $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(COMMANDLINE_DEBUG) \
		-MMD -MP -MF $(@:.o=.d) -c $< -o $@

com_opt/%.o: %.cc | com_opt
	$(CXX) $(ARCH_TYPE) $(OPT_CXXFLAGS) $(ADDITIONAL_FLAGS) $(COMMANDLINE_OPT) \
		-MMD -MP -MF $(@:.o=.d) -c $< -o $@

test_dbg/%.o: %.cc | test_dbg
	$(CXX) $(ARCH_TYPE) $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(TEST_DEBUG) \
		-MMD -MP -MF $(@:.o=.d) -c $< -o $@

test_dbg/%.o: ../unittests/%.cc | test_dbg
	$(CXX) -I. $(ARCH_TYPE) $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(TEST_DEBUG) \
		-MMD -MP -MF $(@:.o=.d) -c $< -o $@

ghi_dbg/%.o: %.cc | ghi_dbg
	$(CXX) $(ARCH_TYPE) $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(GHIDRA_DEBUG) \
		-MMD -MP -MF $(@:.o=.d) -c $< -o $@

ghi_opt/%.o: %.cc | ghi_opt
	$(CXX) $(ARCH_TYPE) $(OPT_CXXFLAGS) $(ADDITIONAL_FLAGS) $(GHIDRA_OPT) \
		-MMD -MP -MF $(@:.o=.d) -c $< -o $@

sla_dbg/%.o: %.cc | sla_dbg
	$(CXX) $(ARCH_TYPE) $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(SLEIGH_DEBUG) \
		-MMD -MP -MF $(@:.o=.d) -c $< -o $@

sla_opt/%.o: %.cc | sla_opt
	$(CXX) $(ARCH_TYPE) $(OPT_CXXFLAGS) $(ADDITIONAL_FLAGS) $(SLEIGH_OPT) \
		-MMD -MP -MF $(@:.o=.d) -c $< -o $@

OBJDIRS := com_dbg com_opt test_dbg ghi_dbg ghi_opt sla_dbg sla_opt

$(OBJDIRS):
	mkdir -p $@

grammar.cc:	grammar.y
	$(YACC) -l -o $@ $<
xml.cc:	xml.y
	$(YACC) -l -o $@ $<
pcodeparse.cc:	pcodeparse.y
	$(YACC) -l -o $@ $<
slghparse.cc:	slghparse.y
	$(YACC) -l -d -o $@ $<
slghscan.cc:	slghscan.l
	$(LEX) -L -o$@ $<
ruleparse.cc:	ruleparse.y
	$(YACC) -p ruleparse -d -o $@ $<

slghparse.hh:	slghparse.y slghparse.cc
slghscan.cc:	slghparse.hh slgh_compile.hh
ruleparse.hh:	ruleparse.y ruleparse.cc

decomp_dbg:	$(COMMANDLINE_DBG_OBJS)
	$(CXX) $(DBG_CXXFLAGS) $(ARCH_TYPE) -o decomp_dbg $(COMMANDLINE_DBG_OBJS) $(BFDLIB) $(LNK)

decomp_opt:	$(COMMANDLINE_OPT_OBJS)
	$(CXX) $(OPT_CXXFLAGS) $(ARCH_TYPE) -o decomp_opt $(COMMANDLINE_OPT_OBJS) $(BFDLIB) $(LNK)

#decomp_test_dbg:	DBG_CXXFLAGS += -D_GLIBCXX_ASSERTIONS -fsanitize=address,undefined
decomp_test_dbg:	$(TEST_DEBUG_OBJS)
	$(CXX) $(DBG_CXXFLAGS) $(ARCH_TYPE) -o decomp_test_dbg $(TEST_DEBUG_OBJS) $(BFDLIB) $(LNK)

test: decomp_test_dbg
	./decomp_test_dbg

ghidra_dbg:	$(GHIDRA_DBG_OBJS)
	$(CXX) $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(MAKE_STATIC) $(ARCH_TYPE) -o ghidra_dbg $(GHIDRA_DBG_OBJS)

ghidra_opt:	$(GHIDRA_OPT_OBJS)
	$(CXX) $(OPT_CXXFLAGS) $(ADDITIONAL_FLAGS) $(MAKE_STATIC) $(ARCH_TYPE)  -o ghidra_opt $(GHIDRA_OPT_OBJS)

sleigh_dbg:	$(SLEIGH_DBG_OBJS)
	$(CXX) $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(MAKE_STATIC) $(ARCH_TYPE) -o sleigh_dbg $(SLEIGH_DBG_OBJS) $(LNK)

sleigh_opt:	$(SLEIGH_OPT_OBJS)
	$(CXX) $(OPT_CXXFLAGS) $(ADDITIONAL_FLAGS) $(MAKE_STATIC) $(ARCH_TYPE) -o sleigh_opt $(SLEIGH_OPT_OBJS) $(LNK)

install_ghidradbg:	ghidra_dbg
	cp ghidra_dbg $(GHIDRA_BIN)/Ghidra/Features/Decompiler/os/$(OSDIR)/decompile

install_ghidraopt:	ghidra_opt
	cp ghidra_opt $(GHIDRA_BIN)/Ghidra/Features/Decompiler/os/$(OSDIR)/decompile

libsla_dbg.a:	$(LIBSLA_DBG_OBJS)
	rm -rf libsla_dbg.a
	ar qc libsla_dbg.a $(LIBSLA_DBG_OBJS)
	ranlib libsla_dbg.a

libsla.a:	$(LIBSLA_OPT_OBJS)
	rm -rf libsla.a
	ar qc libsla.a $(LIBSLA_OPT_OBJS)
	ranlib libsla.a

libdecomp_dbg.a:	$(LIBDECOMP_DBG_OBJS)
	rm -rf libdecomp_dbg.a
	ar qc libdecomp_dbg.a $(LIBDECOMP_DBG_OBJS)
	ranlib libdecomp_dbg.a

libdecomp.a:	$(LIBDECOMP_OPT_OBJS)
	rm -rf libdecomp.a
	ar qc libdecomp.a $(LIBDECOMP_OPT_OBJS)
	ranlib libdecomp.a

sleighexamp_dir: slghscan.cc
	rm -rf $(SLEIGHVERSION)
	mkdir $(SLEIGHVERSION)
	mkdir $(SLEIGHVERSION)/src $(SLEIGHVERSION)/specfiles
	cp ../../../../../Processors/x86/data/languages/x86.sla \
	  ../../../../../Processors/x86/data/languages/x86.slaspec \
	  ../../../../../Processors/x86/data/languages/ia.sinc \
		 $(SLEIGHVERSION)/specfiles
	cp $(LIBSLA_SOURCE) Makefile Doxyfile $(SLEIGHVERSION)/src
	cp sleighexample.cc $(SLEIGHVERSION)
	grep ^-- sleighexample.cc | sed -e s/--// > $(SLEIGHVERSION)/Makefile
	grep ^-a- sleighexample.cc | sed -e s/-a-// > $(SLEIGHVERSION)/README
	sed -e s/page\ sleigh\ /mainpage\ / < $(SLEIGHVERSION)/src/sleigh.hh > $(SLEIGHVERSION)/spam
	mv $(SLEIGHVERSION)/spam $(SLEIGHVERSION)/src/sleigh.hh
	cd $(SLEIGHVERSION)/src; doxygen Doxyfile

link_extensions:
	rm -rf coreext_*.cc coreext_*.hh ghidraext_*.cc ghidraext_*.hh consoleext_*.cc consoleext_*.hh
	for i in $(EXTERNAL_COREEXT_NAMES) $(EXTERNAL_GHIDRAEXT_NAMES) $(EXTERNAL_CONSOLEEXT_NAMES); do \
		ln -s $(EXTENSION_POINT)/$$i.cc $$i.cc; \
		ln -s $(EXTENSION_POINT)/$$i.hh $$i.hh; \
	done

link_extensions_hard:
	rm -rf coreext_*.cc coreext_*.hh ghidraext_*.cc ghidraext_*.hh consoleext_*.cc consoleext_*.hh
	for i in $(EXTERNAL_COREEXT_NAMES) $(EXTERNAL_GHIDRAEXT_NAMES) $(EXTERNAL_CONSOLEEXT_NAMES); do \
		ln $(EXTENSION_POINT)/$$i.cc $$i.cc; \
		ln $(EXTENSION_POINT)/$$i.hh $$i.hh; \
	done

tags:
	etags *.c *.h *.cc *.hh

doc:
	doxygen Doxyfile

clean:
	rm -f com_dbg/*.o com_dbg/*.d
	rm -f com_opt/*.o com_opt/*.d
	rm -f test_dbg/*.o test_dbg/*.d
	rm -f ghi_dbg/*.o ghi_dbg/*.d
	rm -f ghi_opt/*.o ghi_opt/*.d
	rm -f sla_dbg/*.o sla_dbg/*.d
	rm -f sla_opt/*.o sla_opt/*.d
	rm -f *.gcov com_dbg/*.gcno com_dbg/*.gcda

resetgcov:
	rm -f *.gcov com_dbg/*.gcda

reallyclean:	clean	
	rm -rf coreext_*.cc coreext_*.hh ghidraext_*.cc ghidraext_*.hh consoleext_*.cc consoleext_*.hh
	rm -rf com_dbg com_opt test_dbg ghi_dbg ghi_opt sla_dbg sla_opt
	rm -f $(EXECS) TAGS *~

.PHONY: \
	decomp_dbg \
	decomp_opt \
	decomp_test_dbg \
	test \
	ghidra_dbg \
	ghidra_opt \
	sleigh_dbg \
	sleigh_opt \
	install_ghidradbg \
	install_ghidraopt \
	sleighexamp_dir \
	link_extensions \
	link_extensions_hard \
	tags \
	doc \
	clean \
	resetgcov \
	reallyclean
