CC?= gcc
CFLAGS?=-Wall -Wextra -std=c11 -pedantic -O2 -fPIC -fvisibility=hidden -D_POSIX_C_SOURCE=200809L
ifeq ($(shell uname),Darwin)
# macOS hides memmem() (used by r_util headers) under strict _POSIX_C_SOURCE
CFLAGS+=-D_DARWIN_C_SOURCE
endif
DEBUG_FLAGS?=-g -DDEBUG -DHBC_DEBUG_LOGGING=1

VERSION=$(shell grep "^[[:space:]]*version:" meson.build | cut -d "'" -f 2)

VH=include/hbc/version.h

# Directories
SRC_DIR = src/lib
BUILD_DIR = build
BIN_DIR = bin

## Source files
R2_CFLAGS=$(shell pkg-config --cflags r_util 2>/dev/null || r2pm -H R2_CFLAGS 2>/dev/null || r2 -H R2_CFLAGS 2>/dev/null || echo "-I/usr/local/include/libr -I/usr/local/libr")
UTILS_SRC = $(wildcard $(SRC_DIR)/utils/*.c)
PARSERS_SRC = $(wildcard $(SRC_DIR)/parsers/*.c)
DISASM_SRC = $(wildcard $(SRC_DIR)/disassembly/*.c)
DECOMPILE_SRC = $(wildcard $(SRC_DIR)/decompilation/*.c)
OPCODES_SRC = $(filter-out $(SRC_DIR)/opcodes/%.inc.c,$(wildcard $(SRC_DIR)/opcodes/*.c))
LIB_SRC = $(UTILS_SRC) $(PARSERS_SRC) $(DISASM_SRC) $(DECOMPILE_SRC) $(OPCODES_SRC) \
          $(SRC_DIR)/hbc.c $(SRC_DIR)/literals_api.c $(SRC_DIR)/opcodes/encoder.c $(SRC_DIR)/opcodes/decoder.c $(SRC_DIR)/r2.c
MAIN_SRC = src/tool/r2hermes.c
HBC_HEADERS = $(shell find include -name '*.h')
CFLAGS+=${R2_CFLAGS}

## Object files
LIB_OBJ = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(LIB_SRC))
LIB_RELOC_OBJ = $(BUILD_DIR)/libhbc_all.o
MAIN_OBJ = $(BUILD_DIR)/r2hermes.o
MAIN_RELOC_OBJ = $(BUILD_DIR)/r2hermes_all.o

## Artifacts
BIN_FILE = $(BIN_DIR)/r2hermes
STATIC_LIB = $(BUILD_DIR)/libhbc.a

# Include paths
INCLUDES = -Iinclude
LD_RELOC_FLAGS ?= -r -nostdlib

all: $(STATIC_LIB) $(BIN_FILE)

debug: CFLAGS += $(DEBUG_FLAGS)
debug: all

ASAN_FLAGS = -fsanitize=address -fno-omit-frame-pointer -g

asan:
	$(MAKE) clean
	$(MAKE) $(VH)
	$(MAKE) all CFLAGS="$(CFLAGS) $(ASAN_FLAGS)" LDFLAGS="$(LDFLAGS) -fsanitize=address"
	CFLAGS="$(ASAN_FLAGS)" LDFLAGS="-fsanitize=address" $(MAKE) -C src/r2
	$(MAKE) -C src/r2 user-install

format indent fmt:
	clang-format-radare2 $(shell find src include src/r2 | grep '\.[c|h]$$')

$(LIB_RELOC_OBJ): $(LIB_OBJ)
	$(CC) $(LD_RELOC_FLAGS) -o $@ $^

$(STATIC_LIB): $(LIB_RELOC_OBJ)
	$(RM) $@
	ar rcs $@ $^

$(MAIN_RELOC_OBJ): $(MAIN_OBJ) $(LIB_OBJ)
	$(CC) $(LD_RELOC_FLAGS) -o $@ $^

$(BIN_FILE): $(STATIC_LIB) $(MAIN_RELOC_OBJ) | $(shell mkdir -p $(BIN_DIR))
	$(CC) $(CFLAGS) -o $@ $(MAIN_RELOC_OBJ) $(R2_LDFLAGS) -lr_util

$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(HBC_HEADERS) | $(VH)
	@mkdir -p $(dir $@)
	$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@

$(MAIN_OBJ): $(MAIN_SRC) $(HBC_HEADERS) | $(VH)
	$(CC) $(CFLAGS) -fvisibility=default $(INCLUDES) -c $< -o $@

clean:
	rm -rf $(BUILD_DIR) $(BIN_DIR)/r2hermes
	rm -f $(VH)
	$(MAKE) -C src/r2 clean

# Testing
TEST_DIR = tests
TEST_SRC = $(wildcard $(TEST_DIR)/*.c)
TEST_BIN = $(BIN_DIR)/run_tests

$(TEST_BIN): $(STATIC_LIB) $(TEST_SRC) | $(shell mkdir -p $(BIN_DIR))
	$(CC) $(CFLAGS) -fvisibility=default $(INCLUDES) -o $@ $(TEST_SRC) -L$(BUILD_DIR) -lhbc $(R2_LDFLAGS) -lr_util

test: $(BIN_FILE) $(TEST_BIN)
	$(TEST_BIN)
	$(BIN_FILE) sl test/bins/hbc/bespoke_eval.hbc | grep -q '0x000000b4 f=0 +0x4 yes.js:1:5 stmt=0'
	$(BIN_FILE) eval test/bins/hbc/bespoke_eval.hbc | grep -q '0x000000b4 f=0 +0x4 func=0x000000b0 strict=false name=global'
	$(BIN_FILE) eval -q test/bins/hbc/bespoke_eval.hbc | grep -q '^0x000000b4$$'
	$(BIN_FILE) eval -j test/bins/hbc/bespoke_eval.hbc | grep -q '"address":180'
	$(BIN_FILE) Eq test/bins/hbc/bespoke_eval.hbc | grep -q '^0x000000b4$$'
	$(BIN_FILE) Ej test/bins/hbc/bespoke_eval.hbc | grep -q '"function_id":0'
	$(BIN_FILE) Ej test/bins/hbc/bespoke_eval.hbc | grep -q '"strict":false'
	$(BIN_FILE) h test/bins/hbc/bespoke_eval.hbc | grep -q 'Source Locations: 13 bytes'
	$(BIN_FILE) h test/bins/hbc/index.android.bundle | grep -q 'Source Locations: 0 bytes'
	$(BIN_FILE) s test/bins/hbc/index.android.bundle 2959 | grep -q 'name=•'
	$(BIN_FILE) libs test/bins/hbc/index.android.bundle | grep -q 'react-native'

r2:
	$(MAKE) clean
	$(MAKE) -C src/r2 && $(MAKE) -C src/r2 user-install
	r2r -i test/db/extras

r2one:
	$(MAKE) -C src/r2 r2one

user-install user-uninstall:
	$(MAKE) -C src/r2
	$(MAKE) -C src/r2 $@

# Binary package for r2pm -bi: plugins/ and bin/ for this r2 version and platform
# Binary package for r2pm -bi: plugins/ and bin/ for this r2 version and platform
# R2PM_OS/ARCH/BITS require a recent r2pm; fall back to uname when missing
R2PM_OS_SH=$(shell r2pm -H R2PM_OS 2>/dev/null)
R2PM_ARCH_SH=$(shell r2pm -H R2PM_ARCH 2>/dev/null)
R2PM_BITS_SH=$(shell r2pm -H R2PM_BITS 2>/dev/null)
ifeq ($(R2PM_OS_SH),)
ifeq ($(shell uname -s),Darwin)
R2PM_OS_SH=darwin
else
R2PM_OS_SH=linux
endif
endif
ifeq ($(R2PM_ARCH_SH),)
ifeq ($(shell uname -m),x86_64)
R2PM_ARCH_SH=x86
else ifeq ($(shell uname -m),aarch64)
R2PM_ARCH_SH=arm
else ifeq ($(shell uname -m),arm64)
R2PM_ARCH_SH=arm
else
R2PM_ARCH_SH=$(shell uname -m)
endif
endif
ifeq ($(R2PM_BITS_SH),)
R2PM_BITS_SH=64
endif
BINDIST_ZIP=r2hermes-$(shell r2 -qv)-$(R2PM_OS_SH)-$(R2PM_ARCH_SH)-$(R2PM_BITS_SH).zip

bindist: all
	$(MAKE) -C src/r2
	rm -rf $(BUILD_DIR)/bindist $(BINDIST_ZIP)
	$(MAKE) -C src/r2 user-install R2_PLUGDIR=$(CURDIR)/$(BUILD_DIR)/bindist/plugins
	mkdir -p $(BUILD_DIR)/bindist/bin
	cp -f $(BIN_FILE) $(BUILD_DIR)/bindist/bin/
	cd $(BUILD_DIR)/bindist && zip -r $(CURDIR)/$(BINDIST_ZIP) plugins bin

$(VH):
	@mkdir -p $(dir $@)
	echo '#ifndef LIBHBC_VERSION' > $@
	echo '#define LIBHBC_VERSION "$(VERSION)"' >> $@
	echo '#define LIBHBC_VERSION_MAJOR "$(shell echo $(VERSION) | cut -d . -f 1)"' >> $@
	echo '#define LIBHBC_VERSION_MINOR "$(shell echo $(VERSION) | cut -d . -f 2)"' >> $@
	echo '#define LIBHBC_VERSION_PATCH "$(shell echo $(VERSION) | cut -d . -f 3)"' >> $@
	echo '#endif' >> $@

.PHONY: r2 test test2 all clean debug asan bindist
