# Reverse Engineer's Hex Editor
# Copyright (C) 2017-2021 Daniel Collins <solemnwarning@solemnwarning.net>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published by
# the Free Software Foundation.
#
# This program 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
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

LUA          ?= lua
WX_CONFIG    ?= wx-config
CAPSTONE_PKG ?= capstone
JANSSON_PKG  ?= jansson
LUA_PKG      ?= $(shell pkg-config --exists lua5.3 && echo lua5.3 || echo lua)

EXE ?= rehex
EMBED_EXE ?= ./tools/embed

# Wrapper around the $(shell) function that aborts the build if the command
# exits with a nonzero status.
shell-or-die = $\
	$(eval sod_out := $$(shell $(1); echo $$$$?))$\
	$(if $(filter 0,$(lastword $(sod_out))),$\
		$(wordlist 1, $(shell echo $$(($(words $(sod_out)) - 1))), $(sod_out)),$\
		$(error $(1) exited with status $(lastword $(sod_out))))

WX_CXXFLAGS ?= $(call shell-or-die,$(WX_CONFIG) --cxxflags base core aui propgrid adv)
WX_LIBS     ?= $(call shell-or-die,$(WX_CONFIG) --libs     base core aui propgrid adv)

CAPSTONE_CFLAGS ?= $(call shell-or-die,pkg-config $(CAPSTONE_PKG) --cflags)
CAPSTONE_LIBS   ?= $(call shell-or-die,pkg-config $(CAPSTONE_PKG) --libs)

JANSSON_CFLAGS ?= $(call shell-or-die,pkg-config $(JANSSON_PKG) --cflags)
JANSSON_LIBS   ?= $(call shell-or-die,pkg-config $(JANSSON_PKG) --libs)

LUA_CFLAGS ?= $(call shell-or-die,pkg-config $(LUA_PKG) --cflags)
LUA_LIBS   ?= $(call shell-or-die,pkg-config $(LUA_PKG) --libs)

CFLAGS   := -Wall -std=c99   -ggdb -I. -Iinclude/ -IwxLua/modules/ $(CAPSTONE_CFLAGS) $(JANSSON_CFLAGS) $(LUA_CFLAGS) $(CFLAGS)
CXXFLAGS := -Wall -std=c++11 -ggdb -I. -Iinclude/ -IwxLua/modules/ $(CAPSTONE_CFLAGS) $(JANSSON_CFLAGS) $(LUA_CFLAGS) $(WX_CXXFLAGS) $(CXXFLAGS)

LDLIBS := $(WX_LIBS) $(CAPSTONE_LIBS) $(JANSSON_LIBS) $(LUA_LIBS) $(LDLIBS)

ifeq ($(DEBUG),)
	DEBUG=0
endif

ifeq ($(DEBUG),0)
	CFLAGS   += -DNDEBUG
	CXXFLAGS += -DNDEBUG
else
	CFLAGS   += -g
	CXXFLAGS += -g
endif

# Define this for releases
VERSION := 0.3.91

ifdef VERSION
	LONG_VERSION := Version $(VERSION)
else
	# Check if we are actually in a git checkout before trying to get the
	# commit hash with `git log`, else we blow up in a git-archive export.
	
	ifneq ($(wildcard .git/*),)
		GIT_COMMIT_SHA ?= $(call shell-or-die,git log -1 --format="%H")
	else
		GIT_COMMIT_SHA ?= UNKNOWN
	endif
	
	GIT_COMMIT_TIME ?= $(call shell-or-die,git log -1 --format="%ct")
	
	VERSION      := 37fc16444e8e2ec9c624ea288f36c75acf951465
	LONG_VERSION := Snapshot 37fc16444e8e2ec9c624ea288f36c75acf951465
endif

DEPDIR := .d
DEPPRE = @mkdir -p "$(dir $(DEPDIR)/$@.Td)"
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$@.Td
DEPPOST = @mv -f $(DEPDIR)/$@.Td $(DEPDIR)/$@.d && touch $@

WXLUA_BINDINGS := wxLua/bindings/.done

.PHONY: all
all: $(EXE)

# Stop Make from deleting intermediate files at-will. Some intermediates (e.g. resources) are used
# by multiple targets (all and test) and so they should persist. Others are generated as a
# side-effect of another target (wxLua bindings) so Make cannot properly track how to build them
# when it needs to recreate them later.
.SECONDARY:

.PHONY: check
check: tests/all-tests
	./tests/all-tests

.PHONY: clean
clean:
	rm -f res/ascii16.c   res/ascii16.h \
	      res/ascii24.c   res/ascii24.h \
	      res/ascii32.c   res/ascii32.h \
	      res/ascii48.c   res/ascii48.h \
	      res/icon16.c    res/icon16.h \
	      res/icon32.c    res/icon32.h \
	      res/icon48.c    res/icon48.h \
	      res/icon64.c    res/icon64.h \
	      res/icon128.c   res/icon128.h \
	      res/license.c   res/license.h \
	      res/offsets16.c res/offsets16.h \
	      res/offsets24.c res/offsets24.h \
	      res/offsets32.c res/offsets32.h \
	      res/offsets48.c res/offsets48.h
	
	rm -f $(APP_OBJS)
	rm -f $(EXE)
	rm -f $(TEST_OBJS)
	rm -f ./tests/all-tests
	rm -f $(EMBED_EXE)
	
	grep -r "generated by genwxbind.lua" wxLua/ --exclude=genwxbind.lua | cut -d: -f1 | sort | uniq | xargs -r rm
	rm -f $(WXLUA_BINDINGS)

.PHONY: distclean
distclean: clean

WXLUA_OBJS := \
	wxLua/modules/wxlua/bit.o \
	wxLua/modules/wxlua/lbitlib.o \
	wxLua/modules/wxlua/wxlbind.o \
	wxLua/modules/wxlua/wxlcallb.o \
	wxLua/modules/wxlua/wxllua.o \
	wxLua/modules/wxlua/wxlobject.o \
	wxLua/modules/wxlua/wxlstate.o \
	wxLua/modules/wxlua/wxlua_bind.o

WXBIND_OBJS := \
	wxLua/modules/wxbind/src/wxadv_bind.o \
	wxLua/modules/wxbind/src/wxadv_wxladv.o \
	wxLua/modules/wxbind/src/wxaui_bind.o \
	wxLua/modules/wxbind/src/wxbase_base.o \
	wxLua/modules/wxbind/src/wxbase_bind.o \
	wxLua/modules/wxbind/src/wxbase_config.o \
	wxLua/modules/wxbind/src/wxbase_data.o \
	wxLua/modules/wxbind/src/wxbase_datetime.o \
	wxLua/modules/wxbind/src/wxbase_file.o \
	wxLua/modules/wxbind/src/wxcore_appframe.o \
	wxLua/modules/wxbind/src/wxcore_bind.o \
	wxLua/modules/wxbind/src/wxcore_clipdrag.o \
	wxLua/modules/wxbind/src/wxcore_controls.o \
	wxLua/modules/wxbind/src/wxcore_core.o \
	wxLua/modules/wxbind/src/wxcore_defsutils.o \
	wxLua/modules/wxbind/src/wxcore_dialogs.o \
	wxLua/modules/wxbind/src/wxcore_event.o \
	wxLua/modules/wxbind/src/wxcore_gdi.o \
	wxLua/modules/wxbind/src/wxcore_geometry.o \
	wxLua/modules/wxbind/src/wxcore_graphics.o \
	wxLua/modules/wxbind/src/wxcore_help.o \
	wxLua/modules/wxbind/src/wxcore_image.o \
	wxLua/modules/wxbind/src/wxcore_mdi.o \
	wxLua/modules/wxbind/src/wxcore_menutool.o \
	wxLua/modules/wxbind/src/wxcore_picker.o \
	wxLua/modules/wxbind/src/wxcore_print.o \
	wxLua/modules/wxbind/src/wxcore_sizer.o \
	wxLua/modules/wxbind/src/wxcore_windows.o \
	wxLua/modules/wxbind/src/wxcore_wxlcore.o \
	wxLua/modules/wxbind/src/wxpropgrid_bind.o

APP_OBJS := \
	res/ascii16.o \
	res/ascii24.o \
	res/ascii32.o \
	res/ascii48.o \
	res/icon16.o \
	res/icon32.o \
	res/icon48.o \
	res/icon64.o \
	res/icon128.o \
	res/license.o \
	res/offsets16.o \
	res/offsets24.o \
	res/offsets32.o \
	res/offsets48.o \
	src/AboutDialog.o \
	src/AppMain.o \
	src/AppTestable.o \
	src/ArtProvider.o \
	src/BasicDataTypes.o \
	src/buffer.o \
	src/BytesPerLineDialog.o \
	src/ByteRangeSet.o \
	src/ClickText.o \
	src/CodeCtrl.o \
	src/CommentTree.o \
	src/ConsoleBuffer.o \
	src/ConsolePanel.o \
	src/DataType.o \
	src/decodepanel.o \
	src/DiffWindow.o \
	src/disassemble.o \
	src/DisassemblyRegion.o \
	src/document.o \
	src/DocumentCtrl.o \
	src/EditCommentDialog.o \
	src/Events.o \
	src/FillRangeDialog.o \
	src/LicenseDialog.o \
	src/lua-bindings/rehex_bind.o \
	src/lua-plugin-preload.o \
	src/LuaPluginLoader.o \
	src/mainwindow.o \
	src/Palette.o \
	src/search.o \
	src/SelectRangeDialog.o \
	src/StringPanel.o \
	src/textentrydialog.o \
	src/Tab.o \
	src/ToolPanel.o \
	src/util.o \
	src/VirtualMappingDialog.o \
	src/VirtualMappingList.o \
	src/win32lib.o \
	$(WXLUA_OBJS) \
	$(WXBIND_OBJS) \
	$(EXTRA_APP_OBJS)

$(EXE): $(APP_OBJS)
	$(CXX) $(CXXFLAGS) -DLONG_VERSION='"$(LONG_VERSION)"' -DLIBDIR='"$(libdir)"' -c -o res/version.o res/version.cpp
	$(CXX) $(CXXFLAGS) -o $@ $^ res/version.o $(LDFLAGS) $(LDLIBS)

TEST_OBJS := \
	googletest/src/gtest-all.o \
	res/ascii16.o \
	res/ascii24.o \
	res/ascii32.o \
	res/ascii48.o \
	res/icon16.o \
	res/icon32.o \
	res/icon48.o \
	res/icon64.o \
	res/icon128.o \
	res/license.o \
	res/offsets16.o \
	res/offsets24.o \
	res/offsets32.o \
	res/offsets48.o \
	src/AboutDialog.o \
	src/AppTestable.o \
	src/ArtProvider.o \
	src/BasicDataTypes.o \
	src/buffer.o \
	src/ByteRangeSet.o \
	src/BytesPerLineDialog.o \
	src/ClickText.o \
	src/CommentTree.o \
	src/ConsoleBuffer.o \
	src/DataType.o \
	src/DiffWindow.o \
	src/DisassemblyRegion.o \
	src/document.o \
	src/DocumentCtrl.o \
	src/EditCommentDialog.o \
	src/Events.o \
	src/FillRangeDialog.o \
	src/LicenseDialog.o \
	src/lua-bindings/rehex_bind.o \
	src/lua-plugin-preload.o \
	src/LuaPluginLoader.o \
	src/mainwindow.o \
	src/Palette.o \
	src/search.o \
	src/SelectRangeDialog.o \
	src/StringPanel.o \
	src/Tab.o \
	src/textentrydialog.o \
	src/ToolPanel.o \
	src/util.o \
	src/VirtualMappingDialog.o \
	src/win32lib.o \
	tests/buffer.o \
	tests/ByteRangeMap.o \
	tests/ByteRangeSet.o \
	tests/CommentsDataObject.o \
	tests/CommentTree.o \
	tests/ConsoleBuffer.o \
	tests/DiffWindow.o \
	tests/DisassemblyRegion.o \
	tests/Document.o \
	tests/DocumentCtrl.o \
	tests/LuaPluginLoader.o \
	tests/main.o \
	tests/NestedOffsetLengthMap.o \
	tests/NumericTextCtrl.o \
	tests/search-bseq.o \
	tests/search-text.o \
	tests/SearchValue.o \
	tests/SafeWindowPointer.o \
	tests/SharedDocumentPointer.o \
	tests/StringPanel.o \
	tests/Tab.o \
	tests/util.o \
	$(WXLUA_OBJS) \
	$(WXBIND_OBJS) \
	$(EXTRA_TEST_OBJS)

tests/all-tests: $(TEST_OBJS)
	$(CXX) $(CXXFLAGS) -DLONG_VERSION='"$(LONG_VERSION)"' -DLIBDIR='"$(libdir)"' -c -o res/version.o res/version.cpp
	$(CXX) $(CXXFLAGS) -o $@ $^ res/version.o $(LDFLAGS) $(LDLIBS)

$(EMBED_EXE): tools/embed.cpp
	$(CXX) $(CXXFLAGS) -o $@ $<

src/AboutDialog.o: res/icon128.h
src/ArtProvider.o: res/ascii16.h res/ascii24.h res/ascii32.h res/ascii48.h res/offsets16.h res/offsets24.h res/offsets32.h res/offsets48.h
src/DiffWindow.o: res/icon16.h res/icon32.h res/icon48.h res/icon64.h
src/LicenseDialog.o: res/license.h
src/LuaPluginLoader.o: src/lua-bindings/rehex_bind.h src/lua-plugin-preload.h
src/mainwindow.o: res/icon16.h res/icon32.h res/icon48.h res/icon64.h

res/license.c res/license.h: LICENSE.txt $(EMBED_EXE)
	$(EMBED_EXE) $< LICENSE_TXT res/license.c res/license.h

res/%.c res/%.h: res/%.png $(EMBED_EXE)
	$(EMBED_EXE) $< $*_png res/$*.c res/$*.h

src/lua-bindings/rehex_bind.cpp src/lua-bindings/rehex_bind.h: src/lua-bindings/rehex.i src/lua-bindings/rehex_override.hpp src/lua-bindings/rehex_rules.lua $(WXLUA_BINDINGS)
	$(LUA) -e"rulesFilename=\"src/lua-bindings/rehex_rules.lua\"" wxLua/bindings/genwxbind.lua
	
	# genwxbind.lua may not modify individual files if they are already up to date.
	touch -c src/lua-bindings/rehex_bind.cpp
	touch -c src/lua-bindings/rehex_bind.h

$(WXLUA_BINDINGS):
	$(MAKE) -C wxLua/bindings/ wxadv wxaui wxbase wxcore wxlua wxpropgrid LUA=$(LUA)
	touch $@

src/lua-plugin-preload.c src/lua-plugin-preload.h: src/lua-plugin-preload.lua $(EMBED_EXE)
	$(EMBED_EXE) $< LUA_PLUGIN_PRELOAD src/lua-plugin-preload.c src/lua-plugin-preload.h

%.o: %.c $(WXLUA_BINDINGS)
	$(DEPPRE)
	$(CC) $(CFLAGS) $(DEPFLAGS) -c -o $@ $<
	$(DEPPOST)

tests/%.o: tests/%.cpp $(WXLUA_BINDINGS)
	$(DEPPRE)
	$(CXX) $(CXXFLAGS) -I./googletest/include/ $(DEPFLAGS) -c -o $@ $<
	$(DEPPOST)

googletest/src/%.o: googletest/src/%.cc
	$(DEPPRE)
	$(CXX) $(CXXFLAGS) -I./googletest/include/ -I./googletest/ $(DEPFLAGS) -c -o $@ $<
	$(DEPPOST)

%.o: %.cpp $(WXLUA_BINDINGS)
	$(DEPPRE)
	$(CXX) $(CXXFLAGS) $(DEPFLAGS) -c -o $@ $<
	$(DEPPOST)

wxLua/%.cpp: $(WXLUA_BINDINGS)
	@true

include $(shell test -d .d/ && find .d/ -name '*.d' -type f)

prefix      ?= /usr/local
exec_prefix ?= $(prefix)
bindir      ?= $(exec_prefix)/bin
datarootdir ?= $(prefix)/share
libdir      ?= $(exec_prefix)/lib

PLUGINS_INSTALL := \
	exe/bitops52.lua \
	exe/class.lua \
	exe/document_stream.lua \
	exe/enum.lua \
	exe/kaitaistruct.lua \
	exe/microsoft_pe.lua \
	exe/plugin.lua \
	exe/string_decode.lua \
	exe/string_stream.lua \
	exe/utils.lua

.PHONY: install
install: $(EXE)
	install -D -m 0755 $(EXE) $(DESTDIR)$(bindir)/$(EXE)
	
	for s in 16 32 48 64 128 256 512; \
	do \
		install -D -m 0644 res/icon$${s}.png $(DESTDIR)$(datarootdir)/icons/hicolor/$${s}x$${s}/apps/rehex.png; \
	done
	
	install -D -m 0644 res/rehex.desktop $(DESTDIR)$(datarootdir)/applications/rehex.desktop
	
	for f in $(PLUGINS_INSTALL); \
	do \
		install -D -m 0644 plugins/$${f} $(DESTDIR)$(libdir)/rehex/$${f}; \
	done

.PHONY: uninstall
uninstall:
	rm -f $(DESTDIR)$(bindir)/$(EXE)
	rm -f $(DESTDIR)$(datarootdir)/applications/rehex.desktop
	
	for s in 16 32 48 64 128 256 512; \
	do \
		rm -f $(DESTDIR)$(datarootdir)/icons/hicolor/$${s}x$${s}/apps/rehex.png; \
	done
	
	for f in $(PLUGINS_INSTALL); \
	do \
		rm -f $(DESTDIR)$(libdir)/rehex/$${f}; \
	done
	
	# Delete any empty directories, preserving plugins we didn't install
	find $(DESTDIR)$(libdir)/rehex/ -type d -empty -delete

.PHONY: dist
dist:
	rm -rf rehex-$(VERSION) rehex-$(VERSION).tar
	mkdir rehex-$(VERSION)/
	
ifneq ("$(wildcard MANIFEST)","")
	# Running from a dist tarball, ship anything in the MANIFEST
	xargs cp --parents -t rehex-$(VERSION)/ < MANIFEST
else
	# Running from the git tree, ship any checked in files
	(git ls-files && echo MANIFEST) | LC_ALL=C sort > rehex-$(VERSION)/MANIFEST
	git ls-files | xargs cp --parents -t rehex-$(VERSION)/
	
	# Inline any references to the HEAD commit sha/timestamp
	sed -i -e "s|\$37fc16444e8e2ec9c624ea288f36c75acf951465|37fc16444e8e2ec9c624ea288f36c75acf951465|g" rehex-$(VERSION)/Makefile
	sed -i -e "s|\$1620045476|1620045476|g" rehex-$(VERSION)/Makefile
endif
	
	# Generate reproducible tarball. All files use git commit timestamp.
	find rehex-$(VERSION) -print0 | \
		LC_ALL=C sort -z | \
		tar \
			--format=ustar \
			--mtime=@1620045476 \
			--owner=0 --group=0 --numeric-owner \
			--no-recursion --null  -T - \
			-cf - | \
		gzip -9n - > rehex-$(VERSION).tar.gz
