#Makefile for use with GNU make

THIS_MAKEFILE_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
THIS_DIR:=$(shell basename "${THIS_MAKEFILE_DIR}")
THIS_MAKEFILE:=$(lastword $(MAKEFILE_LIST))

.POSIX:
.SUFFIXES:
.SUFFIXES: .o .c .a

CONFIGFILE ?= config.mk
$(info Using config file ${CONFIGFILE})
include ${CONFIGFILE}
CONFIGFILEPATH=$(shell ls ${CONFIGFILE} >/dev/null 2>/dev/null && realpath ${CONFIGFILE})
ifeq ($(CONFIGFILEPATH),)
  $(error Config file ${CONFIGFILE} not found)
endif

CC ?= cc

WIN=
ifeq ($(WIN),)
  # MINGW = 1 in config.mk (configure's host check; not the environment, and a CC given
  # on the command line does not undo it), or a w64 compiler
  WIN=$(if $(and $(filter file,$(origin MINGW)),$(filter 1,$(MINGW))),1,$(if $(findstring w64,$(CC)),1,0))
endif

ifeq ($(WIN),0)
  EXE=
  EXT=
else
  EXE=.exe
  EXT=.win
endif

# The 2toon command's line in `zsv help` exists only when built with TOON
# (configure --disable-toon omits it). Strip it from the EXPECTED output (only)
# when TOON is disabled; the actual output is always compared strictly, so a
# missing 2toon line in an enabled build -- or a spurious one when disabled --
# still fails the test.
ifeq ($(ZSV_NO_TOON),1)
  TOON_HELP_FILTER=grep -v '^  2toon '
else
  TOON_HELP_FILTER=cat
endif

ifeq ($(WIN),1)
  # Windows programs end their output lines in \r\n
  CMP ?= diff --strip-trailing-cr
endif
CMP ?= cmp
export CMP

export TEST_DATA_DIR=./../../data
export TMP_DIR=./../../tmp

ifeq ($(DEBUG),1)
  DBG_SUBDIR+=dbg
else
  DBG_SUBDIR+=rel
endif

ifeq ($(WIN),0)
  BUILD_SUBDIR=$(shell uname)/${DBG_SUBDIR}
  SO=so
  ifeq ($(BUILD_SUBDIR),Linux)
    CFLAGS+= -ldl -lpthread
  endif
else
  BUILD_SUBDIR=win/${DBG_SUBDIR}
  CFLAGS+= -D__USE_MINGW_ANSI_STDIO -D_ISOC99_SOURCE
  CFLAGS+= -Wl,--strip-all
  SO=dll
endif
CFLAGS+= ${CFLAGS_PIC}

ifeq ($(ZSVSHEET_BUILD),1)
  CFLAGS+=-DZSVSHEET_BUILD
endif

ifeq ($(ZSV_EXTRAS),1)
  CFLAGS+= -DZSV_EXTRAS
endif

CCBN=$(shell basename ${CC})

DEBUG=0
ifeq ($(DEBUG),0)
  CFLAGS+= -O3 -DNDEBUG -std=gnu11 -Wshadow -Wall -Wextra -Wno-missing-braces -pedantic -DSTDC_HEADERS -D_GNU_SOURCE -lm -ftree-vectorize -flto
  ifeq (${CCBN},clang)
    CFLAGS+= -Wno-gnu-statement-expression
  endif
else
  CFLAGS += -g
endif

export THIS_LIB_BASE=$(shell cd ../.. && pwd)
BUILD_DIR=${THIS_LIB_BASE}/build/${BUILD_SUBDIR}/${CCBN}
TARGET=${BUILD_DIR}/bin/zsvextmy.${SO}
TARGET_SHEET=${BUILD_DIR}/bin/zsvextmysheet.${SO}

COLOR_NONE=\033[0m
COLOR_GREEN=\033[1;32m
COLOR_RED=\033[1;31m
COLOR_BLUE=\033[1;34m
COLOR_PINK=\033[1;34m
COLOR_YELLOW=\033[1;33m

TEST_INIT=printf "${COLOR_PINK}$@: ${COLOR_NONE}\n"
TEST_SKIP=printf "${COLOR_BLUE}$@: ${COLOR_YELLOW}Skipped${COLOR_NONE}\n"
TEST_PASS=printf "${COLOR_BLUE}$@: ${COLOR_GREEN}Passed${COLOR_NONE}\n"
TEST_FAIL=(printf "${COLOR_BLUE}$@: ${COLOR_RED}Failed!${COLOR_NONE}\n" && exit 1)

EXPECT=../../scripts/test-expect.sh
export EXPECTED_PATH=test/expected

CFLAGS_SHARED=-shared
ifneq ($(findstring emcc,$(CC)),) # emcc
  CFLAGS_SHARED=-s SIDE_MODULE=1 -s LINKABLE=1
  INSTALLED_EXTENSION=${BINDIR}/zsvextmy.${SO}
else
  INSTALLED_EXTENSION=
endif

CFLAGS+= -I${THIS_LIB_BASE}/include -I${INCLUDEDIR}

all: ${TARGET} ${TARGET_SHEET}
	@echo Built ${TARGET}
	@echo Built ${TARGET_SHEET}

ifneq ($(findstring emcc,$(CC)),) # emcc
install: ${INSTALLED_EXTENSION}

${INSTALLED_EXTENSION}: ${TARGET} ${TARGET_SHEET}
	@mkdir -p `dirname "$@"`
	cp -p $< $@
endif

CLI=${BUILD_DIR}/bin/cli${EXE}

# RUN_CLI sets ZSV_CONFIG_DIR and ZSV_PROG_NAME, and WSL passes a Windows executable only the
# variables WSLENV names. ZSV_CONFIG_DIR needs no /p path translation because TMP_DIR is relative
ifeq ($(WIN),1)
  export WSLENV:=$(if ${WSLENV},${WSLENV}:)ZSV_CONFIG_DIR:ZSV_PROG_NAME
endif

# Pin the program name so help/usage output matches the expected fixtures
# regardless of the build-dir binary name (basename(argv[0]) would otherwise be "cli").
RUN_CLI=ZSV_CONFIG_DIR=${TMP_DIR} ZSV_PROG_NAME=zsv ${CLI}

${CLI}:
	${MAKE} -C .. build-cli CONFIGFILE=${CONFIGFILEPATH} DEBUG=${DEBUG}

${BUILD_DIR}/objs/utils/%.o:
	(cd .. && ${MAKE} CONFIGFILE=${CONFIGFILEPATH} CC=${CC} DEBUG=${DEBUG} $@ )

TESTS=test-extension-1 test-extension-2 test-extension-3 test-extension-4 test-extension-5 test-extension-thirdparty

ifeq ($(ZSVSHEET_BUILD),1)
  TESTS+=test-sheet-extension-1 test-sheet-extension-2
endif

test: ${TESTS}

test-extension-1: test-%: ${CLI} ${TARGET}
	@${TEST_INIT}
	@${RUN_CLI} my-echo < ${TEST_DATA_DIR}/quoted.csv > ${TMP_DIR}/$@.out
	@${CMP} ${TMP_DIR}/$@.out test/expected/$@.out && ${TEST_PASS} || ${TEST_FAIL}

test-extension-2: test-%: ${CLI} ${TARGET}
	@${TEST_INIT}
	@${RUN_CLI} my-echo -t < ${TEST_DATA_DIR}/hi.txt > ${TMP_DIR}/$@.out
	@${CMP} ${TMP_DIR}/$@.out test/expected/$@.out && ${TEST_PASS} || ${TEST_FAIL}

test-extension-3: test-%: ${CLI} ${TARGET}
	@${TEST_INIT}
	@rm -f ${TMP_DIR}/$@.out ${TMP_DIR}/$@.err
	@${RUN_CLI} unregister my 2>/dev/null 1>/dev/null || true
	@${RUN_CLI} register my >>${TMP_DIR}/$@.out 2>>${TMP_DIR}/$@.err || true
	@${RUN_CLI} unregister my >>${TMP_DIR}/$@.out 2>>${TMP_DIR}/$@.err || true
	@${RUN_CLI} unregister my >>${TMP_DIR}/$@.out 2>>${TMP_DIR}/$@.err || true
	@${RUN_CLI} help >>${TMP_DIR}/$@.out 2>>${TMP_DIR}/$@.err || true
	@${RUN_CLI} register my >>${TMP_DIR}/$@.out 2>>${TMP_DIR}/$@.err || true
	@${RUN_CLI} help >>${TMP_DIR}/$@.out 2>>${TMP_DIR}/$@.err || true
	@grep -v [.]${SO} ${TMP_DIR}/$@.err >${TMP_DIR}/$@.err.filtered && mv ${TMP_DIR}/$@.err.filtered ${TMP_DIR}/$@.err
	@# 2toon's help line is present only in TOON-enabled builds; TOON_HELP_FILTER
	@# strips it from the expected when --disable-toon (actual is compared as-is)
	@${TOON_HELP_FILTER} test/expected/$@.out${EXT} >${TMP_DIR}/$@.out.expected
	@${CMP} ${TMP_DIR}/$@.out 2>&1 ${TMP_DIR}/$@.out.expected && ${TEST_PASS} || ${TEST_FAIL}
	@${CMP} ${TMP_DIR}/$@.err 2>&1 test/expected/$@.err${EXT} && ${TEST_PASS} || ${TEST_FAIL}

test-extension-4: test-%: ${CLI} ${TARGET}
	@${TEST_INIT}
	@${RUN_CLI} unregister my 2>/dev/null 1>/dev/null || true
	@${RUN_CLI} license > ${TMP_DIR}/$@.out 2>> ${TMP_DIR}/$@.err
	@${RUN_CLI} register my 2>/dev/null 1>/dev/null
	@${RUN_CLI} license >> ${TMP_DIR}/$@.out 2> ${TMP_DIR}/$@.err
	@${CMP} ${TMP_DIR}/$@.out test/expected/$@.out && ${TEST_PASS} || ${TEST_FAIL}
	@${CMP} ${TMP_DIR}/$@.err test/expected/$@.err && ${TEST_PASS} || ${TEST_FAIL}

test-extension-5: test-%:${TARGET}
	@${TEST_INIT}
	@${RUN_CLI} my-count newline.csv 2>/dev/null > ${TMP_DIR}/$@.out
	@${RUN_CLI} my-count -q newline.csv 2>/dev/null >> ${TMP_DIR}/$@.out
	@${CMP} ${TMP_DIR}/$@.out test/expected/$@.out && ${TEST_PASS} || ${TEST_FAIL}

test-extension-thirdparty: test-%: ${CLI} ${TARGET}
	@${TEST_INIT}
	@${RUN_CLI} unregister my 2>/dev/null 1>/dev/null || true
	@${RUN_CLI} thirdparty > ${TMP_DIR}/$@.out
	@${RUN_CLI} register my 2>/dev/null 1>/dev/null
	@${RUN_CLI} thirdparty >> ${TMP_DIR}/$@.out
	@${CMP} ${TMP_DIR}/$@.out test/expected/$@.out && ${TEST_PASS} || ${TEST_FAIL}

../test/worldcitiespop_mil.csv:
	${MAKE} -C ../test worldcitiespop_mil.csv

DATE_TIME:=$(shell date +%F-%H-%M-%S)
export TIMINGS_CSV:=${TMP_DIR}/timings-${DATE_TIME}.csv
TMUX_TERM=xterm-256color
test-sheet-extension-1: ${CLI} ${TARGET} ../test/worldcitiespop_mil.csv
	@${TEST_INIT}
	@rm -f ${TMP_DIR}/$@.out tmux-*.log
	@tmux kill-session -t $@ 2>/dev/null || true
	@echo 'set-option default-terminal "${TMUX_TERM}"' > ~/.tmux.conf
	@${RUN_CLI} unregister my 2>/dev/null 1>/dev/null || true
	@${RUN_CLI} register my 2>&1 | grep -v [.]${SO} || true
	@(ZSV_CONFIG_DIR=${TMP_DIR} tmux -v new-session -x 80 -y 5 -d -s $@ "$< sheet ../test/worldcitiespop_mil.csv" && \
	${EXPECT} $@ indexed && \
	tmux send-keys -t $@ "t" "hello" Enter Up Down && \
	${EXPECT} $@ && ${TEST_PASS} || ${TEST_FAIL})

test-sheet-extension-2: ${CLI} ${TARGET}
	@${TEST_INIT}
	@rm -f ${TMP_DIR}/$@.out tmux-*.log
	@tmux kill-session -t $@ 2>/dev/null || true
	@echo 'set-option default-terminal "${TMUX_TERM}"' > ~/.tmux.conf
	@${RUN_CLI} unregister my 2>/dev/null 1>/dev/null || true
	@${RUN_CLI} register my 2>&1 | grep -v [.]${SO} || true
	@(ZSV_CONFIG_DIR=${TMP_DIR} tmux -v new-session -x 120 -y 5 -d -s $@ "$< sheet -d 3 ${TEST_DATA_DIR}/test/mixed-line-endings.csv" && \
	${EXPECT} $@ indexed && \
	tmux send-keys -t $@ "T" && \
	${EXPECT} $@ transformed && \
	tmux send-keys -t $@ "G" && \
	${EXPECT} $@ && ${TEST_PASS} || ${TEST_FAIL})

clean:
	@rm -f ${TARGET} ${TARGET_SHEET} ${TMP_DIR}/test*.out

${BUILD_DIR}/objs/%.o : ${THIS_LIB_BASE}/src/%.c ${PARSER_DEPS}
	${MAKE} -C ${THIS_LIB_BASE}/src CONFIGFILE=${CONFIGFILEPATH} DEBUG=${DEBUG} WIN=${WIN} $@

LIBZSV=${LIBDIR}/libzsv.a
${LIBZSV}:
	${MAKE} -C ${THIS_LIB_BASE}/src CONFIGFILE=${CONFIGFILEPATH} DEBUG=${DEBUG} WIN=${WIN} install

LIBZSVUTIL=${LIBDIR}/libzsvutil.a
${LIBZSVUTIL}:
	${MAKE} -C ${THIS_LIB_BASE}/app CONFIGFILE=${CONFIGFILEPATH} DEBUG=${DEBUG} WIN=${WIN} install-util-lib

YAJL_SRC_DIR=${THIS_MAKEFILE_DIR}/../external/yajl
YAJL_INCLUDE=-I${YAJL_SRC_DIR}/build/yajl-2.1.1/include
YAJL_HELPER_INCLUDE=-I${THIS_MAKEFILE_DIR}/../external/yajl_helper
${TARGET_SHEET}: LIBS="../external/sqlite3/sqlite3.c" -lzsvutil -lzsv -L${LIBDIR} -ljq -lutf8proc
${TARGET}: LIBS=-lzsvutil -lzsv -L${LIBDIR}
${TARGET} ${TARGET_SHEET}: ${BUILD_DIR}/bin/zsvext%.${SO} : %_extension.c ${LIBZSV} ${LIBZSVUTIL}
	@mkdir -p `dirname "$@"`
	${CC} ${CFLAGS} ${CFLAGS_SHARED} $< -o $@ ${LIBS} ${YAJL_INCLUDE} ${YAJL_HELPER_INCLUDE}

.PHONY: all test test-% clean install ${LIBDIR}/libzsvutil.a
